6

按照这个问题,我试图找到一种方法让 Scala 编译器推断出两种类型 A 和 B 的最大常见子类型。

类似“A without B”的东西,其定义是:

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

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

编辑:

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

IE。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]
4

2 回答 2

3

听起来你想要一个 scala 类型的最小上界(LUB)?我会从 Miles 的 Shapeless 库中寻找灵感,他们实际上有一个LUBConstraint

或者,如果您想要更大的下限(GLB),恐怕我不得不向您推荐使用宏定义,您可以在其中获得 LUB 或 GLB,请参阅Types

于 2013-08-19T13:37:18.847 回答
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

    请注意,这使用类型投影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 )

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

于 2013-08-24T12:58:28.837 回答