scala lower type bounds example - scala

I have the following code:
class A[+X] {
def printY[Y >: X](y: Y) = println(y)
}
class F
class G extends F
val f = new F
val g = new G
val a = new A[F]
a.printY(g)
I expect a compilation error in a.printY(g) as g has type G which is not a supertype of F. But in class A I indicated that printY method only accepts supertype of A's type paramter which is F in my example.
Why it works correctly?

Because g is also an instance of F. Therefore, Y is inferred as F, which is a supertype of F. I.e.:
a.printY[F](g)
However, this wouldn't compile:
a.printY[G](g)

Note: if you want a.printY(g) not to compile you need to rewrite the method:
def printY[Y](y: Y)(implicit ev: X <:< Y) = println(y)
This way the compiler infers the type parameter to be G and then checks whether it's a supertype of F instead of looking for a supertype of F which also happens to be an acceptable type for g.

Just try to explain why it works from another two angles.
First, as you know the upper bound is reflexive, for Y >: X, type X or subtype of Y is acceptable. so when you define val a = new A[F], printY will be like:
def printY[Y >: F](y: Y) = println(y)
when you invoke a.printY(g), printY's type parameter will be inferred as G, which is also a type of F.
Second, for def printY[Y >: F](y: Y) = println(y), when you pass an instance I of type Y, compiler will try to find the common parent of I and F and make the resultant type as the type parameter of printY, so you can even pass value of String, Int to printY.
a.printY[Any]("xxx")
a.printY[Any](3)

Related

Why contravariant position in Builder is marked as invariant by compiler?

I want to understand why scala compiler emits error for the following snippet:
import scala.collection.mutable
class X[+A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
The error ouput:
<console>:13: error: covariant type A occurs in invariant position in type => scala.collection.mutable.Map[Int,scala.collection.mutable.Builder[A,C]] of value x
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
^
As I understand it, the variance rule is deduced outside-in like this:
x is at positive position.
type parameters of mutable.Map.empty is at neutral position.
Because of 2. and variance annotation of Builder[-Elem, +To], A is at negative position, and C is at positive position.
Therefore, A being covariant was misplaced in a negative (contravariant) position -Elem. Why does compiler say that it's an "invariant position"?
If I make A invariant, as in:
class X[A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
The error became:
On line 2: error: covariant type C occurs in invariant position in type scala.collection.mutable.Map[Int,scala.collection.mutable.Builder[A,C]] of value x
I thought C position should have been positive (covariant), but compiler says it's also invariant?
Because value type of Map is invariant. The value type may appear both at covariant position (like in the return value of get) and contravariant position (like in the right hand side of +=).
If the compiler allows you to proceed, there will be type safety problem.
class A0
class A1 extends A0
class A2 extends A0
class C0
class C1 extends C0
class C2 extends C0
class X[+A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]] // won't compile
}
val x1: X[A1, C1] = new X[A1, C1]
val x2: X[A0, C0] = x1 // works because X[+A, +C]
x2.x += (new A2, new SomeBuilder[A0, C2]) // breaks type system
// we placed A2 as key, while the original x1 only accepts A1
// similar conflict happens for the value position
The issue here is that you are trying to use the A, and C as genercis to val x.
The compiler does not know which type to use.
You can try the following:
trait A1
trait C1
class X[A <: A1, C <: C1] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
Which will ensure that A and C are from the type you want.

Scala Generics, which of these 4 generic version is specific to which particular problem

