加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala:由嵌套类型值参数化的类型级编程

发布时间:2020-12-16 18:37:24 所属栏目:安全 来源:网络整理
导读:目前我有一个实现peano算法的程序: sealed trait NaturalNumber 和一个名为getResource的函数,它在其参数中获取具有接口的自然数字类型值: sealed trait VersionNumber { type Nat : NaturalNumber} 并根据此接口中提供的引用类型 – 值版本号:MAJ和MIN检
目前我有一个实现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.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读