Can Map be performed on a Scala HList

前端 未结 3 850
再見小時候
再見小時候 2021-01-30 17:09

I have done a few implementations of HList now. One based on Daniel Spiewak\'s High Wizardry in the Land of Scala talk and another based on a post in Apocalisp blog. The goal

3条回答
  •  一个人的身影
    2021-01-30 18:07

    The HList implementation in shapeless is rich enough to subsume both HList and KList functionality. It provides a map operation which applies a higher-ranked function, possibly with type-specific cases, across it's elements yielding an appropriately typed HList result,

    import shapeless.Poly._
    import shapeless.HList._
    
    // Define a higher-ranked function from Sets to Options
    object choose extends (Set ~> Option) {
      def default[T](s : Set[T]) = s.headOption 
    }
    
    // An HList of Sets
    val sets = Set(1) :: Set("foo") :: HNil
    
    // Map our choose function across it ...
    val opts = sets map choose
    
    // The resulting value
    opts == Option(1) :: Option("foo") :: HNil 
    

    Note that although it's the case in the above example there's no requirement that the HList elements share a common outer type constructor, it just has to be the case that the higher-ranked function mapped with has cases for all of the types involved,

    // size is a higher-ranked function from values of arbitrary type to a 'size'
    // which is defined as 1 by default but which has type specific cases for
    // Strings and tuples
    object size extends (Id ~> Const[Int]#λ) {
      def default[T](t : T) = 1
    }
    implicit def sizeString = size.λ[String](s => s.length)
    implicit def sizeTuple[T, U](implicit st : size.λ[T], su : size.λ[U]) =
      size.λ[(T, U)](t => 1+size(t._1)+size(t._2))
    
    size(23) == 1          // Default
    size("foo") == 3       // Type specific case for Strings
    size((23, "foo")) == 5 // Type specific case for tuples
    

    Now let's map this across an HList,

    val l = 23 :: true :: "foo" :: ("bar", "wibble") :: HNil
    val ls = l map size
    
    ls == 1 :: 1 :: 3 :: 10 :: HNil
    

    In this case the result type of the function being mapped is constant: it's an Int no matter what the argument type is. Consequently the resulting HList has elements all of the same type, which means that it can usefully be converted to a vanilla list,

    ls.toList == List(1, 1, 3, 10)
    

提交回复
热议问题