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

时间:2013-08-19 12:56:00

标签: scala type-constraints

沿着this question,我试图找到一种方法让Scala编译器推断出两种类型A和B的最大公共子类型。

类似“A without B”的内容,定义为:

(A without B = C) === (A = C with B)

或者返回C的类型函数,其中:

编辑:

A <: C && C <:!< B

即。 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

然后我希望能够传播类型约束,如下所示:

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] {

我一直在尝试使用从其他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

我有一些测试用例:

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]

这些应该失败:

implicitly[POSINT =:= POSINTCONST without OPINION]
implicitly[POSINT with OPINION =!= POSINTCONST without OPINION]

2 个答案:

答案 0 :(得分:3)

听起来你想要scala类型的最小上限(LUB)?我会向Miles的无形图书馆寻找灵感,他们实际上有LUBConstraint

或者如果你想要更大的下界(GLB)我恐怕我不得不推荐你使用宏定义来获得LUB或GLB,请参阅Types

答案 1 :(得分: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]
  }
}

测试它似乎有效:

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 ]]

并且应该失败:

implicitly[isWithout[POSINTCONST, INTCONST, POSINTCONST]]
implicitly[isWithout[NUMERIC, ANYSYNTAX, ANYSYNTAX]]
implicitly[isWithout[INTEGER, POSITIVE, POSINT]]
implicitly[isWithout[POSNUM, DISCRETE, POSCONST]]

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
}

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

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

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

    def test[R : without[POSINTCONST, DISCRETE]#l](v: R) = v

    请注意,这使用了类型projection without#l[C] = isWithout[A, B, C]作为编译器所依赖的上下文绑定:

    def test[R](v: R)(implicit $ev0: isWithout[POSINTCONST, DISCRETE, R]) = v
    

    因此,它要求指定的隐含在范围内。

  3. 作为类型约束,如原始问题中所要求的那样:

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

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