Scala: Implicit evidence for class with type parameter

后端 未结 3 1527
清歌不尽
清歌不尽 2021-02-14 07:20

Here is a simple setup with two traits, a class with a covariant type parameter bounded by the previous traits, and a second class with a type parameter bounded by the other cla

3条回答
  •  北海茫月
    2021-02-14 07:42

    0__'s answer (use the implicit evidence argument to turn bar into the right type) is the answer I'd give to the specific question you asked (although I'd suggest not using implicitly if you've got the implicit argument sitting right there).

    It's worth noting that the situation you're describing sounds like it might be a good use case for ad-hoc polymorphism via type classes, however. Say for example that we've got the following setup:

    trait Foo
    case class Bar[F <: Foo](foo: F)
    case class Grill[B <: Bar[_]](bar: B)
    

    And a type class, along with some convenience methods for creating new instances and for pimping a readField method onto any type that has an instance in scope:

    trait Readable[A] { def field(a: A): Int }
    
    object Readable {
      def apply[A, B: Readable](f: A => B) = new Readable[A] {
        def field(a: A) = implicitly[Readable[B]].field(f(a))
      }
    
      implicit def enrich[A: Readable](a: A) = new {
        def readField = implicitly[Readable[A]].field(a)
      }
    }
    
    import Readable.enrich
    

    And a couple of instances:

    implicit def barInstance[F <: Foo: Readable] = Readable((_: Bar[F]).foo)
    implicit def grillInstance[B <: Bar[_]: Readable] = Readable((_: Grill[B]).bar)
    

    And finally a readable Foo:

    case class MyFoo(x: Int) extends Foo
    
    implicit object MyFooInstance extends Readable[MyFoo] {
      def field(foo: MyFoo) = foo.x
    }
    

    This allows us to do the following, for example:

    scala> val readableGrill = Grill(Bar(MyFoo(11)))
    readableGrill: Grill[Bar[MyFoo]] = Grill(Bar(MyFoo(11)))
    
    scala> val anyOldGrill = Grill(Bar(new Foo {}))
    anyOldGrill: Grill[Bar[java.lang.Object with Foo]] = Grill(Bar($anon$1@483457f1))
    
    scala> readableGrill.readField
    res0: Int = 11
    
    scala> anyOldGrill.readField
    :22: error: could not find implicit value for evidence parameter of
    type Readable[Grill[Bar[java.lang.Object with Foo]]]
                  anyOldGrill.readField
                  ^
    

    Which is what we want.

提交回复
热议问题