scala中的隐式解析及其参数类型推断

时间:2014-10-14 13:46:29

标签: scala implicits

关于隐式分辨率如何工作是否有任何好的来源,特别是关于类型推断和顺序(如果有的话)搜索所有隐含参数的implicits?

首先,我当然读过Scala语言规范,所以我理解(或者我认为)基础知识,比如编译器搜索implicits的地方。无论何时我尝试做一些更高级的事情,比如递归暗示,我总是遇到超自然的问题,并且与它们一起工作似乎是在尝试实验魔法。即使将所有隐含在一个伴随对象中并因此消除所有冲突的困难问题,代码中的一点变化(根据规范不应改变语义)有时会在编译和不编译之间产生差异。

我记得编译器找不到隐式Companion的情况,但是在调用之前引入import Companion._使其编译,而对象Companion只有隐式defs返回Companion实例。

我有一个代码以这种方式构建的案例:

object ADT {

class Evidence;
object Evidence {
    class ParticularEvidence extends Evidence
    object ParticularEvidence {
        implicit def ... :ParticularEvidence
    }
}
...
def f(...)(implicit e:ParticularEvidence) = ...
...
f(...)

没有编译,但是将特殊情况defs(UniqueEvidence中唯一的)从PRIVEvidence移动到父Evidence对象使它们可见。

经过一些试验和错误,我明白解决逻辑是非常有限的,并且构造了这个类,例如:

implicit def f[X, Y, Z](x :X)(implicit e1:Evidence1[X, Y], implicit e2:Evidence[Y, Z]) :Y
...
val x :X = ...
val z:Z = x

其中证据类对其类型参数不变将很少编译,即使只有一个类型Z存在这样的隐式值。但我不知道为什么以下是一个问题:

/** Stub for the purpose of this example **/
sealed abstract class ||[+A,+B]
case class LeftVariant[+A](left :A) extends ||[A, Nothing]
case class RightVariant[+B](right :B) extends ||[Nothing, B]


object SupportingEvidence {

    /** C is an Atom and U==C or U is a type union explicitly containg C */
    sealed class ComponentOf[C, U] protected[SupportingEvidence]() extends SupportingEvidence

    final class Atom[T] protected[SupportingEvidence]() extends ComponentOf[T, T]

    implicit def atom[T](implicit ev :Not[T <:< ||[_,_]]) = a.asInstanceOf[Atom[T]]

    implicit def leftComponent[A, B, C](implicit ev: C ComponentOf A): C ComponentOf (A || B) =
        ev.asInstanceOf[ComponentOf[C, A || B]]

    implicit def rightComponent[A, B, C](implicit ev: C ComponentOf B, but: Not[C ComponentOf A]): C ComponentOf (A || B) =
        ev.asInstanceOf[ComponentOf[C, A || B]]

    private[this] val a = new Atom[Nothing]


    type Something[X] = Not[X<:<Nothing]


    /** T = U - C, calculated as follows:
      *  U = A || B => A - C || B - C
      *  U <: C => Nothing
      *  else => U
      */
    final class Minus[U, C, T] protected[SupportingEvidence]() extends SupportingEvidence

    object Minus {
        implicit def nothing[U, C](implicit atom :Atom[U], conforms :U ConformsTo C) :Minus[U, C, Nothing] =
            certify[U, C, Nothing]

        implicit def self[U, C](implicit atom :Atom[U], diff :Not[U ConformsTo C]) :Minus[U, C, U] =
            certify[U, C, U]

        implicit def left[A, B, C, T](implicit left :Minus[A, C, T],
                                      leftSomething :Not[C ConformsTo A],
                                      rightNothing :C ConformsTo B) :Minus[A || B, C, T] =
            certify[A || B, C, T]

        implicit def right[A, B, C, T](implicit leftNothing :C ConformsTo A,
                                       right :Minus[B, C, T]) :Minus[A || B, C, T] =
            certify[A || B, C, T]

        implicit def union[A, B, C, L, R](implicit atom :Atom[C],
                                          leftSomething :Not[C ConformsTo A],
                                          rightSomething :Not[C ConformsTo B],
                                          left :Minus[A, C, L],
                                          right :Minus[B, C, R]) :Minus[A || B, C, L || R] =
            certify[A || B, C, L || R]

        private[this] def certify[U, C, T] = m.asInstanceOf[Minus[U, C, T]]

    }

    private[this] val m = new Minus[Nothing, Nothing, Nothing]
}



final class ConformsTo[-X, +Y] protected[ADT] (protected[ADT] val cast :X=>Y) //extends Conversion[X, Y]

object ConformsTo {
    import SupportingEvidence._

    private def apply[X, Y](fun :X=>Y) :ConformsTo[X, Y] = new ConformsTo(fun)

    implicit def directlyConformsTo[X, Y](implicit ev :X <:< Y) :ConformsTo[X, Y] =
        ConformsTo(ev.apply _)

    implicit def conformsToLeft[X, A, B](implicit atom :Atom[X],
                                         conform :ConformsTo[X, A],
                                         only :Not[ConformsTo[X, B]]) :ConformsTo[X, A || B] =
        ConformsTo((x :X) => LeftVariant(conform.cast(x)))

