编辑:我将问题重新表述为更简单,更少域名的问题: 在下面的代码中,我想实现mplus函数,该函数结合了由特定字段的存在约束的两个函数。结果函数应该受到两个字段的存在的约束。 谢谢!
import shapeless._, ops.record.Selector, record._, syntax.singleton._
def requiresIntervalKey[L <: HList](l: L)
(implicit sel: Selector.Aux[L, Witness.`"interval"`.T, Int]): Unit = {
println(sel(l))
}
def requiresPlatformField[L <: HList](l: L)
(implicit sel: Selector.Aux[L, Witness.`"platform"`.T, String]): Unit = {
println(sel(l))
}
def mplus = ??? // That is the function I'd like to implement. Eventually it will be the additive operator of a monoid
// needsBothFields: L <: HList -> (implicit) Selector.Aux[L, Witness.`"interval"`.T, Int] -> (implicit) Selector.Aux[L, Witness.`"platform"`.T, String] -> Unit
val needsBothField = mplus(requiresIntervalKey _, requiresIntervalKey _ )
// Usage
requiresIntervalKey(("interval" ->> "a string") :: HNil) // Shoudn't compile, value type is wrong
requiresIntervalKey(("wrongKey" ->> "a string") :: HNil) // Shoudn't compile, "interval" key not provided
requiresIntervalKey(("interval" ->> 42) :: HNil) // Compiles
requiresPlatformField(("platform" ->> "EU") :: HNil) // Compiles
needsBothFields(("interval" ->> 42) :: ("platform" ->> "EU") :: HNil) // Should compile
//上一个问题版本
对于无形和类型级编程来说相当新,我正在努力实现我的目标并获得清晰的理解。非常感谢您的帮助!
我基本上有几个函数如下所示:
trait PathGenerator[T] {
def apply(T): Set[Seq[String]]
}
val hourlyIntervalPathGenerator: PathGenerator[Interval] = ???
val constantGenerator: PathGenerator[String] = ???
// A Monoid[PathGenerator]
// an implicit class adding a '/' operator to PathGenerator
// Usage
val hourlyRegionPathGenerator = hourlyIntervalPathGenerator / constantGenerator
val paths = hourlyRegionPathGenerator(StaticIntervals.lastDay, "EU")
现在,当将两个以上的生成器一起添加时,用户必须使用嵌套元组。此外,命名生成器参数对于生成解析器非常有用(例如:cmd-line。)
无形记录似乎很合适,所以我继续实施以下非工作解决方案: https://gist.github.com/amazari/911449b55270a5871d14
这个不可构建的代码和我无形的误解产生了几个问题:
L26: keys("interval")
和L35: keys(constant)
)获取字段的值不会返回预期类型的值,即使提供了Witness和选择器也很难。那么,记录是否是实现此用例的正确工具?代码段中未显示从PathPattern的记录形状/模板生成的命令行解析器。
我如何强制提供给生成器的记录(简单的或由几个组合产生的)在名称和类型方面具有完全正确的字段?
感谢您的帮助!
编辑:这一系列问题和Travis Brown的答案非常相关:
Passing a Shapeless Extensible Record to a Function Passing a Shapeless Extensible Record to a Function (continued) Passing a shapeless extensible record to a function (never ending story?
答案 0 :(得分:2)
这对于Shapeless来说可能是类型的,尽管不完全是你要求的形式。这是一个简单的例子:
import shapeless._, ops.record.Selector, record._, syntax.singleton._
class UseKey[K <: String, V, R](w: Witness.Aux[K])(f: V => R) extends Poly1 {
implicit def onRecord[L <: HList](implicit
sel: Selector.Aux[L, K, V]
): Case.Aux[L, R] = at[L](l => f(sel(l)))
}
class Combine[P1 <: Poly1, P2 <: Poly1, R1, R2, R](
p1: P1,
p2: P2
)(f: (R1, R2) => R) extends Poly1 {
implicit def onRecord[L <: HList](implicit
c1: p1.Case.Aux[L, R1],
c2: p2.Case.Aux[L, R2]
): Case.Aux[L, Unit] = at[L](l => f(c1(l), c2(l)))
}
class mplus[P1 <: Poly1, P2 <: Poly1](p1: P1, p2: P2)
extends Combine(p1, p2)((_: Unit, _: Unit) => ())
然后:
object requiresIntervalKey extends UseKey(Witness("interval"))(
(i: Int) => println(i)
)
object requiresPlatformField extends UseKey(Witness("platform"))(
(s: String) => println(s)
)
object needsBothFields extends mplus(requiresIntervalKey, requiresPlatformField)
(作为旁注,我猜想你可以在这里写UseKey("platform")
,但由于某种原因,捕获见证人的隐式转换并不起作用。幸运的是Witness("platform")
并非{&1}}太糟糕了。)
接下来显示它有效:
scala> import test.illTyped
import test.illTyped
scala> illTyped("""requiresIntervalKey("interval" ->> "a string" :: HNil)""")
scala> illTyped("""requiresIntervalKey("wrongKey" ->> "a string" :: HNil)""")
scala> requiresIntervalKey("interval" ->> 42 :: HNil)
42
scala> requiresPlatformField("platform" ->> "EU" :: HNil)
EU
scala> needsBothFields("interval" ->> 42 :: "platform" ->> "EU" :: HNil)
42
EU
我们需要Poly1
而不是常规旧函数的原因是我们无法在组合普通函数时收集我们需要的不同证据 - 而是我们需要多态函数值(Shapeless提供为{ {1}})。
值得注意的是,这并非真正的“幺半群”。类型的monoid实例提供了一个操作,该操作接受该类型的两个值并返回另一个值。 PolyN
这里有两个操作,每个操作都有自己的隐含要求,并给我们另一个操作,它具有两者的组合要求。