Let's say we have a function:
def bar[F[_], A](x: F[A], y: F[A]) = null
All the following behavior are clear:
bar(List(1, 2, 3), List(1)) // compiles ok
bar(List(1), Some(1)) // doesn't compile
But,
bar(1, List(1)) // compiles ok
bar(1, 1) // compiles ok
Why?
P.S. example from FSiS Part 1 - Type Constructors, Functors, and Kind Projector
I think the following gives a clue (although there is still some mystery involved):
def baz[F[_], A](x: F[A]): F[A] = x
scala> baz("str")
res5: Comparable[String] = str
scala> baz(1)
res6: Any = 1
scala> class Foo
defined class Foo
scala> baz(new Foo)
<console>:15: error: no type parameters for method baz: (x: F[A])F[A] exist so that it can be applied to arguments (Foo)
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Foo
required: ?F
baz(new Foo)
^
<console>:15: error: type mismatch;
found : Foo
required: F[A]
baz(new Foo)
^
scala> case class Foo2
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
defined class Foo2
scala> baz(Foo2)
res10: scala.runtime.AbstractFunction0[Foo2] = Foo2
So the functions bar
and baz
are finding any container type they can (Comparable
for String, AbstractFunction0
for a case class, etc) to match the expected F[_]
.
Speculation: in the case of Int
, I suspect we are hitting a special "container" type for what are (boxed) primitive types in the underlying bytecode. If this special type can only be printed back to Scala as "Any
", but is really some special type we can think of as "Any[_]
", then that could explain the results we see. As corroboration that this is to do with the special handling of primitives, note that it fails for non-primitive simple types, like the (non-case) class Foo
above.
I think you're running into a limitation of the type inference system. To shed some light on this, let's look at what happens when we redefine this a bit to get a more useful output:
class Bar[F[_], A](x: F[A], y: F[A]) {}
res0: Bar[List,Int] = Bar@69f1a286
new Bar(List(1,2,3), List(1))
res1: Bar[Any,Int] = Bar@7b139eab
new Bar(List(1), 1)
res2: Bar[Any,Int] = Bar@456be73c
new Bar(List(1), Some(1))
<console>:12: error: inferred kinds of the type arguments (Product with java.io.Serializable,Int) do not conform to the expected kinds of the type parameters (type F,type A) in class Bar.
Product with java.io.Serializable's type parameters do not match type F's expected parameters:
<refinement of Product with java.io.Serializable> has no type parameters, but type F has one
new Bar(List(1), Some(1))
^
<console>:12: error: type mismatch;
found : List[Int]
required: F[A]
new Bar(List(1), Some(1))
^
<console>:12: error: type mismatch;
found : Some[Int]
required: F[A]
new Bar(List(1), Some(1))
In the first example, we have a Bar[List, Int]
, which makes perfect sense, we passed in two List[Int]
.
In the second and third, we have a Bar[Any, Int]
. Here's where it gets weird. Keep in mind that Any
is the parent of both AnyVal
(the parent of Scala's equivalents of Java's primitives) and AnyRef
(the Scala equivalent of Java's Object) (see Scala Documentation for further explanation).
Scala's type inference has decided that this Bar
's constructor should accept Any
for F
, and Int
for A
. As Any
is indeed a parent of List
and Int
, that's fine. The List
is indeed parameterized as [Int]
, so that's fine. What's weird is that Scala is okay with saying that the Int
is also of type Any[Int]
. I don't have a good explanation for that part.
With the last one, that's where I'm honestly confused, and I have to wonder if this is a bug. For some reason, even though both List
and Some
are children of Any
, and both are parameterized with Int
, it doesn't allow it. I'm afraid I'm not well versed in the intricacies of the compiler's inference methods, but for what it's worth, explicitly specify those parameters works:
new Bar[Any,Int](List(1), Some(1))
res14: Bar[Any,Int] = Bar@36238b12
To me, that suggests the type inference system just can't properly infer the types, or it's inferring types that aren't correct.
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