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?
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.
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.
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.
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.
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.
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;
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With