使用Peter Neyens的帮助answer,我尝试创建一个只包含X
小于或等于2的Nat
类。
import shapeless._
import shapeless.ops.nat._
import shapeless.nat._
case class X[A <: Nat](values: List[A])(implicit ev: LTEq[A, _2])
以下作品:
scala> X( List(_1, _1, _1) )
res6: X[shapeless.nat._1] = X(List(Succ(), Succ(), Succ()))
但是当我使用不同的Nat
时,即_1
和_2
时,我遇到了编译时错误:
scala> X( List(_1, _2) )
<console>:23: error: could not find implicit value for parameter ev:
shapeless.ops.nat.LTEq[shapeless.Succ[_ >: shapeless.Succ[shapeless._0]
with shapeless._0 <: Serializable with shapeless.Nat],shapeless.nat._2]
X( List(_1, _2) )
^
然后我尝试了:
scala> case class X(values: List[_ <: Nat])
defined class X
适用于sub-classes
的{{1}},但不受Nat
限制:
<= 2
如何撰写上面scala> X(List(_1, _2, _3))
res8: X = X(List(Succ(), Succ(), Succ()))
的{{1}}列表,其中X
列在Nat
范围内?
我的想法是使用0 to 2
,MyZero
和MyOne
的代数数据类型,但不确定这是最干净的方法。
最后,在我看来,MyTwo
要求拥有类型参数是不必要的。可以推断出也可以在我想要的X
答案 0 :(得分:1)
为手动绑定引入新的ADT可能是唯一的方法,因为您需要使用只能通过常量实现的不同宏实现对象进行绑定。
明显的通用解决方案是为可能的比较器操作生成类型证据的隐式机制,例如当使用<
或<=
或Nat
时可能产生任何隐含证据这只会比较Nat
的基础int值。
您仍然可以使用NatOps
使用原生toInt
手动执行此操作。
您点击另一个约束,查看Nat
的实施,即需要Literal(Constant())
。
val n = i.tree match {
case Literal(Constant(n: Int)) => n
case _ =>
c.abort(c.enclosingPosition, s"Expression ${i.tree} does not evaluate to an Int constant")
}
所以这可能会给你留下如下内容:
abstract class SizeBound[N <: Nat](n: N)
import shapeless.nat._
implicit object ZeroBound extends SizeBound(_0)
implicit object OneBound extends SizeBound(_1)
implicit object TwoBound extends SizeBound(_2)
然后是显而易见的def op[T <: Nat : SizeBound](...)
。也许周围的一个无形大师有一个更好的方法,我很遗憾只是一个新手。
<强>更新强>
我刚才有一个闪回思考这个问题,并记住Miles如何做出Shapeless Fizz的嗡嗡声,即它如何依靠Mod.Aux
在nat之间进行mod划分。
长话短说,已经有了一个类型类:
import shapeless.ops.nat._
import shapeless.nat._
def doStuff[N <: Nat](n: N)(
// There is a _2 type already available.
implicit ev: LT[N, _2]
) = { // now you know N is less than _2}
LT
显然低于,并且您可以获得更多变体。 Here you go,您也可以在这里浏览其他有趣的内容。这是有效的,但不是在您使用_2.type
时,而是必须直接使用_2
import shapeless.nat._
类型