可以在Scala列表上执行映射

可以在Scala列表上执行映射,scala,shapeless,hlist,Scala,Shapeless,Hlist,我现在已经完成了HList的一些实现。一个是基于丹尼尔·斯皮瓦克的《斯卡拉之地的高级巫术》的演讲,另一个是基于Apocalisp博客上的一篇文章。我们的目标是建立一个异构列表,该列表在主要类型中不是异构的,而是更高类型的。例如: val requests = Request[String] :: Request[Int] :: HNil 我将能够在整个列表中进行映射以执行请求,并生成更高种类的异构列表。因此: requests.map(execute) 应该相等 String :: Int

我现在已经完成了HList的一些实现。一个是基于丹尼尔·斯皮瓦克的《斯卡拉之地的高级巫术》的演讲,另一个是基于Apocalisp博客上的一篇文章。我们的目标是建立一个异构列表,该列表在主要类型中不是异构的,而是更高类型的。例如:

val requests = Request[String] :: Request[Int] :: HNil
我将能够在整个列表中进行映射以执行请求,并生成更高种类的异构列表。因此:

requests.map(execute)
应该相等

String :: Int :: HNil
可悲的是,我所有的尝试都导致了一个列表。以下是最近一次尝试的代码:

class Request[+Out](o:Out) {
  type O = Out

  def v:O = o
}

object HList {
  trait Func[-Elem,Out] {
    type Apply[E <: Elem] <: Out
    def apply[N <: Elem](e:N):Apply[N]
  }
  sealed trait HList[Base] {
    type Head <: Base
    type Tail <: HList[Base]
    type Map[Out,F <: Func[Base,Out]] <: HList[Out]
    def head:Head
    def tail:Tail

    def ::[A <: Base](a:A):HList[Base]
    def map[Out,F <: Func[Base,Out]](f:F):Map[Out,F]
  }

  case class HNil[Base]() extends HList[Base] {
    type Head = Nothing
    type Tail = Nothing
    type Map[Out,F <: Func[Base,Out]] = HNil[Out]

    def head = error("Head of an empty HList")
    def tail = error("Head of an empty HList")

    def ::[A <: Base](a:A) = HCons(a,this)
    def map[Out,F <: Func[Base,Out]](f:F) = new HNil[Out]
  }

  case class HCons[Base,A <: Base,B <: HList[Base]](head: A, tail: B) extends HList[Base] {
    type Head = A
    type Tail = B    
    type Map[Out,F <: Func[Base,Out]] = HCons[Out,F#Apply[Head],Tail#Map[Out,F]]

    def ::[C <: Base](c:C) = HCons(c,this)
    def map[Out,F <: Func[Base,Out]](f:F) =
      HCons(f(head),tail.map(f))
  }

  val :: = HCons 
}

object Test extends Application {
  import HList._

  val HNil = new HNil[Request[_]]

  val list = new Request[Int](1) :: new Request[String]("1") :: HNil

  val (a :: b :: HNil) = list
  val y:Request[String] = b

  val results = list.map[Any,Unwrap.type](Unwrap)

  val i:Int = results.head
}

import HList._
object Unwrap extends Func[Request[Any],Any] {
  type Apply[I <: Request[Any]] = I#O
  def apply[N <: Request[Any]](e:N) = null.asInstanceOf[Apply[N]]
}
类请求[+Out](o:Out){
类型O=输出
定义v:O=O
}
对象列表{
特征函数[-Elem,Out]{

类型Apply[E您需要的是一个带有类型构造函数
Request
的Klist,以及一个自然转换
execute:Request~>Id
。所有这些都在Apocalisp的奇妙类型级编程系列文章中详细介绍,特别是:

  • 您可以从中签出整个系列的代码

    在你的情况下,你需要

    val reqList = new Request[Int](1) :^: new Request[String]("1") :^: KNil    
    val exec = new (Request ~> Id) { def apply[T](reqs: Request[T]): T = reqs.execute }    
    val results = reqList down exec
    

    上面的
    down
    方法在概念上与nat transf
    M~>Id
    map
    方法相同;您还有更通用的
    map
    ,它从nat transf
    M~>N
    和类型M的Klist得到类型N的Klist。

    HList中的
    HList实现足够丰富,可以同时包含
    HList
    KList
    功能。它提供了一个
    map
    操作,在元素之间应用一个更高级别的函数,可能具有特定类型的情况,从而产生一个适当类型的
    HList
    结果

    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 
    
    请注意,尽管在上面的示例中,HList元素不需要共享一个公共的外部类型构造函数,但必须是这样的情况,即使用映射的更高级别函数对所有涉及的类型都有cases

    // 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
    
    现在,让我们将其映射到
    HList

    val l = 23 :: true :: "foo" :: ("bar", "wibble") :: HNil
    val ls = l map size
    
    ls == 1 :: 1 :: 3 :: 10 :: HNil
    
    在这种情况下,被映射函数的结果类型是常量:无论参数类型是什么,它都是Int。因此,生成的HList具有所有相同类型的元素,这意味着它可以有效地转换为普通列表

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

    请注意,您在最近(2016年10月,OP发表5年后)的文章“”中有一个带有HList的地图示例

      //glue for the ParserStageDefs
      specs.map(s => Flow[Data].map(s.parser).map(s.processor))
                        .foreach(broadcast ~> _ ~> merge)
    
    问题在于,我们的规范列表中的类型信息没有被保留。或者说,没有按照我们想要的方式被保留-
    列表
    元素的类型是
    ParserStageDef[\u>:Int with String]
    ,因此我们的装饰器和递增器的最低通用超类型

    上述情况意味着,在解析器和处理器元素之间进行映射时,编译器无法提供给定规范中使用的实际类型
    T

    解决办法 这是HLists的解救之道。因为它们保存了每个元素的完整类型信息,所以可以定义与上次尝试非常相似的流

    首先,让我们将列表替换为
    HList

    现在,对于从
    ParserStageDefs
    Flows
    的映射,我们需要采取不同的方法,因为
    HList
    map
    需要一个称为p****的东西

    在我们的案例中,一个人会是这样的:

    为了让它工作,我们还需要将
    ProcessingFlow
    更改为type
    ProcessingFlow[\u]=Flow[Data,Data,\u]
    ,因为上面的多态函数需要更高类型的类型

    现在,我们的核心声明是:

    我们都准备好了


    我注意到XSBT中的KLists,但还没有时间看一看。谢谢你的提示。这个问题让我抓狂。大小的定义在Shapeless 2.0中有效吗?我得到一个错误“无法创建对象,因为方法应用于[t]类型的trait~>(f:Shapeless.Id[t])Int没有定义”如果有人想知道,报告的@JamesMoore错误可以通过将
    default
    替换为
    apply
    来解决。有人知道当前的语法吗?
    import shapeless.ops.hlist._
    import shapeless._
    //...
    
    val specs = decorator :: incrementer :: HNil
    val specsSize = specs.length.toInt
    
    import shapeless.PolyDefns.~>
    object toFlow extends (ParserStageDef ~> ProcessingFlow) {
      override def apply[T](f: ParserStageDef[T]) = 
                    Flow[Data].map(f.parser).map(f.processor)
    }
    
    //we convert to a List[ProcessingFlow[_]] for simplicity
    specs.map(toFlow).toList.foreach(broadcast ~> _ ~> merge)