I am trying to create a method which could take a type parameter of any descendant of immutable.Seq. This is what I got so far:
def writeSomeData[Holder[_] <: Seq[String]](path: String, holder: Holder[String]): Unit = {
// irrelevant implementation
}
However when I call the above method with an immutable.List:
writeSomeData(tmp, List(res1, res2, res3, res4, res5))
it breaks with the following error:
[error] /home/v.gorcinschi/repos/...: inferred type arguments [List] do not conform to method writeSomeData's type parameter bounds [Holder[_] <: Seq[String]]
[error] writeSomeData(tmp, List(res1, res2, res3, res4, res5))
[error] ^
[error] /home/v.gorcinschi/repos/...: type mismatch;
[error] found : List[String]
[error] required: Holder[String]
[error] writeSomeData(tmp, List(res1, res2, res3, res4, res5))
Why is this happening and how must I correct it? List is a descendant of a Seq isn't it?
The problem is that Holder[_] <: Seq[String] doesn't mean what you think it does. I am actually not sure what does it mean, probably something like: trait Holder[A] extends Seq[String] which as you can see is very different from how List would look like.
You actually want to express that Holder[String] should be a subtype of Seq[String] many people think the right syntax for that is Holder[_] <: Seq[_] However, that is also incorrect; that just means that Holder must extend Seq but doesn't guarantee that Holder[String] <: Seq[String]
Thankfully, Scala does provides syntax to represent that: Holder[x] <: Seq[x] that x is not another type parameter, is just a way to say that Holder[x] is a subtype of Seq[x] for any type x
Sometimes, you it is also easier to use a generalized type constraint like:
def foo[Holder[_]](holder: Holder[String])(implicit ev: Holder[String] <:< Seq[String]): Unit
To represent that exact same relationship.
Nevertheless, that is only useful if you reference Holder in the return type, if you only want to consume any Seq then you can just do:
def foo(holder: Seq[String]): Unit
Thanks to Liskov, you can pass any subtype of Seq[String] there.
The type constraint
Holder[_] <: Seq[String]
means method takes any type constructor Holder such that Holder[X] is a subtype of Seq[String] for arbitrary X. By arbitrary we mean X >: Nothing <: Any.
Now when you pass List("") as argument then Scala will try to unify
?Holder := List
such that Holder[X] <: Seq[String] for arbitrary X. However List[X] is not a subtype of Seq[String] for arbitrary X, for example take X := Int. Hence it does not type check.
As a workaround we can do
Holder[x <: String] <: Seq[x]
This effectively achieves the same thing but this time it works because now
Holder[x] <: Seq[x]
does hold true for arbitrary x because both List and Seq are similarly defined
trait Seq[+A]
trait List[+A]
and it is true that [X] =>> List[X] <: [X] =>> Seq[X] for arbitrary x.
Plus compiler has extra information x <: String from Holder[x <: String] to check the element type of the argument.
Given
Holder[_] <: Seq[String]
You could make it work by helping out the compiler with explicitly passing appropriate type lambda something like so
writeSomeData[[X] =>> List[String]](???, List(""))
but Scala does not infer such type lambdas automatically I think because of in general type constructor unification being undecidable.
Related
I am starting to embrace abstract type members over type parameters - mainly because they seem to work better with type inference for me. However, I am still struggling to understand how to use them from outside of the types they are defined in. For example, I cannot understand why the following Scala program should not compile:
trait Thing
trait Describer {
type X<:Thing
def describe(x:X) = println(x)
}
object Program extends App {
def print[T <: Thing, D <: Describer]
(describer: D, thing:T)
(implicit ev: D#X =:= T)
= describer.describe(thing)
}
Intuitively, I would expect that the requirement D#X =:= T would guarantee that the two types are indeed equal and hence that instances of two could be used interchangeably, but I get this compilation error:
error: type mismatch;
found : thing.type (with underlying type T)
required: describer.X
= describer.describe(thing)
What have I misunderstood? Is there another way to do what I want to do? Or failing that, is it safe to cast thing to the required type (describer.X)?
The type of the parameter of describer.describe is actually describer.X and not D#X. Another thing you have to know is that A =:= B also functions as a conversion from A to B but (at least currently) not the other way around. So the following should work.
def print[T <: Thing]
(describer: Describer, thing: T)
(implicit ev: T =:= describer.X)
= describer.describe(thing)
(Scala 2.11.8)
Consider the following code:
trait Class[A] {
def f1[B >: A](arg1: Int)(ord: Ordering[B]): Int
def f2[B >: A](arg1: Int, ord: Ordering[B]): Int
def f[B >: A](ord: Ordering[B]): Int = {
f1(123)(ord) // Compilation error!
f2(123, ord) // OK
}
}
Here, line f1(123)(ord) raises type mismatch; found : Ordering[B] required: Ordering[A] Note: B >: A, but trait Ordering is invariant in type T. You may wish to investigate a wildcard type such as _ >: A. (SLS 3.2.10)
If we change the call to f1[B](123)(ord), error disappears.
Why does the presence of multiple arguments list confuses the typechecker? Is this a bug, or an expected result?
It's not a bug - the separation into parameter lists means that the type parameter is inferred based on the first argument list alone:
f1(123)(ord)
can be rewritten as:
val partiallyApplied = f1(123)
partiallyApplied(ord)
Now - what is partiallyApplied's type? Since the type parameter wasn't explicitly set, and there's no argument / return type to use for inference, the type parametter is inferred to be A (there's no specific B yet! So partiallyApplied's type is (Ordering[A]) => Int), hence using it with Ordering[B] later gives the exception.
In contrast, when calling:
f2(123, ord)
Since ord has type Ordering[B], the type parameter can be inferred to be B, hence compilation succeeds.
Given:
class Invar[T]
trait ExtendsAnyref extends AnyRef
def f(a: Invar[ExtendsAnyref]) = {}
The following is erroneous
scala> val x: Function1[Invar[_ <: AnyRef], Unit] = f
<console>:13: error: type mismatch;
found : Invar[ExtendsAnyref] => Unit
required: Invar[_ <: AnyRef] => Unit
val x: Function1[Invar[_ <: AnyRef], Unit] = f
^
Why?
I understand that in Scala, generic types have by
default nonvariant subtyping. Thus, in the context of this example, instances of Invar with different type parameters would never be in a subtype relationship with each other. So an Invar[ExtendsAnyref] would not be usable as a Invar[AnyRef].
But I am confused about the meaning of _ <: AnyRef which I understood to mean "some type below AnyRef in the type hierarchy." ExtendsAnyref is some type below AnyRef in the type hierarchy, so I would expect Invar[ExtendsAnyref] to conform to Invar[_ <: AnyRef].
I understand that function objects are contravariant in their input-parameter types, but since I use Invar[_ <: AnyRef] rather than Invar[AnyRef] I understood, apparently incorrectly, the use of the upper bounds would have the meaning "Invar parameterized with Anyref or any extension thereof."
What am I missing?
When you write
val x: Function1[Invar[_ <: AnyRef], Unit] = ...
it means x must accept any Invar[_ <: AnyRef]. That is, it must accept Invar[AnyRef], Invar[String], etc. f obviously doesn't: it only accepts Invar[ExtendsAnyref].
In other words, you need to combine your last two paragraphs: because functions are contravariant in argument types, for Function1[Invar[ExtendsAnyref], Unit] to conform to Function1[Invar[_ <: AnyRef], Unit] you'd need Invar[_ <: AnyRef] to conform to Invar[ExtendsAnyref], not vice versa.
If you
want a function that takes Invar parameterized with any subclass of AnyRef
this can be written as Function1[Invar[A], Unit] forSome { type A <: AnyRef }. However, I don't believe there is anything useful you could do with an object of this type, because 1) the only thing you can do with a function is to apply it to an argument, but 2) you don't know what arguments this function accepts.
In Scala, an existential type has the following two forms:
// placeholder syntax
List[_]
// forSome
List[T forSome {type T}]
However, seems that the second form can not appear in the method type parameter position(at least in the way like I write below).
// placeholder syntax is Okay
scala> def foo[List[_]](x: List[_]) = x
foo: [List[_]](x: List[_])List[_]
scala> def foo[List[t forSome {type t}]](x: List[_]) = x
<console>:1: error: ']' expected but 'forSome' found.
def foo[List[T forSome {type T}]](x: List[_]) = x
^
// being as upper bound is also Okay
scala> def foo[A <: List[T forSome { type T }]](x: A) = x
foo: [A <: List[T forSome { type T }]](x: A)A
// type alias is a possible way but that is not what I want
scala> type BB = List[T forSome {type T}]
defined type alias BB
scala> def foo[BB](x: List[_]) = x
foo: [BB](x: List[_])List[Any]
I have tried for a while but been unable to find the right way to get the second compiled successfully.
So is it just some restrictions about method type parameter, or am i missing something here.
The confusion is that the underscore (_) in foo does not denote an existential type.
Let's see what the following actually means:
def foo[List[_]](x: List[_]) = x
List here is a higher kinded type parameter (and by the way does not refer to scala's built-in List type -- aka scala.collection.immutable). This type parameter itself has a single type parameter, denoted by the underscore (_).
Now that it's clear that List[_] is not an existential here, it follows that forSome has no business going there.
However, you can use forSome in the type of x. The following is equivalent to your original definition of foo:
def foo[List[_]](x: List[T] forSome { type T }) = x
Then again, this is probably still not what you'd want, seeing as List is still a type parameter and not scala.collection.immutable. What you'd probably want is:
def foo(x: List[T] forSome { type T }) = x
which is the same as:
def foo(x: List[_]) = x
Hinted by Régis Jean-Gilles's "List here is a higher-kinded type parameter (and by the way does not refer to Scala's built-in List type -- aka scala.collection.immutable)", I had a re-check on the definition of existential type and finally figured out the problem in my case, just write it here as complement for Régis Jean-Gilles's answer.
Existential types are a means of constructing types where portions of the type signature are existential, where existential means that although some real type meets that portion of a type signature, we don’t care about the specific type. Existential types were introduced into Scala as a means to interoperate with Java’s generic types, such as Iterator<?> or Iterator<? extends Component>(Quote from <<Scala In Depth>>).
Real type could be type from the library(like the scala.collection.immutable.List), or the self-defined type like type alias BB in my case. Anyway, Scala compiler has to guarantee that part of the existential type is also known.
The problem in my case is that
// List is type parameter which accepts another type as parameter
def foo[List[_]](x: List[_]) = x
// List may be misleading here, just change it to another name
def foo[TT[_]](x: TT[_]) = x
Scala compiler only knows that TT is higher-kinded type parameter, apparently no real type exists here. So in this case, TT[_] is not a existential type therefore forSome form can not be used in the type parameter position.
And the following cases are valid.
// we define a higher-kined type HKT, so HKT is a real type
// when Scala compiler checks the method parameter part
// HKT can be defined as existential type
def foo[HKT[_]](x: HKT[T forSome { type T }]) = x
def foo[HKT[_]](x: HKT[T] forSome { type T }) = x
Lets assume I have instance of arbitrary one-argument generic class (I'll use List in demonstration but this can me any other generic).
I'd like to write generic function that can take instances (c) and be able to understand what generic class (A) and what type argument (B) produced the class (C) of that instance.
I've come up with something like this (body of the function is not really relevant but demonstrates that C conforms to A[B]):
def foo[C <: A[B], A[_], B](c: C) {
val x: A[B] = c
}
... and it compiles if you invoke it like this:
foo[List[Int], List, Int](List.empty[Int])
... but compilation fails with error if I omit explicit type arguments and rely on inference:
foo(List.empty[Int])
The error I get is:
Error:Error:line (125)inferred kinds of the type arguments (List[Int],List[Int],Nothing) do not conform to the expected kinds of the type parameters (type C,type A,type B).
List[Int]'s type parameters do not match type A's expected parameters:
class List has one type parameter, but type A has one
foo(List.empty[Int])
^
Error:Error:line (125)type mismatch;
found : List[Int]
required: C
foo(List.empty[Int])
^
As you can see Scala's type inference cannot infer the types correctly in this case (seems like it's guess is List[Int] instead of List for 2nd argument and Nothing instead of Int for 3rd).
I assume that type bounds for foo I've come up with are not precise/correct enough, so my question is how could I implement it, so Scala could infer arguments?
Note: if it helps, the assumption that all potential generics (As) inherit/conform some common ancestor can be made. For example, that A can be any collection inherited from Seq.
Note: the example described in this question is synthetic and is a distilled part of the bigger problem I am trying to solve.
This is a known limitation of current Scala's type inference for type constructors. Defining the type of formal parameter c as C only collects type constraints for C (and indirectly to A) but not B. In other words List[Int] <: C => { List[Int] <: C <: Any, C <: A[_] <: Any }.
There is a pretty simple translation that allows to guide type inference for such cases. In your case it is:
def foo[C[_] <: A[_], A[_], B](c: A[B]) { val x: A[B] = c }
Same semantics, just slightly different type signature.
In addition to hubertp answer, you can fix you function by removing obsolete (in you example) type variable C, e.g:
def foo[A[_], B](c: A[B]) {
val x: A[B] = c
}
In this case scalac would infer A[_] as List and B as Int.
Update (according to the comment).
If you need an evidence that C is subtype of A[B], then use implicit:
def foo[A[_], B, C](c: C)(implicit ev: C <:< A[B]) = {
val x: A[B] = c
}
Then it won't compile this:
scala> foo[List, String, List[Int]](List.empty[Int])
<console>:9: error: Cannot prove that List[Int] <:< List[String].
foo[List, String, List[Int]](List.empty[Int])