Nat 列表介于 0 和 2 之间的类? [英] Class with List of Nat's between 0 and 2?

查看:40
本文介绍了Nat 列表介于 0 和 2 之间的类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 Peter Neyens 有用的 answer,我尝试创建一个仅包含以下内容的 XNat 小于或等于 2.

import shapeless._导入 shapeless.ops.nat._导入 shapeless.nat._case class X[A <: Nat](values: List[A])(implicit ev: LTEq[A, _2])

以下工作:

scala>X( 列表(_1, _1, _1) )res6: X[shapeless.nat._1] = X(List(Succ(), Succ(), Succ()))

但是当我使用不同的 Nat 时,即 _1_2,我得到了一个编译时错误:

scala>X( 列表(_1, _2) )<console>:23: 错误: 找不到参数 ev 的隐式值:shapeless.ops.nat.LTEq[shapeless.Succ[_ >: shapeless.Succ[shapeless._0]with shapeless._0 <: Serializable with shapeless.Nat],shapeless.nat._2]X( 列表(_1, _2) )^

然后我尝试了:

scala>case class X(values: List[_ <: Nat])定义类 X

适用于 Natsub-classes,但不受 <=2 的限制:

scala>X(列表(_1, _2, _3))res8: X = X(List(Succ(), Succ(), Succ()))

如何编写上面的 X,其中包含 0 到 2 范围内的 Nat 列表?

我的直接想法是对 MyZeroMyOneMyTwo 使用代数数据类型,但不确定这是最简洁的方法.

最后,在我看来 X 要求有一个类型参数是不必要的.它可以在我想要的 X 的实现中被推断或遗漏吗?

解决方案

为手动绑定引入新的 ADT 可能是唯一的出路,因为您需要绑定不同的宏实体化对象,而这些宏实体化对象只能从常量中实体化.

显而易见的通用解决方案是为可能的比较器操作生成类型证据的隐式机制,例如在使用 <<=<= 时可以生成代码>Nat 隐式证据,简单地比较Nat 的基础int 值.

您仍然可以使用 NatOps 和本机 toInt 手动执行此操作.

您在查看 Nat 的实现时遇到了另一个约束,即需要一个 Literal(Constant()).

val n = i.tree 匹配 {case Literal(Constant(n: Int)) =>n案例_ =>c.abort(c.enclosurePosition, s"Expression ${i.tree} 不计算为 Int 常量")}

所以这可能会让你得到如下结果:

抽象类 SizeBound[N <: Nat](n: N)导入 shapeless.nat._隐式对象 ZeroBound 扩展了 SizeBound(_0)隐式对象 OneBound 扩展了 SizeBound(_1)隐式对象 TwoBound 扩展了 SizeBound(_2)

然后是明显的def op[T <: Nat : SizeBound](...).也许身边的一位无形大师有更好的方法,遗憾的是我只是一个新手.

更新

我刚刚想到了这个问题,还记得 Miles 如何让 Shapeless Fizz 嗡嗡作响以取乐,即它如何依赖 Mod.Aux 在 nats 之间进行 mod 划分.

长话短说,已经有一个类型类:

import shapeless.ops.nat._导入 shapeless.nat._def doStuff[N <: Nat](n: N)(//已经有一个 _2 类型可用.隐式 ev: LT[N, _2]) = {//现在你知道 N 小于 _2}

LT 显然低于,而且您还有更多变化可供您使用.给你去,你也可以在这里浏览其他有趣的东西.这有效,但当您使用 _2.type 时无效,您必须直接从 import shapeless.nat._<使用 _2 类型/p>

Using Peter Neyens's helpful answer, I tried to create an X class that only consists of Nat's less than or equal to 2.

import shapeless._
import shapeless.ops.nat._
import shapeless.nat._

case class X[A <: Nat](values: List[A])(implicit ev: LTEq[A, _2])

The following works:

scala> X( List(_1, _1, _1) )
res6: X[shapeless.nat._1] = X(List(Succ(), Succ(), Succ()))

But when I used different Nat's, i.e. _1 and _2, I got a compile-time error:

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) )
        ^

Then I tried:

scala> case class X(values: List[_ <: Nat])
defined class X

which works for sub-classes of Nat, but it's not bounded by <= 2:

scala> X(List(_1, _2, _3))
res8: X = X(List(Succ(), Succ(), Succ()))

How can I write my above X that has a List of Nat's that fall in the range of 0 to 2?

My immediate thought is to use an Algebraic Data Type for MyZero, MyOne, and MyTwo, but not sure that's the cleanest way.

Lastly, it seems to me that X's requirement to have a type parameter is unnecessary. Can it either be inferred or left out in an implementation of my desired X?

解决方案

Introducing a new ADT for the manual bound is probably the only way to go given you need to bound by distinct macro materialised objects that can only be materialised out of constants.

The obvious generic solution would be an implicit mechanism of generating type evidence for possible comparator operations, such as when using < or <= or whatever on a Nat implicit evidence could be produced that would simply compare the underlying int values of the Nat.

You could still manually do this using NatOps with the native toInt.

You hit another constraint looking into the implementation of Nat, namely there's a Literal(Constant()) required.

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")
}

So this probably leaves you with something like the following:

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)

And then the obvious def op[T <: Nat : SizeBound](...). Maybe one of the Shapeless gurus around has a better way, I'm sadly just a novice.

Update

I just had a flashback thinking about this, and remember how Miles did Shapeless Fizz buzz for fun, namely how it relied on Mod.Aux to do mod division between nats.

Long story short, there is a type-class already:

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 is obviously lower than, and you've got a few more variations at your disposal. Here you go, you can browse for other fun things in here too. This works, but not when you use _2.type, instead you have to use the _2 type directly from import shapeless.nat._

这篇关于Nat 列表介于 0 和 2 之间的类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