Rea*_*ame 6 scala type-constraints
根据这个问题,我试图找到一种方法让Scala编译器推断两种类型A和B的最大公共子类型.
类似"A without B"的东西,其定义是:
(A without B = C) === (A = C with B)
Run Code Online (Sandbox Code Playgroud)
或者返回C的类型函数,其中:
编辑:
A <: C && C <:!< B
Run Code Online (Sandbox Code Playgroud)
即.A是C的子类型,C不是B的子类型
事实上,我希望有人会指出这与"最常见的子类型"不同,因为我实际上并不需要这样做A <: B.
用法:
trait Syntax
trait ANYSYNTAX extends Syntax
trait NUMERIC extends ANYSYNTAX
trait DISCRETE extends ANYSYNTAX
trait POSITIVE extends ANYSYNTAX
trait CONST extends ANYSYNTAX
type NUMCONST = NUMERIC with CONST
type POSCONST = POSITIVE with CONST
type ORDINALCONST = DISCRETE with CONST
type INTEGER = NUMERIC with DISCRETE
type POSNUM = POSITIVE with NUMERIC
type POSINT = POSNUM with INTEGER
type INTCONST = INTEGER with NUMCONST with ORDINALCONST
type POSNUMCONST = POSNUM with POSCONST with NUMCONST
type POSINTCONST = POSNUMCONST with INTCONST with POSINT
Run Code Online (Sandbox Code Playgroud)
然后我希望能够传播类型约束,如下所示:
abstract class Expression[+R]( val args: Expression[_]* )
case class Add[A <: R, R <: NUMERIC]( arg1: Expression[A], arg2: Expression[A] ) extends Expression[R] {
case class Subtract[A <: R, R : A without POSITIVE]( arg1: Expression[A], arg2: Expression[A] ) extends Expression[R] {
case class Multiply[A <: R, R <: NUMERIC]( arg1: Expression[A], arg2: Expression[A] ) extends Expression[R]{
case class Divide[A <: R, R : A without DISCRETE]( arg1: Expression[A], arg2: Expression[A] ) extends Expression[R] {
Run Code Online (Sandbox Code Playgroud)
我一直在尝试使用从其他SO答案中借用的某些类型约束来提出一些东西:
sealed class =!=[A,B]
trait LowerPriorityImplicits {
implicit def equal[A]: =!=[A, A] = sys.error("should not be called")
}
object =!= extends LowerPriorityImplicits {
implicit def nequal[A,B](implicit same: A =:= B = null): =!=[A,B] =
if (same != null) sys.error("should not be called explicitly with same type")
else new =!=[A,B]
}
// Encoding for "A is not a subtype of B"
trait <:!<[A, B]
// Uses ambiguity to rule out the cases we're trying to exclude
implicit def nsub[A, B] : A <:!< B = null
implicit def nsubAmbig1[A, B >: A] : A <:!< B = null
implicit def nsubAmbig2[A, B >: A] : A <:!< B = null
Run Code Online (Sandbox Code Playgroud)
我有一些测试用例:
implicitly[POSINT <:!< CONST]
implicitly[POSITIVE <:!< OPINION]
implicitly[DOGMA <:!< CONST]
implicitly[POSINTCONST <:< POSITIVE with CONST]
implicitly[POSINTCONST <:< POSCONST]
implicitly[POSITIVE with CONST <:!< POSINTCONST]
implicitly[POSITIVE =:= POSCONST without CONST]
implicitly[NUMERIC =:= INTEGER without DISCRETE]
implicitly[POSINT =:= POSINTCONST without CONST]
Run Code Online (Sandbox Code Playgroud)
这些应该失败:
implicitly[POSINT =:= POSINTCONST without OPINION]
implicitly[POSINT with OPINION =!= POSINTCONST without OPINION]
Run Code Online (Sandbox Code Playgroud)
在随机敲击键盘并尽可能多地阅读我能理解的类型约束之后,这就是我想到的:
// A without B is C
sealed abstract class isWithout[A, B, C]
object Syntax {
implicit def composedWithout[A <: C, B, C](implicit ev: C <:!< B): isWithout[A, B, C] = new isWithout[A, B, C] {
def apply(a: A) = a
}
type without[A, B] = {
type l[C] = isWithout[A, B, C]
}
}
Run Code Online (Sandbox Code Playgroud)
测试一下它是否有效:
implicitly[isWithout[POSCONST, POSITIVE, CONST]]
implicitly[isWithout[POSINTCONST, DISCRETE, POSNUMCONST]]
implicitly[isWithout[POSINTCONST, DISCRETE, POSITIVE]]
implicitly[isWithout[POSNUM, CONST, POSNUM]]
implicitly[isWithout[POSCONST, CONST, POSITIVE ]]
implicitly[isWithout[POSCONST, POSITIVE, CONST ]]
implicitly[isWithout[INTEGER, DISCRETE, NUMERIC ]]
implicitly[isWithout[POSINTCONST, CONST, POSINT ]]
Run Code Online (Sandbox Code Playgroud)
并在应该失败时失败:
implicitly[isWithout[POSINTCONST, INTCONST, POSINTCONST]]
implicitly[isWithout[NUMERIC, ANYSYNTAX, ANYSYNTAX]]
implicitly[isWithout[INTEGER, POSITIVE, POSINT]]
implicitly[isWithout[POSNUM, DISCRETE, POSCONST]]
Run Code Online (Sandbox Code Playgroud)
implicitly让编译器在当前隐式作用域中查找可以生成所需类型的对象(在本例中为 isWithout 类的实例)的隐式函数。apply如果它找到满足类型签名的类型,那么它就会编译(我认为类中定义的方法返回什么并不重要)。重要的一点是类型签名,它利用了<:!<问题中提到的并借用了 Miles 的另一个 SO 答案。
该类型签名表示:A 是 C 的子类型,并且 C 不能是 B 的子类型。替代版本(对应于问题中的第一个定义)是:
implicit def composedWithout[A <: C with B, B, C](implicit ev: C <:!< B): isWithout[A, B, C] = new isWithout[A, B, C] {
def apply(a: A, b: B) = a
}
Run Code Online (Sandbox Code Playgroud)
这可以通过几种方式使用:
检查类型层次结构(即测试用例)的有效性,如上面使用implicitly.
指定方法参数的类型:
def test[R : without[POSINTCONST, DISCRETE]#l](v: R) = v
请注意,这使用类型投影without#l[C] = isWithout[A, B, C]作为编译器脱糖的上下文绑定:
def test[R](v: R)(implicit $ev0: isWithout[POSINTCONST, DISCRETE, R]) = v
Run Code Online (Sandbox Code Playgroud)
因此,它要求指定的隐式位于范围内。
作为类型约束,按照原始问题中的要求:
case class Subtract[A <: R, R <: A without POSITIVE]( arg1: Expression[A], arg2: Expression[A] ) extends BinaryPrimitive[A, R]( arg1, arg2 )
虽然我承认我还没有运行任何东西,但它可以编译,所以它可能没有做我认为它正在做的事情。