Scala 中的抽象类型、变量和类型类

Abstract type, variables and typeclasses in Scala

我正在尝试创建一个取决于用户输入的类型class。假设我们有一些案例对象:

sealed trait H
case object Ha extends H
case object Hb extends H

和类型 class:

trait Foo[A] {
    def bar: String
}

object Foo {
    def bar[A : Foo] = implicitly[Foo[A]].bar

    implicit object FooA extends Foo[Ha.type] {
        override def bar: String = "A"
    }

    implicit object FooB extends Foo[Hb.type] {
        override def bar: String = "B"
    }
}

虽然我找到了一个使用匹配的有效解决方案:

variableComingFromMainArgs match {
  case "a" => Foo.bar[Ha.type] _
  case "b" => Foo.bar[Hb.type] _
}

我记得我们在 Scala 中有抽象类型,所以我可以将我的案例 class 更改为:

sealed trait H {
    type T <: H
}

case object Ha extends H {
    type T = this.type
}

case object Hb extends H {
    type T = this.type
}

现在,根据用户对程序的输入,我可以做类似的事情 val 变量 = Ha println(Foo.bar[variable.T])

但是,由于某种原因,这不起作用,并且错误对我来说不是很有用:

error: could not find implicit value for evidence parameter of type Foo[variable.T]
        println(Foo.bar[variable.T])

是否可以克服这个问题,如果不能,为什么?

谢谢。

隐式是编译时构造,因此原则上它们不能直接依赖于用户输入(程序员可以像您一样使用模式匹配连接它)。

考虑以下代码。它按预期编译和工作:

trait H {
    type A
}

case object Ha extends H {
    override type A = Int
}

case object Hb extends H {
  override type A = Long
}

trait Adder[T] {
  def add(a: T, b: T): T
}

implicit object IntAdder extends Adder[Int] {
  override def add(a: Int, b: Int): Int = a + b
}

implicit object LongAdder extends Adder[Long] {
  override def add(a: Long, b: Long): Long = a + b
}

def addWithAdder(input: H)(a: input.A, b: input.A)(implicit ev: Adder[input.A]): input.A = ev.add(a, b)

val x: Int = addWithAdder(Ha)(3, 4)
val y: Long = addWithAdder(Hb)(3, 4)

让我们关注addWithAdder 方法。由于路径依赖类型,编译器可以为此任务选择正确的隐式。不过这个方法还是和下面的基本一样:

def add[T](a: T, b: T)(implicit ev: Adder[T]) = ev.add(a, b)

第一个可以拥有的唯一优势是您可以自己提供所有实例并阻止代码的用户添加自己的类型(当 H 是密封的并且所有实现都是最终的)。