Jes*_*har 29 scala hlist shapeless
我现在已经做了一些HList的实现.一个基于丹尼尔斯皮瓦克在斯卡拉之地的高级巫术讲话,另一个基于Apocalisp博客中的帖子.目标是有一个异类列表,其中主要类型不是异质的,而是更高类型.例如:
val requests = Request[String] :: Request[Int] :: HNil
Run Code Online (Sandbox Code Playgroud)
我可以在列表中执行映射来执行请求,并生成更高类型的异构列表.所以:
requests.map(execute)
Run Code Online (Sandbox Code Playgroud)
应该相等
String :: Int :: HNil
Run Code Online (Sandbox Code Playgroud)
可悲的是,我的所有尝试都产生了任何HList.以下是最近尝试的代码:
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]]
}
Run Code Online (Sandbox Code Playgroud)
另一个尝试是基于Apocalisp版本,它使用fold创建一个新的HList,并再次产生任何类型的HList.任何提示将不胜感激.
Mil*_*bin 22
无形的HList实现足以包含两者和功能.它提供了一个操作,它可以将更高级别的函数(可能是特定于类型的情况)应用于它的元素,从而产生适当类型的结果,HListKListmapHList
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
Run Code Online (Sandbox Code Playgroud)
请注意,虽然在上面的示例中就是这种情况,但并不要求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
Run Code Online (Sandbox Code Playgroud)
现在让我们将它映射到一个HList,
val l = 23 :: true :: "foo" :: ("bar", "wibble") :: HNil
val ls = l map size
ls == 1 :: 1 :: 3 :: 10 :: HNil
Run Code Online (Sandbox Code Playgroud)
在这种情况下,被映射的函数的结果类型是常量:无论参数类型是什么,它都是Int.因此,生成的HList具有所有相同类型的元素,这意味着它可以有用地转换为vanilla列表,
ls.toList == List(1, 1, 3, 10)
Run Code Online (Sandbox Code Playgroud)
你需要的是一个带有类型构造函数的 KlistRequest和一个自然转换execute: Request ~> Id。所有这些都在 Apocalisp 精彩的类型级编程系列文章中进行了详细介绍,特别是:
你可以从Mark Harrah 的 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
Run Code Online (Sandbox Code Playgroud)
上述方法在概念上与 nat transfdown相同;你还有更一般的情况,从 nat transf和类型 M 的 Klist 生成类型 N 的 KList。mapM ~> IdmapM ~> N