[英]Enforce that all inputs of some type `T` to methods of a trait `F` have been previously produced by `F` itself
Suppose that I have some trait Foo
, for example: 假设我有一些特征Foo
,例如:
trait Foo {
def bar: List[Int]
def baz(i: Int): Unit
}
I want to enforce at compile time that all inputs passed to baz
have been previously produced by bar
. 我想在编译时强制所有传递给baz
输入都是bar
先前产生的。 For example, if this is an implementation of Foo
: 例如,如果这是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")
}
}
then I want to enforce that only single-digit primes can be passed to baz
. 然后我要强制只将个位数的素数传递给baz
。 This obviously doesn't work if the input type of baz
is known to be Int
, because it allows me to instantiate all kinds of integers that are not prime and not between 0
and 9
: 如果已知baz
的输入类型为Int
,则这显然不起作用,因为它允许我实例化所有不是素数且不在0
到9
之间的整数:
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
How can I enforce that only the values previously produced by bar
can be passed to baz
? 我该如何强制只将先前由bar
生成的值传递给baz
?
What doesn't work 什么不起作用
Converting Int
to a type member of Foo
doesn't help, because it's instantiated to the concrete type Int
in the implementation A
of Foo
: 将Int
转换为Foo
的类型成员没有帮助,因为它在Foo
的实现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
Here is one solution that relies on replacing a concrete type Int
by an abstract type member T
, and then simply not exposing the concrete implementation of T
by Int
: 这是一个解决方案,它依赖于用抽象类型成员T
替换具体类型Int
,然后简单地不通过Int
公开T
的具体实现:
Int
by an abstract type member T
用抽象类型成员T
替换具体的Int
类型 bar
and baz
and the type T
into an inner trait YFoo
将方法bar
和baz
以及类型T
到内部特征YFoo
Foo
, provide a method that produces YFoo
, but does not expose what T
is. 在Foo
内部,提供一种生成YFoo
的方法,但不公开T
是什么。 In code: 在代码中:
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
Now all the dangerous / invalid stuff doesn't even compile. 现在,所有危险/无效的东西甚至都无法编译。 The only values that you can pass to baz
are those from the "list of certified values" produced by bar
. 可以传递给baz
的唯一值是bar
产生的“认证值列表”中的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.