【问题标题】:Generic derivation of type class instance for ADTADT 的类型类实例的通用派生
【发布时间】:2016-10-25 12:13:40
【问题描述】:

假设我有一个 ADT 和类型类 Foo,如下所示:

sealed trait A
case class A1() extends A
case class A2() extends A
case class A3() extends A

trait Foo[X] { def foo(x: X): String; }
object Foo {
  implicit val a1foo = new Foo[A1] { def foo(a1: A1) = "A1" }
  implicit val a2foo = new Foo[A2] { def foo(a2: A2) = "A2" }
  implicit val a3foo = new Foo[A3] { def foo(a3: A3) = "A3" }
}  

现在我可以这样写Foo[A]

implicit val afoo = new Foo[A] {
  def foo(a: A) = a match {
    case a1 : A1 => a1foo.foo(a1)
    case a2 : A2 => a2foo.foo(a2)
    case a3 : A3 => a3foo.foo(a3)
  }
}

很遗憾,这段代码太boilerplaty。是否有可能自动摆脱所有样板文件并派生 Foo[A](可能使用shapeless)?

【问题讨论】:

  • implicit def fromA[A](a: A): Foo[A] = new Foo[A] { def foo(a: A) = a.getClass.getSimpleName } 够用吗?它将编译 Foo.fromA(A1).foo(A1) 而不是 Foo.fromA(A1).foo(A2)
  • getClass.getSimpleName 是运行时反射调用吗?
  • 是的,但我添加它是为了说明。你知道里面fooA型。
  • @ipoteka 我没明白,抱歉。你能写更详细的答案吗?
  • Shapeless 确实可以用来解决问题。它需要编写比您在问题中列出的代码更多的代码,但它会在更大的代码库中减少很多样板。我写了a blog post about using Shapeless,这可能对您描述的场景有所帮助。对不起,无耻的插件。

标签: scala typeclass shapeless


【解决方案1】:

这个afoo 隐式不仅在其他三个存在的情况下无用,甚至更糟糕,因为它会因new A {} 值上的MatchError 而失败。

我不明白为什么你需要 Foo[A] 这样的实例,因为你有一个隐含覆盖了所有可能的(有效)类型的 A 值和 afoo 没有添加任何东西。

我可以想象如果你有一个函数

def foo(a: A)(implicit f: Foo[A]): String = f.foo(a)

那么,当然,a1fooa2fooa3foo 都不适合。 afoo 会,所以foo(A1()) 会编译并正常工作,但foo(new A {}) 也会编译,但会以MatchError 失败。顺便说一句,如果代码中存在调用foo(new A {}),它将在编译时出现关于非穷举匹配的警告,但如果不是,它将静默编译。

所以解决这个问题的方法是修改foo,取更精确的类型:

def foo[X <: A](a: X)(implicit f: Foo[X]): String = f.foo(a)

现在foo(A1()) 将编译并选择a1foo(与A2A3 相同),而foo(new A {}) 不会编译(因为它不应该)。


更新

如果您仍希望将Foo[A] 的实例作为默认情况,您可以像这样更改您的代码:

object Foo extends Foo_1 {

  implicit val a1foo: Foo[A1] = ...
  implicit val a2foo: Foo[A2] = ...
  implicit val a3foo: Foo[A3] = ...
}

trait Foo_1 {
  // this implicit has a lower priority:
  implicit val afoo: Foo[A] = new Foo[A] { def foo(a: A) = "A" }
}

现在foo(new A {})foo(A2(): A) 将编译并返回"A"

P.S.顺便说一句,写隐式的显式类型是recommended

【讨论】:

    猜你喜欢
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    • 2017-04-28
    • 2018-06-26
    • 1970-01-01
    • 2023-03-19
    • 2011-11-05
    相关资源
    最近更新 更多