These four versions compile but i'm curious about context in which we should prefer one option rather than another.
// 1
def f[N, S <: Seq[N]](s: S)
// 2
def f[N, S[N] <: Seq[N]](s: S[N])
They are pretty similar when use 1 rather than 2
2 impose that S have N as generic parameter as 1 but then what is the difference between these two ?
Then we have more general settings.
// 3
def f[N, S[X] <: Seq[X]](s: S[N])
// 3.a
def f[N, S[X] <: Seq[X]](s: S[N]): S[Int]
From what i humbly understood 3 authorize to extract the generic container type to reuse it later and get something like 3.a.
But what is the meaning of the undeclared X generic parameter, i suppose it's a way to declare something special but i don't get it.
// 4
def f[N, S[X] <: Seq[_]](s: S[N])
I don't know what to say about 4 except than from what i know Seq[_] stands for Seq[Any]
Finally i'm simply curious to have more information about these tools and their specificity to get things done more properly.
// 2
def f[N, S[N] <: Seq[N]](s: S[N])
The idea here is that the first N parameter and N mentioned in S[N] <: Seq[N] are completely independent parameters. They are just sharing the same name.
N mentioned in S[N] is visible only in a scope of its bound <: Seq[N].
N used in parameter definition (s: S[N]) comes from first N as this is the only N parameter visible for parameter type definition. So instead of N in S[N] <: Seq[N] you can use any letter and this will not affect your parameter type at any way.
// 4
def f[N, S[X] <: Seq[_]](s: S[N])
Here you just ignored X parameter.
Edit: as #alexey-romanov mentioned in a comment. There is difference between S[X] <: Seq[X] and S[X] <: Seq[_]
Here is an example showing the difference:
def f1[N, S[X] <: Seq[X]](s: S[N]) = ""
def f2[N, S[X] <: Seq[_]](s: S[N]) = ""
type Foo[A] = Seq[Int]
val foo: Foo[String] = Seq(2,3)
//f1(foo) -- compilation error
f2(foo)
The problem here is as type constrictor is a kind of "function on types", we can define such "function" accepting one type as a parameter but returning type parametrized by another parameter not related to parameter used in type constructor. (See type Foo)
Passing foo val to f2 is fine because X is infered to String and Foo[String] is a "subtype"(actually they are equal) of Seq[Int], but when we pass foo to f1 the X is still a String but Foo[String] is not "subtype" of Seq[String] (because Foo[String]==Seq[Int] not subtype of Seq[String])
// 1
def f[N, S <: Seq[N]](s: S)
And here you said that N used in Seq[N] is the same as first parameter N. So this is the same N

How to use scala type lambda

With the following code,
val x: ({type X[Y] = Function1[Y, Unit]})#X = (y: Int) =>println(y)
It successfully compiles, but how could I use it? When I call it with x(1)
An compiling occurs that complains Type mismatch, Y expected: Y accutal: Int
You can't have a value of a type like ({type X[Y] = Function1[Y, Unit]})#X, just as you can't have a value of type Option or Function1. The only thing you can do with it is to apply it to a parameter, or to use it as a type argument for another type/method, which will eventually apply it to some parameters.

Why compound types with different order of components are equivalent in Scala?

According to the Scala Language Specification,
Two compound types are equivalent if the sequences of their component are pairwise equivalent, and occur in the same order, and their refinements are equivalent. Two refinements are equivalent if they bind the same names and the modifiers, types and bounds of every declared entity are equivalent in both refinements.
However, given
trait A { val a: Int }
trait B { val b: String }
I'm getting
scala> implicitly[A with B =:= B with A]
res0: =:=[A with B,B with A] = <function1>
i.e. they are considered equivalent, even though the order of components is different. Why?
I think the =:= evidence only asserts that each is the upper bound of the other.
trait A; trait B
import scala.reflect.runtime.{universe => ru}
val ab = ru.typeOf[A with B]
val ba = ru.typeOf[B with A]
ab =:= ba // false!
ab <:< ba // true!
ba <:< ab // true!
The implicit from Predef you get basically if LUB(X, Y) == X == Y, because then the implicit resolution finds =:=.tpEquals with the inferred upper bound.
This is most likely what you want, because it means that you can treat one type as the other, and that works because the members of A with B are equal to the members of B with A even if the trait linearisation in the implementations is different.

Scala: Option[T] as ?[T] (or even T?)

