Scala:由嵌套类型值参数化的类型级编程
目前我有一个实现peano算法的程序:
sealed trait NaturalNumber 和一个名为getResource的函数,它在其参数中获取具有接口的自然数字类型值: sealed trait VersionNumber { type Nat <: NaturalNumber } 并根据此接口中提供的引用类型 – 值版本号:MAJ和MIN检查值: trait ResourceManifest { def getResource: Int type Major <: NaturalNumber type Minor <: NaturalNumber } 取决于哪个,函数执行或不编译.该函数具有以下形式: def getResource(manifest: ResourceManifest)(maj: VersionNumber,min: VersionNumber) (implicit maj_check: manifest.Major IsEqual maj.Nat,min_check: manifest.Minor IsLessOrEqual min.Nat ) = manifest.getResource 这是full code.(如果你喜欢类型递归,这里有一个替代implementation.) 实际上,这是由覆盖的类型值驱动的,普通的Scala用户可能不太习惯.此外,getResource获取主要版本和次要版本的单独参数. 理想情况下,我想用户在包装类中提供值而不是类型: case class VersionInfo(major: VersionNumber,minor: VersionNumber) 所以我的清单是这样的形式: trait ResourceManifestRefactored { def getResource: Int val versionInfo: VersionInfo } 同样有: def getResourceRefactored(manifest: ResourceManifestRefactored)(versionInfo: VersionInfo) 并通过从包装器版本值类中获取版本类型来执行我的类型级别约束:VersionInfo.然而,尽管我以许多不同的方式做到这一点,但我仍在努力让它发挥作用.例如,我尝试直接使用路径依赖类型进行类型检查,但失败了.我还尝试根据VersionInfo中的类型定义MAJ和MIN,但类型约束不再按照预期的方式工作.我知道我们可能面临aux-pattern等类似问题所解决的类似问题,但我正在努力为我的问题提供类似的解决方案. 本质上,我想要预先定义的对象包装类型,我想通过这些对象而不是直接类型来进行类型约束. 是否有一个基本的原因,我不能,如果没有,我怎么能这样做? 解决方法
假设__1和__2是两个扩展VersionNumber的对象,有两个不同的类型_1,_2,它们在其中扩展了Nat.编译器拒绝编译是否有任何理由?
val foo: VersionInfo = VersionInfo( if (math.random < 0.5) __1 else __2,__2) ?在您当前的代码中,编译器没有理由拒绝这一点.这意味着VersionInfo打破了外部常量__1,__2与内部值major和minor之间的常量路径,这些路径存储在VersionInfo中.例如,只要将__1作为主要文件传递给VersionInfo foo,__1.Nat与foo.major.Nat类型相同的信息将永远丢失. 这可以通过简单地不抛弃此类型信息,而是将其作为类型参数附加到VersionInfo来轻松解决. 假设你的自然数看起来像这样: sealed trait NaturalNumber class _3 extends NaturalNumber class _2 extends _3 class _1 extends _2 class _0 extends _1 class VersionNumber { type Nat <: NaturalNumber } val __0 = new VersionNumber { type Nat = _0 } val __1 = new VersionNumber { type Nat = _1 } val __2 = new VersionNumber { type Nat = _2 } val __3 = new VersionNumber { type Nat = _3 } type IsEqual[A,B] = A =:= B type IsLessOrEqual[A,B] = A <:< B 您可以按如下方式定义VersionInfo和ResourceManifest: case class VersionInfo[Major,Minor]( major: VersionNumber { type Nat = Major },minor: VersionNumber { type Nat = Minor } ) trait ResourceManifest { def getResource: Int type Major <: NaturalNumber type Minor <: NaturalNumber } 然后将它们用作getResource的参数类型: def getResource[A,B] (manifest: ResourceManifest) (versionInfo: VersionInfo[A,B]) (implicit maj_check: manifest.Major IsEqual A,min_check: manifest.Minor IsLessOrEqual B ) : Unit = println("it compiles,ship it") 一点测试: val manifest21 = new ResourceManifest { def getResource = 21 type Major = _2 type Minor = _1 } val manifest22 = new ResourceManifest { def getResource = 22 type Major = _2 type Minor = _2 } getResource(manifest21)(VersionInfo(__2,__1)) getResource(manifest21)(VersionInfo(__2,__2)) // getResource(manifest22)(VersionInfo(__2,__1)) // won't compile,good getResource(manifest22)(VersionInfo(__2,__2)) 在上面的代码中,我尝试使用相同的名称as in this answer of yours from few months ago. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |