找到两个Scala类型的最常见子类型

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)

Rea*_*ame 1

在随机敲击键盘并尽可能多地阅读我能理解的类型约束之后,这就是我想到的:

// 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)

这可以通过几种方式使用:

  1. 检查类型层次结构(即测试用例)的有效性,如上面使用implicitly.

  2. 指定方法参数的类型:

    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)

    因此,它要求指定的隐式位于范围内。

  3. 作为类型约束,按照原始问题中的要求:

    case class Subtract[A <: R, R <: A without POSITIVE]( arg1: Expression[A], arg2: Expression[A] ) extends BinaryPrimitive[A, R]( arg1, arg2 )

    虽然我承认我还没有运行任何东西,但它可以编译,所以它可能没有做我认为它正在做的事情。