强制所有某种类型“T”对特征“F”的方法的输入先前已由“F”本身生成

Enforce that all inputs of some type `T` to methods of a trait `F` have been previously produced by `F` itself

假设我有一些特质Foo,例如:

trait Foo {
  def bar: List[Int]
  def baz(i: Int): Unit
}

我想在编译时强制传递给 baz 的所有输入都已由 bar 生成。例如,如果这是 Foo:

的实现
object A extends Foo {
  def bar = List(2, 3, 5, 7)
  def baz(i: Int): Unit = {
    if (bar contains i) println("ok")
    else println("not good")
  }
}

然后我想强制只能将个位数的质数传递给 baz。如果已知 baz 的输入类型是 Int,这显然不起作用,因为它允许我实例化各种非质数且不在 0 和 [ 之间的整数=22=]:

val okValues: List[Int] = A.bar
A.baz(okValues(1)) // ok
A.baz(3)           // ok (but dangerous! `3` appeared out of nowhere!)
A.baz(42)          // not good

如何强制只有 bar 之前生成的值才能传递给 baz


什么不起作用

Int 转换为 Foo 的类型成员没有帮助,因为它在 [=14] 的实现 A 中被实例化为具体类型 Int =]:

trait Foo {
  type T
  def bar: List[T]
  def baz(t: T): Unit
}

object A extends Foo {
  type T = Int 
  def bar = List(2, 3, 4, 5)
  def baz(i: Int): Unit = {
    if (bar contains i) println("ok")
    else println("not good")
  } 
}

A.baz(42) // not good

这是一个解决方案,它依赖于用抽象类型成员 T 替换具体类型 Int,然后根本不公开 T 的具体实现 [=11] =]:

  • 将具体类型 Int 替换为抽象类型成员 T
  • 将方法 barbaz 以及类型 T 移动到内部特征 YFoo
  • Foo 中,提供一个生成 YFoo 但不暴露 T 是什么的方法。

在代码中:

trait Foo {
  trait YFoo {
    type T
    def bar: List[T]
    def baz(i: T): Unit
  }
  def yFoo: YFoo
}

object B extends Foo {
  def yFoo: YFoo = new YFoo {
    type T = Int
    def bar: List[Int] = List(2, 3, 5, 7)
    def baz(i: Int): Unit = {
      if (bar contains i) println("ok")
      else println("not good")
    }
  }
}

val a = B.yFoo
val okValues: List[a.T] = a.bar
a.baz(okValues(1)) // ok
// a.baz(3)           // dangerous stuff doesn't compile!
// found   : Int(3)
// required: a.T

// a.baz(42)          // invalid stuff also doesn't compile
// found   : Int(42)
// required: a.T

现在所有危险/无效的东西甚至都无法编译。您可以传递给 baz 的唯一值是由 bar.

生成的 "list of certified values" 中的值