Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: Implicit evidence for class with type parameter

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 class. For both classes, a particular method is available (via implicit evidence) only if one of the two traits underlies the type parameter. This compiles fine:

trait Foo
trait ReadableFoo extends Foo {def field: Int}

case class Bar[+F <: Foo](foo: F) {
  def readField(implicit evidence: F <:< ReadableFoo) = foo.field
}

case class Grill[+F <: Foo, +B <: Bar[F]](bar: B) {
  def readField(implicit evidence: F <:< ReadableFoo) = bar.readField
}

However, since Bar is covariant in F, I shouldn't need the F parameter in Grill. I should just require that B is a subtype of Bar[ReadableFoo]. This, however, fails:

case class Grill[+B <: Bar[_]](bar: B) {
  def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField
}

with the error:

error: Cannot prove that Any <:< this.ReadableFoo.
  def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField

Why is the implicit evidence not being taken into account?

like image 205
drhagen Avatar asked Jul 01 '12 18:07

drhagen


People also ask

How do you pass an implicit parameter?

The implicit parameter in Java is the object that the method belongs to. It's passed by specifying the reference or variable of the object before the name of the method. An implicit parameter is opposite to an explicit parameter, which is passed when specifying the parameter in the parenthesis of a method call.

What are implicit parameters in Scala?

Implicit parameters are the parameters that are passed to a function with implicit keyword in Scala, which means the values will be taken from the context in which they are called.

What is type parameter in Scala?

Language. Methods in Scala can be parameterized by type as well as by value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses.

How do Scala implicit classes work?

Scala 2.10 introduced a new feature called implicit classes. An implicit class is a class marked with the implicit keyword. This keyword makes the class's primary constructor available for implicit conversions when the class is in scope. Implicit classes were proposed in SIP-13.


2 Answers

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
<console>: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.

like image 96
Travis Brown Avatar answered Oct 27 '22 17:10

Travis Brown


This is not an answer to the question, but to show that the 'type constraint' is really just an implicit conversion:

Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_33).
Type in expressions to have them evaluated.
Type :help for more information.

scala> trait A { def test() {} }
defined trait A

scala> class WhatHappens[T] { def test(t: T)(implicit ev: T <:< A) = t.test() }
defined class WhatHappens

scala> :javap -v WhatHappens
...
public void test(java.lang.Object, scala.Predef$$less$colon$less);
  Code:
   Stack=2, Locals=3, Args_size=3
   0:   aload_2
   1:   aload_1
   2:   invokeinterface #12,  2; //InterfaceMethod scala/Function1.apply:(Ljava/lang/Object;)Ljava/lang/Object;
   7:   checkcast   #14; //class A
   10:  invokeinterface #17,  1; //InterfaceMethod A.test:()V
   15:  return
...
  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      16      0    this       LWhatHappens;
   0      16      1    t       Ljava/lang/Object;
   0      16      2    ev       Lscala/Predef$$less$colon$less;
...
like image 36
0__ Avatar answered Oct 27 '22 17:10

0__