zig*_*tar 14 scala parser-combinators hlist shapeless
是否有可能为无形'创建一个HList看起来如下的提取器.
val a ~ _ ~ b = 4 :: "so" :: 4.5 :: HNil
=> a == 4 && b == 4.5
Run Code Online (Sandbox Code Playgroud)
::为~,这应该不是问题.HNil.有没有可能出现的问题?经过多少汗水和泪水,我设法到达了以下代码的工作点:
for(
x1 :: _ :: x2 :: HNil <- (expInt ~ "+" ~ expInt).llE
) yield (x1 + x2)
Run Code Online (Sandbox Code Playgroud)
expInt解析Int一些monad E.类型(expInt ~ "+" ~ expInt).llE是E[Int :: String :: Int :: HNil].
我希望左边的模式在<-某种程度上类似于右边的组合子解析器的构造.
Mil*_*bin 36
这可以做到,并有几个有趣的曲折.
第一个是,通常,为了匹配使用右关联构造函数构建的结构(即.::),您将使用相应的右关联提取器,否则您将以相反的顺序分解和绑定提取的元素.不幸的是,正确的关联提取器必须像右关联运算符一样:以Scala 结尾,这会破坏你的解析器组合器语法,因为提取器名称必须是~:plain而不是plain ~.但是,我暂时推迟这一点,并与正确的关联性合作.
第二个转折是,我们需要的不应用方法,以产生取决于我们是否要匹配的不同类型的结果HList的以上两个元素或恰好两个元素(我们不应该能够匹配的少两个列表所有元素).
如果我们匹配两个以上元素的列表,我们需要将列表分解为由头部和HList尾部组成的对,即.鉴于l: H :: T在那里T <: HList,我们必须产生类型的值(H, T).另一方面,如果我们匹配恰好两个元素的列表,即.在形式上E1 :: E2 :: HNil,我们需要将列表分解为仅包含这两个元素的对,即(E1, E2)不是头部和尾部(E1, E2 :: HNil).
这可以使用在整个无形中使用的完全相同的类型级编程技术来完成.首先,我们定义一个类型类,它将完成提取器的工作,实例对应于上述两种情况中的每一种,
import shapeless._
trait UnapplyRight[L <: HList] extends DepFn1[L]
trait LPUnapplyRight {
type Aux[L <: HList, Out0] = UnapplyRight[L] { type Out = Out0 }
implicit def unapplyHCons[H, T <: HList]: Aux[H :: T, Option[(H, T)]] =
new UnapplyRight[H :: T] {
type Out = Option[(H, T)]
def apply(l: H :: T): Out = Option((l.head, l.tail))
}
}
object UnapplyRight extends LPUnapplyRight {
implicit def unapplyPair[H1, H2]: Aux[H1 :: H2 :: HNil, Option[(H1, H2)]] =
new UnapplyRight[H1 :: H2 :: HNil] {
type Out = Option[(H1, H2)]
def apply(l: H1 :: H2 :: HNil): Out = Option((l.head, l.tail.head))
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们就这样定义我们的提取器,
object ~: {
def unapply[L <: HList, Out](l: L)
(implicit ua: UnapplyRight.Aux[L, Out]): Out = ua(l)
}
Run Code Online (Sandbox Code Playgroud)
然后我们很高兴,
val l = 23 :: "foo" :: true :: HNil
val a ~: b ~: c = l
a : Int
b : String
c : Boolean
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.现在让我们回到关联性问题.如果我们想用左关联提取器(即~代替~:)实现相同的效果,我们需要改变分解的方式.首先让我们看看我们刚才使用的正确的关联提取器语法.表达方式,
val a ~: b ~: c = l
Run Code Online (Sandbox Code Playgroud)
相当于,
val ~:(a, ~:(b, c)) = l
Run Code Online (Sandbox Code Playgroud)
相比之下,左联想版本,
val a ~ b ~ c = l
Run Code Online (Sandbox Code Playgroud)
相当于,
val ~(~(a, b), c) = l
Run Code Online (Sandbox Code Playgroud)
要使这个作为HLists我们的unapply类型类的提取器工作,必须从结尾剥离元素,而不是从列表的开头.我们可以借助无形Init和Last类型来做到这一点,
trait UnapplyLeft[L <: HList] extends DepFn1[L]
trait LPUnapplyLeft {
import ops.hlist.{ Init, Last }
type Aux[L <: HList, Out0] = UnapplyLeft[L] { type Out = Out0 }
implicit def unapplyHCons[L <: HList, I <: HList, F]
(implicit
init: Init.Aux[L, I],
last: Last.Aux[L, F]): Aux[L, Option[(I, F)]] =
new UnapplyLeft[L] {
type Out = Option[(I, F)]
def apply(l: L): Out = Option((l.init, l.last))
}
}
object UnapplyLeft extends LPUnapplyLeft {
implicit def unapplyPair[H1, H2]: Aux[H1 :: H2 :: HNil, Option[(H1, H2)]] =
new UnapplyLeft[H1 :: H2 :: HNil] {
type Out = Option[(H1, H2)]
def apply(l: H1 :: H2 :: HNil): Out = Option((l.head, l.tail.head))
}
}
object ~ {
def unapply[L <: HList, Out](l: L)
(implicit ua: UnapplyLeft.Aux[L, Out]): Out = ua(l)
}
Run Code Online (Sandbox Code Playgroud)
现在我们完成了,
val a ~ b ~ c = l
a : Int
b : String
c : Boolean
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1467 次 |
| 最近记录: |