    implicit def conformsToRight[X, A, B](implicit atom :Atom[X],
                                          conform :ConformsTo[X, B],
                                          only :Not[ConformsTo[X, A]]) :ConformsTo[X, A || B] =
        ConformsTo((x :X) => RightVariant(conform.cast(x)))

    implicit def conformsToBoth[X, A, B](implicit atom :Atom[X],
                                         left :ConformsTo[X, A],
                                         right :ConformsTo[X, B]) :ConformsTo[X, A || B] =
        ConformsTo((x :X) => LeftVariant(left.cast(x)))

    implicit def alternativesConform[A, B, Y](implicit left :ConformsTo[A, Y], right :ConformsTo[B, Y],
                                              nonDirect :Not[(A || B) <:< Y]) :ConformsTo[A || B, Y] =
        ConformsTo((x :A || B) => x match {
            case LeftVariant(l) => left.cast(l)
            case RightVariant(r) => right.cast(r)
        })

}
}

/** Implicit value for Not[T] exists <=> there's no implicit value for T in scope */
final class Not[+T](override val toString :String)

object Not {
    private[this] val nice = new Not[Nothing]("default")
    private[this] val mean = new Not[Nothing]("conflict")

    implicit def conflict[T](implicit ev :T) :Not[T] = mean
    implicit def default[T] :Not[T] = nice
}

//test Minus
class SA
class SB
implicitly[Minus[SA || SB, Nothing, SA || SB]] //unhelpful divergent expansion error

我在这里用盒装类型的联合进行实验(|| [A,B]基本上是一个美化的结构和隐藏的左/右选项。)减号是我想要实现的类型算术的一部分:在最后一行中,我要求编译器提供SA || SB - Nothing = SA || SB的证据。更一般地说,我期望隐含的负[A,B,C]存在,当且仅当C是包含A的所有值而B的所有值的最小类型时。更重要的是,我希望即使C也能找到sch证据是未知的,只有来自A,B,所以我可以尝试实现隐式证据,允许自动将任何类型联合转换为某种规范化形式。最终,我想找到提供Normalized值的隐式方法的这种组合,我将能够编写如下内容:

def normalize[A,B,N](v :A || B)(implicit :Normalized[A || B, N]) :N
val a :Int || java.sql.Date || Int || String || java.lang.Date = ...
val n = normalize(a) //n is of type Int || java.lang.Date || String

因此,编译器在隐式方法强制执行的一组规则的帮助下,应该能够通过去除由其他部分支配的部分来简化联合类型。为此,我必须能够在类型上进行集合减法。

我知道我可能在这方面处于敌对的地位,任何编译器的改变都可能会破坏整个程序,但我将其视为一个谜题和概念证明。一旦我知道什么是可能的,我就会开始思考什么是明智的。

所以具体问题是:有没有人知道上面的代码究竟是什么问题,如果可以修复的话可以吗?

1 个答案:

答案 0 :(得分:1)

编辑:我重写了这个答案,专注于Minus

我在减号中交换了隐式ConformsTo规则。在nothingself,您需要检查U ConformsTo C。您应该在leftright

中执行相同的操作
final class Minus[U, C, T] extends SupportingEvidence

object Minus {

  implicit def nothing[U, C](implicit atom: Atom[U], conforms: U ConformsTo C): Minus[U, C, Nothing] =
      certify[U, C, Nothing]

  implicit def self[U, C](implicit atom: Atom[U], diff: Not[U ConformsTo C]): Minus[U, C, U] =
      certify[U, C, U]

  implicit def left[A, B, C, T](implicit left: Minus[A, C, T],
                                leftSomething: Not[A ConformsTo C],
                                rightNothing: B ConformsTo C): Minus[A || B, C, T] =
      certify[A || B, C, T]

  implicit def right[A, B, C, T](implicit leftNothing: A ConformsTo C,
                                 right: Minus[B, C, T]): Minus[A || B, C, T] =
      certify[A || B, C, T]

  implicit def union[A, B, C, L, R](implicit //atom: Atom[C],
                                    leftSomething: Not[A ConformsTo C],
                                    rightSomething: Not[B ConformsTo C],
                                    left: Minus[A, C, L],
                                    right: Minus[B, C, R]): Minus[A || B, C, L || R] =
      certify[A || B, C, L || R]

  def certify[U, C, T] = nothingMinusNothingIsNothing.asInstanceOf[Minus[U, C, T]]

  val nothingMinusNothingIsNothing = new Minus[Nothing, Nothing, Nothing]
}

它允许您检查:

trait Animal
trait Car
trait Cow extends Animal
trait Barking
class Dog extends Animal with Barking

implicitly[Minus[Dog, Animal, Nothing]]
implicitly[Minus[Dog, Barking, Nothing]]
implicitly[Minus[Cow, Barking, Cow]]
implicitly[Minus[Cow, Animal with Barking, Cow]]

implicitly[Minus[Dog || Cow, Barking, Cow]]
implicitly[Minus[Cow || Dog, Barking, Cow]]
implicitly[Minus[Cow || Car, Barking, Cow || Car]]