Implicit class resolution for parameterized types - scala

In the following example, it seems that the Scala compiler only recognizes an implicit class when it is defined to take the higher-kinded representation of Wrapper. Why is that?
scala> case class Nested(n: Int)
defined class Nested
scala> case class Wrapper[A <: Product](nested: A)
defined class Wrapper
scala> implicit class I1[W <: Wrapper[A], A <: Product](underlying: W) {
| def ok1() = true
| }
defined class I1
scala> Wrapper(Nested(5)).ok1()
<console>:26: error: value ok1 is not a member of Wrapper[Nested]
Wrapper(Nested(5)).ok1()
^
scala> implicit class I2[W <: Wrapper[_]](underlying: W) {
| def ok2() = true
| }
defined class I2
scala> Wrapper(Nested(5)).ok2()
res1: Boolean = true
Is there a workaround for implicit resolution that maintains full information about the nested type, allowing typeclass evidence, e.g., TypeTag, to be attached to it?
Note: the example above shows Nested and Wrapper to be case classes but that's not integral to the question. It's simply a convenience for a shorter and simpler console session.

This is happening because of a limitation in Scala's type inference. See SI-2272.
The implicit fails to resolve because the compiler cannot properly infer A. We can see this if we enable -Xlog-implicits. Notice that A is inferred as Nothing:
I1 is not a valid implicit value for Test.w.type => ?{def ok: ?} because:
inferred type arguments [Wrapper[Nested],Nothing] do not conform to method I1's type parameter bounds [W <: Wrapper[A],A <: Product]
The same thing happens if we try to instantiate I1 manually:
scala> val w = Wrapper(Nested(5))
w: Wrapper[Nested] = Wrapper(Nested(5))
scala> new I1(w)
<console>:21: error: inferred type arguments [Wrapper[Nested],Nothing] do not conform to class I1's type parameter bounds [W <: Wrapper[A],A <: Product]
new I1(w)
^
<console>:21: error: type mismatch;
found : Wrapper[Nested]
required: W
new I1(w)
^
Now, the work-arounds.
First, Wrapper is a case class, so there shouldn't be a reason for it to have sub-types. You can remove the W type parameter, and change underlying to a Wrapper[A]:
implicit class I1[A <: Product](underlying: Wrapper[A]) {
def ok = true
}
If you still wish to require two type parameters, you can also require implicit evidence that W <:< Wrapper[A], while removing the upper-bound on the type parameter W:
implicit class I1[W, A <: Product](underlying: W)(implicit ev: W <:< Wrapper[A]) {
def ok = true
}

Everything Michael said is true. Here is some extra perspective on this issue.
Because of the way you wrote your implicit class it looks like you want the implicit class to work on all subtypes of Wrapper and have as specific information about all types involved as possible. (99% of the time it's a bad idea to extend case classes, but it is possible, and these tricks also work for non case classes).
The trick basically is to make sure that all the type parameters that you want inferred are present somewhere in the value parameter lists. Another thing to keep in mind is this:
scala> trait Foo[A]; trait Bar extends Foo[Int]
defined trait Foo
defined trait Bar
scala> implicitly[Bar with Foo[Int] =:= Bar]
res0: =:=[Bar with Foo[Int],Bar] = <function1>
Take these two pieces of knowledge and you can rewrite your implicit class like this:
implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
def ok1(): (Y, A) = ???
}
And see it at work:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Nested(n: Int)
case class Wrapper[A <: Product](nested: A)
class Crazy(override val nested: Nested) extends Wrapper[Nested](nested)
implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
def ok1(): (Y, A) = ???
}
// Exiting paste mode, now interpreting.
scala> :type Wrapper(Nested(5)).ok1()
(Wrapper[Nested], Nested)
scala> :type new Crazy(Nested(5)).ok1()
(Crazy, Nested)
Note that the last solution Michael gave is based on the same thing: by moving the upper bound to the implicit parameter list, A is now present in the value parameter lists and can be inferred by the compiler.

Related

Scala Co-Variant Bounded Types