i tried
type ?[_] = Option[_]
def f(x: ?[Int]) = for (y <- x) yield y
(but i don't know what i am doing.)
insofar as types are just objects, i should be able to define a postix operator (i.e. zero arity method) for use in type signatures (i think). it might need a space like
def f(x: Int ?) = for (y <- x) yield y
scala makes it easy to use the Option type with matching and polymorphism, avoid null. but, most classes are (nullable) vars and java often returns vars. using classes and calling java are two of scala's selling points. an easy-to-write and easy-to-read syntax would support Options even more strongly.
what are all the things that scala does with "?" that make its parsing special.
ideally, one could write
def f(x: Int?) = for (y <- x) yield y
like other languages. can we do this in scala (without a macro)?
First, types are not objects. In fact, Scala has exactly two namespaces: values and types. They are very different things, and play by very different rules.
The postfix idea is kind of nice, actually, but it is not possible. There's an infix notation for types, though.
Now, to what you wrote:
type ?[_] = Option[_]
Each underscore has a different meaning. The underscore in ?[_] means ? is higher-kinded, but you don't care what it's type parameter is. The underscore in Option[_] means Option is an existential type. So when you write x: ?[Int], Scala will convert it to x: Option[t] { forSome type t }. This means that not only you don't get the Int, but the type parameter of Option is unknowable (you just know it exists).
However, it does compile:
scala> def f(x: ?[Int]) = for (y <- x) yield y
f: (x: ?[Int])Option[Any]
Which version of Scala did you use? 2.11? A co-worker of mine has already found some type inference regressions on 2.11, so that could be it.
The proper way to write the type alias would be this:
type ?[+A] = Option[A]
Not only we pass the type parameter along (it is a parameter, after all!), but we need to specify co-variance for it to act just Option (which is co-variant itself).
Now, as to your two questions, Scala has absolutely no special treatment of ?. And, no, you can't do this. This ? is not exactly widespread among languages either, and in all of them that support it, it is built in the language, and not something externally defined.
Besides, it's kind of a joke that, when interface with Java, typing out Option would be a problem -- not with the average identifier size in Java!
You intended to get an Option[Int] out:
scala> type ?[A] = Option[A]
defined type alias $qmark
scala> def f(x: ?[Int]) = for (y <- x) yield y + 1
f: (x: ?[Int])Option[Int]
and it does compile anyway.
You could maybe
scala> type ?[A,_] = Option[A]
defined type alias $qmark
scala> def f(x: Int ? _) = for (y <- x) yield y + 1
f: (x: ?[Int, _])Option[Int]
or similar.
scala> def f(x: Int ?_) = for (y <- x) yield y + 1
f: (x: ?[Int, _])Option[Int]
looks more postfixy.
P.S. Still curious whether variance annotation on type alias is required or merely advisable.
scala> type ?[A] = Option[A]
defined type alias $qmark
scala> trait X ; trait Y extends X ; trait Z extends X
defined trait X
defined trait Y
defined trait Z
scala> val x: ?[X] = null.asInstanceOf[?[Y]] // why is this OK?
x: ?[X] = null
scala> class C[A]
defined class C
scala> val c: C[X] = null.asInstanceOf[C[Y]] // like this is not OK
<console>:10: error: type mismatch;
found : C[Y]
required: C[X]
Note: Y <: X, but class C is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
val c: C[X] = null.asInstanceOf[C[Y]]
^
Maybe compare SI-8522 and related issues.
You might consider a renaming import. When you create a type alias you only rename a type. When you rename a symbol during import you include all referents of that name, both type and value.
To wit:
scala> import scala.{Option => ?}
import scala.{Option=>$qmark}
scala> val oi1: ?[Int] = Some(1)
oi1: Option[Int] = Some(1)
scala> def mi1(oi: ?[Int]): Int = oi.getOrElse(-1)
mi1: (oi: Option[Int])Int
scala> mi1(None)
res1: Int = -1
scala> mi1(?(1))
res2: Int = 1
Compare with this:
scala> type ?[A] = Option[A]
scala> def mi1(oi: ?[Int]): Int = oi.getOrElse(-1)
mi1: (oi: ?[Int])Int
scala> mi1(?(1))
<console>:10: error: not found: value ?
mi1(?(1))
^