Covariance in type-level programming

后端 未结 2 1424
时光说笑
时光说笑 2021-02-14 23:34

I\'m trying to create types Tuple equivalent to the ones in the Scala library, only with a :+ method that extends a Tuple into a Tuple by addition of the N+1st value -- so that

2条回答
  •  悲&欢浪女
    2021-02-15 00:17

    The HList in shapeless is fully covariant and supports conversion to corresponding tuple types.

    The problem you had (covariant type variables appearing in contravariant position) is avoided in general by "pimping away variance": the base HList ADT elements are defined minimally, similarly to the way that Owen has done at the top of his answer, and the definitions which need to use the type variables contravariantly are added via an implicit conversion.

    The tupling operation is provided by an orthogonal mechanism: the resulting tuple type is computed at the type level using a combination of implicit type class definitions (in effect a functional dependency) and dependent method types (so use -Ydependent-method-types or Scala 2.10-SNAPSHOT),

    // Minimal base HList ADT elements
    sealed trait HList
    
    final case class HCons[+H, +T <: HList](head : H, tail : T) extends HList {
      def ::[U](h : U) : U :: H :: T = HCons(h, this)
    }
    
    trait HNil extends HList {
      def ::[H](h : H) = HCons(h, this)
    }
    
    case object HNil extends HNil
    
    type ::[+H, +T <: HList] = HCons[H, T]
    
    // Separate 'Ops` trait allows the HList type L to be used independently
    // of variance.
    final class HListOps[L <: HList](l : L) {
      // More definitions elided ...
    
      def tupled(implicit tupler : Tupler[L]) : tupler.Out = tupler(l)
    }
    
    // Implicitly pimp away the variance
    implicit def hlistOps[L <: HList](l : L) = new HListOps(l)
    
    // Type class representing a type-level function from the HList type to
    // the corresponding tuple type
    trait Tupler[L <: HList] {
      type Out <: Product
      def apply(l : L) : Out
    }
    
    // Type class instances for Tupler   
    object Tupler {
      implicit def hlistTupler1[A] = new Tupler[A :: HNil] {
        type Out = Tuple1[A]
        def apply(l : A :: HNil) = Tuple1(l.head)
      }
    
      implicit def hlistTupler2[A, B] = new Tupler[A :: B :: HNil] {
        type Out = (A, B)
        def apply(l : A :: B :: HNil) = (l.head, l.tail.head)
      }
    
      // Add more instances for higher arities here ...
    }
    
    val t1 = (1 :: HNil).tupled           // type inferred as Tuple1[Int]
    val t2 = (1 :: "foo" :: HNil).tupled  // type inferred as (Int, String)
    

提交回复
热议问题