I'm playing with Scala's type system with the following example:
scala> sealed trait A
defined trait A
scala> case class A1(n: Int) extends A
defined class A1
scala> case class A2(n: Int) extends A
defined class A2
scala> case class MyCase[+P <: A](a: A, y: String)
defined class MyCase
scala> val l = List(MyCase[A1](A1(1), "A1"), MyCase[A2](A2(2), "A2"))
Now, when I do the following:
scala> l.head.asInstanceOf[MyCase[A2]]
res2: MyCase[A2] = MyCase(A1(1),A1)
How can a MyCase[A1] instance be assigned to a MyCase[A2] reference? I mean MyCase[A1] and MyCase[A2] are at the same level in the object hierarchy!
As I understand you're wondering why you didn't get an error here in runtime, when you did l.head.asInstanceOf[MyCase[A2]]. So the answer is simple - type erasure. For JVM both types are equal as it ignores generic parameter - it simply doesn't know about them as they exist on compiler level only, so only compiler can give you an exception:
scala> val mcA2: MyCase[A2] = l.head
<console>:15: error: type mismatch;
found : MyCase[Product with Serializable with A]
required: MyCase[A2]
val mcA2: MyCase[A2] = l.head
^
However, asInstanceOf ignores this check. So when you do
case class MyCase[+P <: A](a: P) //P instead of A
scala> val k = MyCase[A1](A1(0)).asInstanceOf[MyCase[A2]]
k: MyCase[A2] = MyCase(A1(0))
scala> val a: A2 = k.a
java.lang.ClassCastException: A1 cannot be cast to A2
... 33 elided
But in your example - you never really used P in runtime (and it's impossible), so you never gonna get ClassCastException. Actually, under these conditions (no real instances), you can do type-level copy like here, but it safer to use traits for that.

Type alias parameter bounds not enforced in all cases

TL;DR: It appears that type parameters of type aliases (e.g. type T[X<:Serializable]) do not enforce their constraints when referenced as variables, parameters and perhaps other cases. Case classes, however, do enforce the bounds correctly for their parameters.
Consider a type alias designed to represent a subset of generic type. For example, let us say I want a type for lists of Serializable things:
scala> type SerializableList[T <: Serializable] = List[T]
defined type alias SerializableList
Now say that I want a case class with a parameter of these things:
scala> case class NetworkDataCC(things: SerializableList[_])
<console>:9: error: type arguments [_$1] do not conform to type SerializableList's type parameter bounds [T <: Serializable]
case class NetworkDataCC(things: SerializableList[_])
Well, that doesn't work. Scala (annoyingly) does not carry the parameter bounds with the type, but it's easy to fix:
scala> case class NetworkDataCC(things: SerializableList[_ <: Serializable])
defined class NetworkDataCC
Alright. Looks good. Now, what if I want just a regular class with those things, but I again forget to explicitly declare the type bounds. I expect an error:
scala> class NetworkData(val things: SerializableList[_])
defined class NetworkData
Oh, wait. No error... huh.
So, now I can do this?
scala> new NetworkData(List(1))
res3: NetworkData = NetworkData#e344ad3
Well, that seems quite broken. The case class, works fine of course (because the restrictions were declared):
scala> NetworkDataCC(List(1))
<console>:11: error: type mismatch;
found : Int(1)
required: Serializable
NetworkDataCC(List(1))
In my project, I am making use of reflection to generate some metadata about my classes. The metadata for the non-case-class shows a lack of bounds on things:
scala> classOf[NetworkData].getDeclaredFields()(0).getGenericType
res0: java.lang.reflect.Type = scala.collection.immutable.List<?>
Whereas the case class is correct:
scala> classOf[NetworkDataCC].getDeclaredFields()(0).getGenericType
res1: java.lang.reflect.Type = scala.collection.immutable.List<? extends scala.Serializable>
I wasn't able to find any bugs in the scala compiler bug tracker for this. Am I misunderstanding how these bounds should be used?
Scala's underscore is not equivalent to SerializableList[X forSome {type X}] by default:
scala> def aa(a: SerializableList[_]) = a
aa: (a: SerializableList[_])List[Any]
scala> def aa(a: SerializableList[X forSome {type X}]) = a
<console>:11: error: type arguments [X forSome { type X }] do not conform to type SerializableList's type parameter bounds [T <: Serializable]
def aa(a: SerializableList[X forSome {type X}]) = a
^
scala> class NetworkDataCC(things: SerializableList[X forSome {type X}])
<console>:11: error: type arguments [X forSome { type X }] do not conform to typ
e SerializableList's type parameter bounds [T <: Serializable]
class NetworkDataCC(things: SerializableList[X forSome {type X}])
It is equivalent to
scala> def aa(a: SerializableList[X] forSome {type X} ) = a
aa: (a: SerializableList[_])List[Any]
So such "unbounded" behavior is fine. Check out this answer: https://stackoverflow.com/a/15204140/1809978
Case classes seem to have additional type restrictions (due to this bug, which affects unapply method, automatically generated for case class).
If you want to have "unbounded" existential type in case class, just specify higher-order type explicitly:
scala> case class NetworkDataCC[SerializableList[_]](things: SerializableList[_])
warning: there were 2 feature warning(s); re-run with -feature for details
defined class NetworkDataCC
scala> NetworkDataCC(List(1))
res5: NetworkDataCC[List] = NetworkDataCC(List(1))
Or even:
scala> type SLL = SerializableList[_]
defined type alias SLL
scala> case class NetworkDataDD(things: SLL)
defined class NetworkDataDD
So this is definitely a bug regarding that you can workaround it with semantically equivalent type alias (see SI-8997)

Can I use a view bound in a Scala value class?

While dealing with some Java code, I wanted to find a way to reduce a Raw Set to include its parameterized type.
I also wanted it to work for Scala sets as well, so I did the following
implicit class Harden[S <% mutable.Set[_]](val set: S) extends AnyVal {
def cast[T] = set.map(_.asInstanceOf[T])
}
That resulted in a compiler error that I didn't expect
Error:(27, 27) field definition is not allowed in value class
implicit class Harden[S <% mutable.Set[_]](val set: S) extends AnyVal {
I didn't find any mention of this type of restriction in the Scala View Bounds or Value Class documentation.
Why is this not allowed? I'm using Scala 2.10.3.
As you can see from this sbt console output:
scala> :type implicit class Harden[S <% mutable.Set[_]](val set: S)
[S]AnyRef {
val set: S
private[this] val set: S
implicit private[this] val evidence$1: S => scala.collection.mutable.Set[_]
def <init>(set: S)(implicit evidence$1: S => scala.collection.mutable.Set[_]): Harden[S]
}
... the actual constructor of Harden desugars behind the scenes to:
def <init>(set: S)(implicit evidence$1: S => scala.collection.mutable.Set[_]): Harden[S]
... (i.e. takes set in one argument list and implicit evidence$1 in another).
As described in value classes limitations here:
must have only a primary constructor with exactly one public, val parameter whose type is not a value class.
... whitch means that Harden violaties this limitation.
You can achieve something similar, howerver. Try to convert your view bound defined on class to implicit evidence on method instead.
Something like this:
scala> implicit class Harden[S](val set: S) extends AnyVal {
| def cast[T](implicit ev: S => scala.collection.mutable.Set[_]) = set.map(_.asInstanceOf[T])
| }
defined class Harden
This will compile:
scala> Set(1,2,3).cast[Any]
res17: scala.collection.mutable.Set[Any] = Set(1, 2, 3)
And this will fail, as expected:
scala> List(1,2,3).cast[Any]
<console>:24: error: No implicit view available from List[Int] => scala.collection.mutable.Set[_].
List(1,2,3).cast[Any]
^
It is not allowed because as structured now, value classes must have exactly one parameter, but
implicit class Foo[A <% B](val a: A)
desugars to
implicit class Foo[A,B](val a: A)(implicit evidence$1: A => B)
which no longer has just a single parameter.

Scala: Extract types from generic parameters

I have a class like this:
abstract class Foo[I, T, A <: Bar[I, T]](x: SomeClass[A]){
When I want to inherit class Foo, I've to specify types T and I, which could be extracted from type parameters of type A. (I.e. there are enough data to extract these types.) Does the Scala compiler allow to extract them somehow? I'd like to write something like:
abstract class Foo[A <: Bar[_, _]](x: SomeClass[A]){
type Bar[I, T] = A // <-- something like pattern matching
It is strange that I can write that, but the type Bar[I, T] = A line does not seem to declare anything. The line passes, but I can use neither type I nor type T.
Can I do something similar?
I know I could use abstract class Foo[I, T](x: SomeClass[A]){ and then define type A = Bar[I, T], but it loses some universality. Additionaly, this case means more (boilerplate) code for the code users, because they are likely to define a shortcut (i.e. type alias) for Bar[I, T].
I can rewrite the abstract class Foo to a trait and I probably will do so. But I am not sure if it could help.
When type parameters are going to be unhandy one should give abstract types a try:
scala> class Bar[I, T](i: I, t: T)
defined class Bar
scala> class SomeClass[A](a: A)
defined class SomeClass
scala> trait Trait { type I; type T; type A = Bar[I,T] }
defined trait Trait
scala> class Foo(x: SomeClass[Foo#A]) extends Trait { type I = String; type T = String }
defined class Foo
scala> new Foo(new SomeClass(new Bar("", ""))) // works
res0: Foo = Foo#7e1f613c
scala> new Foo(new SomeClass(new Bar("", 0))) // does not work
<console>:12: error: type mismatch;
found : Int(0)
required: String
new Foo(new SomeClass(new Bar("", 0)))
^

Understanding “inferred type arguments do not conform to type parameter bounds” errors in Scala

I fail to understand why I am getting an “inferred type arguments do not conform to type parameter bounds”.
First, I defined a trait called CS which may be implemented by several classes (e.g., CS01 and CS02):
trait CS[+T <: CS[T]] {
this: T =>
def add: T
def remove: T
}
class CS01 extends CS[CS01] {
def add: CS01 = new CS01
def remove: CS01 = new CS01
}
class CS02 extends CS[CS02] {
def add: CS02 = new CS02
def remove: CS02 = new CS02
}
The idea is to keep the implemented type when calling add or remove on CS01 and CS02.
Secondly, I would like to define operations that may be executed on every classes compliant with trait CS. Then, I defined a trait called Exec(with two very simple examples of classes Exec01 and Exec02 mixin the Exec traits):
trait Exec {
def exec[U <: CS[U]](x: U): U
}
class Exec01 extends Exec {
def exec[U <: CS[U]](x: U): U = x.add
}
class Exec02 extends Exec {
def exec[U <: CS[U]](x: U): U = x.remove
}
Once again, I need to keep the implemented type of the class that mixes the CS trait. That is why exec is parametrized with [U <: CS[U]].
Finally, I want any CS enabling operations on it to mixin the trait Executable which makes it possible to execute an operation that follows trait Exec:
trait Executable[T <: CS[T]] {
this: T =>
def execute(e: Exec): T = e.exec(this)
}
However, I get the following error when I am trying to compile:
error: inferred type arguments [this.Executable[T] with T] do not conform to method exec's type parameter bounds [U <: this.CS[U]]
def execute(e: Exec): T = e.exec(this)
^
I don't quite understand because any classes that mix Executable must be of type T with the constraint of mixin the CS trait due to the bound in trait Executable[T <: CS[T]]. So, why this does not conform to the type parameter bound U <: CS[U] ?
Works if you specify the type parameter to exec explicitly:
def execute(e: Exec): T = e.exec[T](this)
Seems to be a limitation in the type inference.
Disclaimer: not a scala guru here, I'm learning it as I'm writing this.
First, let's simplify the example.
scala> trait Moo[+X <: Moo[X]]
defined trait Moo
scala> class Foo extends Moo[Foo]
defined class Foo
scala> def foobar[U <: Moo[U]](x: U) = x
foobar: [U <: Moo[U]](x: U)U
scala> foobar(new Foo)
res0: Foo = Foo#191275b
scala> class Bar extends Foo
defined class Bar
scala> foobar(new Bar)
<console>:12: error: inferred type arguments [Bar] do not conform to method
foobar's type parameter bounds [U <: Moo[U]]
foobar(new Bar)
^
scala>
foobar accepts a Foo argument but rejects a Bar which only extends Foo. Why? foobar is a generic, paramaterized by the type of its argument. It imposes a bound on that type. The type inferencer will not check each and every ancestor of the argument type, hoping to find one that satisfies the bound.
So how to impose a bound on an ancestor type? One method is with existential types.
scala> def foobar[V <: Moo[U] forSome {type U}](x: V) = x
foobar: [U <: Moo[_], V <: U](x: V)V
scala> foobar(new Foo)
res3: Foo = Foo#1154718
scala> foobar(new Bar)
res4: Bar = Bar#5a7ff7
scala>