我现在已经完成了一些HList的实现。一个基于Daniel Spiewak在Scala talk的土地上的高级魔法,另一个基于Apocalisp博客上的一篇文章。我们的目标是拥有一个异质列表,其中不是主要类型的异质,而是更高类型的异质。例如:
val requests = Request[String] :: Request[Int] :: HNil
我将能够在列表中进行映射,以执行请求,并生成更高类型的异类列表。所以:
requests.map(execute)
应该等于
String :: Int :: HNil
遗憾的是,我所有的尝试都导致了HList为Any。以下是最近一次尝试的代码:
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]]
}
另一次尝试是基于Apocalisp版本,该版本使用fold创建新的HList,并再次生成任何类型的HList。任何建议都将不胜感激。
发布于 2012-01-03 02:19:08
shapeless中的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元素共享一个公共的外部类型构造函数,但它只需要映射到的较高级别的函数具有所有涉及的类型的情况,
// 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)
发布于 2011-03-30 18:52:14
您需要的是一个带有类型构造函数Request
和一个自然转换execute: Request ~> Id
的Klist。所有这些都在Apocalisp精彩的类型级编程系列文章中进行了详细介绍,特别是:
您可以从Mark Harrah's up repo中检出整个系列的代码
在您的情况下,您将需要类似于
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。
发布于 2016-10-12 03:11:41
请注意,在最近( 2016年10月,OP之后的5年)来自的文章"“中,您有一个使用HList映射的示例。
//glue for the ParserStageDefs
specs.map(s => Flow[Data].map(s.parser).map(s.processor))
.foreach(broadcast ~> _ ~> merge)
问题在于我们的规范列表中的类型信息没有被保留。或者更确切地说,不是以我们想要的方式保存-
List
元素的类型是ParserStageDef[_ >: Int with String]
,所以对于我们的装饰器和增量器来说,最低的公共超类型。
上面的意思是,在解析器和处理器元素之间进行映射时,编译器无法提供在给定规范中使用的实际类型T
。
一种解决方案
这就是HLists的用武之地。因为它们保留了每个元素的完整类型信息,所以可以像上一次尝试那样定义我们的流。
首先,让我们用HList
替换我们的列表:
import shapeless.ops.hlist._
import shapeless._
//...
val specs = decorator :: incrementer :: HNil
val specsSize = specs.length.toInt
现在,对于从ParserStageDefs
到Flows
的映射,我们需要采取一种不同的方法,因为HList
的map
需要称为P**oly - a polymorphic function value**的东西。
下面是在我们的例子中的示例:
import shapeless.PolyDefns.~>
object toFlow extends (ParserStageDef ~> ProcessingFlow) {
override def apply[T](f: ParserStageDef[T]) =
Flow[Data].map(f.parser).map(f.processor)
}
为了让它起作用,我们还需要将ProcessingFlow
更改为ProcessingFlow[_] = Flow[Data, Data, _]
类型,因为上面的多态函数需要一个更高类型的类型。
现在,我们的核心陈述是:
//we convert to a List[ProcessingFlow[_]] for simplicity
specs.map(toFlow).toList.foreach(broadcast ~> _ ~> merge)
,我们都准备好了!
https://stackoverflow.com/questions/5349482
复制相似问题