investigation of `type` and `#` keywords in scala - scala

Could someone explain how the type keyword and # operator works in scala and how to use it?
Please look at examples.
//Example1
scala> type t1 = Option.type
defined type alias t1
//Shouldn't this work since previous example simply works?
scala> type t2 = String.type
<console>:7: error: type mismatch;
found : String.type
required: AnyRef
type t2 = String.type
^
//lets define custom trait T
scala> trait T
defined trait T
//... and obtain it's type like in Example1.
//Shouldn't this work since previous Example1 simply works?
scala> type t3 = T.type
<console>:7: error: not found: value T
type t3 = T.type
^
//Lets define some value of type T
scala> val v4 = new T{}
v4: T = $anon$1#5c3e8c76
//and obtain it's type (works)
scala> type t4 = v4.type
defined type alias t4
//this doesn't work
scala> type t4_1 = (new T{}).type
<console>:1: error: identifier expected but 'new' found.
type t4_1 = (new T{}).type
//as well as this (doesn't work)
scala> type t5 = "abc".type
<console>:1: error: identifier expected but string literal found.
type t5 = "abc".type
^
//but this compiles well
scala> val v6 = "abc"
v6: String = abc
scala> type t6 = v6.type
defined type alias t6
//lets create some values of created types:
scala> type t1 = Option.type
defined type alias t1
scala> val v1_1 = Some(10)
v1_1: Some[Int] = Some(10)
scala> type t7 = v1_1.type
defined type alias t7
scala> val v7:t7 = null
v7: t7 = null
scala> val v7_1:t7 = v1_1
v7_1: t7 = Some(10)
scala> val v7_2:t7 = Some(10)
<console>:9: error: type mismatch;
found : Some[Int]
required: t7
(which expands to) v1_1.type
val v7_2:t7 = Some(10)
^
//next let's try # operator
scala> class X[A,B](a:A,b:B)
defined class X
//doesn't work
scala> type xa = X[A,B]#A
<console>:8: error: not found: type A
type xa = X[A,B]#A
^
<console>:8: error: not found: type B
type xa = X[A,B]#A
^
//but such approach works:
scala> trait X2[C]{
type A
type B
val c:C
}
defined trait X2
scala> type xa2_1 = X2[String]#A
defined type alias xa2_1
scala> type xa2_2[M] = X2[M]#A
defined type alias xa2_2

First, your questions about type:
The right hand side of a type declaration has to be the name of a type with a stable path. So taking your examples one by one:
type t1 = Option.type
t1 is an alias for the type of the Option object, not the Option class.
type t2 = String.type
This is an error because there is no String object. The error's a little weird because String's a Java class and so operates under different rules (since Java classes never have companions).
type t3 = T.type
ditto. This time the error's clearer, because T is a Scala class and so the compiler can unambiguously say "T does not name an object with a type"
type t4 = v4.type
This is the singleton type of the object named by the val v4. It doesn't refer to any instance of type T, or even any instance of the anonymous class created by your new T{} expression. It refers to a type that is only represented by v4 and null, i.e. they are the only allowed values of that type.
type t4_1 = (new T{}).type
This is illegal because the thing you're taking the type of has to be a stable identifier (roughly, an identifier whose referant can never change -- if the full path to the idenfier consists of only the names of packages, objects, and vals, it's stable).
type t5 = "abc".type
Ditto.
type t6 = v6.type
v6 is a stable identifier. t6 is the type inhabited solely by that particular instance of String which is referred to by the name v6 (and null).
type v6 = v1_1.type
Again, a singleton type.
val v7: t7 = null
null is a valid value of type t7
val v7_1:t7 = v1_1
So is this particular object.
val v7_2:t7 = Some(10)
But this is a different object (even though it's == to v7, it's not eq to it) and therefore is not a member of this type.
Now about #:
class X[A,B](a:A,b:B)
A and B are type parameters. They can't be referred to outside the class. You can think of them like abstract type aliases with private[this] visibility, though that's not quite accurate.
type xa = X[A,B]#A
So yeah, not visible.
type xa2_1 = X2[String]#A
Since this A refers to a public type alias, it can be referred to by name outside the class. Note that this particular case is pretty useless, because you know absolutely nothing about this type. if your trait X2 had a method that returned values of type A, you could do something like
val aFromX2: xa2_1 = x2instance.methodThatReturnsAnA
..but then you couldn't do anything else with it, even pass it back to an instance of X2[String] because there's no guarantee that that the two As would refer to the same type! On the other hand, if you have a concrete instance, you could do this:
def passAroundA(x2instance: X2[String]) {
type x2a = x2instance.A // note dot, not #
val a: x2a = x2instance.methodThatReturnsAnA
x2instance.methodThatTakesAnA(a)
}
In this case it works because even though we have no idea what A actually is, we know that the two methods use the same type -- whatever was fixed at x2instance's construction.

Practically everything regarding your question is explained in this imho absolutely essential talk on use cases like pattern matching etc. I suggest you to take the time to watch it and the effort to thing about it. A lot of scala Type system related stuff was actually a magic to me until I watched this video. I might spare a lot of my man days trying to resolve weird behavior of the type system and type inference if I was aware of these things.

Related

Local assignment affects type?

In the following example, f3 can take a Iterable[Array[Int]]
def f3(o:Iterable[Iterable[Any]]):Unit = {}
f3(Iterable(Array(123))) // OK. Takes Iterable[Array[Int]]
but if I assign the Iterable[Array[Int]] to a local variable, it cannot:
val v3 = Iterable(Array(123))
f3(v3) // Fails to take Takes Iterable[Array[Int]]
with the error:
Error:(918, 10) type mismatch;
found : Iterable[Array[Int]]
required: Iterable[Iterable[Any]]
f3(x)
What the fudge? Why does the first example work, but not the seconds. It seems to have something to do with nested generics:
def f(o:Iterable[Any]):Unit = {}
f( Array(123))
val v1 = Array(123)
f(v1) // OK
def f2(o:Iterable[Any]):Unit = {}
f2( Iterable(Array(123)))
val v2 = Array(123)
f(v2) // OK
With scala.2.11
First of all, it's important that Array doesn't extend Iterable (because it's a Java type). Instead there is an implicit conversion from Array[A] to Iterable[A], so expected types matter.
In the first case: Iterable(Array(123)) is an argument to f3 and thus is typechecked with expected type Iterable[Iterable[Any]]. So Array(123) is typechecked with expected type Iterable[Any]. Well, its actual type is Array[Int] and the compiler inserts the conversion (because Iterable[Int] conforms to Iterable[Any]). So this is actually Iterable(array2iterable(Array(123)) (I don't remember the exact name).
In the second case f3 has the type Iterable[Array[Int]]: there is nothing to trigger the implicit conversion in the val f3 = ... line, right? And there is no implicit conversion from Iterable[Array[Int]] to Iterable[Iterable[Int]] (or, more generally from Iterable[A] to Iterable[B] when there is an implicit conversion from A to B), so the next line fails to compile. You could write this conversion yourself, but it wouldn't help e.g. to convert Array[Array[Int]] to Iterable[Iterable[Int]].
And of course, if you use Iterable[Any], there is again nothing to trigger the implicit conversion!
This has to do with the way type inference/unification works in Scala.
When you define a variable and leave out the type, Scala applies the most specific type possible:
scala> val v1 = Iterable(Array(123))
v1: Iterable[Array[Int]] = List(Array(123))
However, when you specify the expected type (eg by passing the value to a function with a defined parameter type) Scala unifies the given parameter with the expected type (if possible) :
scala> val v2 : Iterable[Iterable[Any]] = Iterable(Array(123))
v2: Iterable[Iterable[Any]] = List(WrappedArray(123))
Since Int is a subtype of Any, unification occurs and the code runs just fine.
If you want your function to accept anything that is a subtype of Any (without Scala's unification help) you will have to define this behavior explicitly.
Edit:
While what I am saying is partially true, see #AlexyRomanov's answer for a more correct assessment. It seems that the "unification" between Array and Iterable is really an implicit conversion being called when you pass Iterable(Array(123)) as a parameter (see the effect of this in my declaration of v2).
Suppose you have a bit of code where the compiler expects type B but finds type A instead. Before throwing an error, the compiler checks a collection of implicit conversion functions for one with the type A => B. If the compiler finds a satisfactory conversion, the conversion is applied automatically (and silently).
The reason f3 doesn't like v1 is because it is too late to call an implicit conversion on the inner Array[Int] and no existing implicit conversion exists for Iterable[Array[Int]] => Iterable[Iterable[Int]], though it would be trivial to implement, as I show below:
scala> implicit def ItAr2ItIt[T](ItAr: Iterable[Array[T]]): Iterable[Iterable[T]] = ItAr.map(_.toIterable)
ItAr2ItIt: [T](ItAr: Iterable[Array[T]])Iterable[Iterable[T]]
scala> def f3(o:Iterable[Iterable[Any]]):Unit = println("I like what I see!")
f3: (o: Iterable[Iterable[Any]])Unit
scala> val v3 = Iterable(Array(123))
v3: Iterable[Array[Int]] = List(Array(123))
scala> f3(v3)
I like what I see!

Scala - how to go resolve "Value is not a member of Nothing" error

This example code is based on Atmosphere classes, but if someone could give me some insights into what the error means in general, I think I can figure out any Atmosphere-specific solution...
val bc = BroadcasterFactory.getDefault().lookup(_broadcasterId)
bc.broadcast(message)
After the first line, bc should contain a handle to an object whose class definition includes the method broadcast() -- in fact, it contains several overloaded variations. However, the compiler chokes on the second line of code with the following: "value broadcast is not a member of Nothing"
Any ideas/suggestions on what would be causing this?
Thanks.
EDIT: signature for [BroadcasterFactor].lookup :
abstract Broadcaster lookup(Object id)
Note: 1) that is the signature version that I've used in the example, 2) it is the java Inteface signature - whereas the getDefault() hands back an instantiated object that implements that interface.
Solution: force type cast on value:
val bc: Broadcaster = BroadcasterFactory.getDefault().lookup(_broadcasterId)
Nothing is the type name. It's the subtype of all other types. You can't call methods from Nothing itself, you have to specify exact type ((bc: ExactType).broadcast(message)). Nothing has no instances. Method, that returns Nothing will, actually, never return value. It will throw an exception eventually.
Type inference
Definition of lookup:
abstract public <T extends Broadcaster> T lookup(Object id);
in scala this definition looks this way:
def lookup[T <: Broadcaster](Object id): T
There is not specified type parameter in lookup method. In this case compiler will infer this type parameter as the most specific type - Nothing:
scala> def test[T](i: Int): T = ???
test: [T](i: Int)T
scala> lazy val x = test(1)
x: Nothing = <lazy>
scala> lazy val x = test[String](1)
x: String = <lazy>
You could specify type parameter like this:
val bc = BroadcasterFactory.getDefault().lookup[Broadcaster](_broadcasterId)
Draft implementation
In development process lookup can be "implemented" like this:
def lookup(...) = ???
??? returns Nothing.
You should specify either result type of lookup method like this: def lookup(...): <TypeHere> = ... or type of bc: val bc: <TypeHere> =.

class A has one type parameter, but type B has one

Recently I stumbled across a strange (to me) compiler error message. Consider the following code:
trait Foo {
type Res <: Foo
type Bar[X <: Res]
}
class MyFoo extends Foo {
override type Res = MyFoo
override type Bar[X <: Res] = List[X]
}
type FOO[F <: Foo, R <: Foo, B[_ <: R]] = F { type Res = R;
type Bar[X <: R] = B[X] }
def process[F <: Foo, R <: Foo, B[_ <: R]](f: FOO[F, R, B]) {}
Now, if I want to call the process method I have to explicitly write the type parameters:
process[MyFoo, MyFoo, List](new MyFoo) // fine
If I write:
process(new MyFoo)
or
process((new MyFoo): FOO[MyFoo, MyFoo, List])
I get the following error message:
inferred kinds of the type arguments (MyFoo,MyFoo,List[X]) do not conform to the expected kinds of the type parameters (type F,type R,type B). List[X]'s type parameters do not match type B's expected parameters: class List has one type parameter, but type B has one
Why isn´t the compiler able to infer the types (although I explicitly stated them at call parameter)? And what does that class List has one type parameter, but type B has one mean? Something has one, but the other has also one, and that´s why they don´t fit together???
If we look to the Scala compiler, the sources could help us understanding what the problem is. I have never contributed to the Scala compiler, but I found the sources very readable and I have already investigated on that.
The class responsible for type inference is scala.tools.nsctypechecker.Infer which you can find simply by looking in the Scala compiler sources for a part of your error. You'll find out the following fragment:
/** error if arguments not within bounds. */
def checkBounds(pos: Position, pre: Type, owner: Symbol,
tparams: List[Symbol], targs: List[Type], prefix: String) = {
//#M validate variances & bounds of targs wrt variances & bounds of tparams
//#M TODO: better place to check this?
//#M TODO: errors for getters & setters are reported separately
val kindErrors = checkKindBounds(tparams, targs, pre, owner)
if(!kindErrors.isEmpty) {
error(pos,
prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") +
" do not conform to the expected kinds of the type parameters "+ tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." +
kindErrors.toList.mkString("\n", ", ", ""))
}
So now the point is understanding why checkKindBounds(tparams, targs, pre, owner) returns those errors. If you go down the method call chain, you will see that the checkKindBounds call another method
val errors = checkKindBounds0(tparams, targs, pre, owner, true)
You'll see the problem is connected to checking bounds of higher-kinded type, at line 5784, inside checkKindBoundsHK :
if (!sameLength(hkargs, hkparams)) {
if (arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded
else {error = true; (List((arg, param)), Nil, Nil) } // shortcut: always set error, whether explainTypesOrNot
}
The test is not passed, it appears that in my debugger:
hkargs$1 = {scala.collection.immutable.Nil$#2541}"List()"
arg$1 = {scala.tools.nsc.symtab.Symbols$ClassSymbol#2689}"class List"
param$1 = {scala.tools.nsc.symtab.Symbols$TypeSymbol#2557}"type B"
paramowner$1 = {scala.tools.nsc.symtab.Symbols$MethodSymbol#2692}"method process"
underHKParams$1 = {scala.collection.immutable.$colon$colon#2688}"List(type R)"
withHKArgs$1 = {scala.collection.immutable.Nil$#2541}"List()"
exceptionResult12 = null
hkparams$1 = {scala.collection.immutable.$colon$colon#2688}"List(type R)"
So it appears like there is one higher kinded param, type R, but there is no provided value for that.
If you actually go back to the to checkKindBounds, you see that after the snippet:
val (arityMismatches, varianceMismatches, stricterBounds) = (
// NOTE: *not* targ.typeSymbol, which normalizes
checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO)
)
the arityMismatches contains a tuple List, B. And now you can also see that the error message is wrong:
inferred kinds of the type arguments (MyFoo,MyFoo,List[X]) do not
conform to the expected kinds of the type parameters (type F,type
R,type B). List[X]'s type parameters do not match type B's expected
parameters: class List has one type parameter, but type B has ZERO
In fact if you put a breakpoint at line 5859 on the following call
checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO)
you can see that
tparam = {scala.tools.nsc.symtab.Symbols$TypeSymbol#2472}"type B"
targ = {scala.tools.nsc.symtab.Types$UniqueTypeRef#2473}"List[X]"
Conclusion:
For some reason, when dealing with complex higher-kinded types such as yours, Scala compiler inference is limited. I don't know where it does come from, maybe you want to send a bug to the compiler team
I only have a vague understanding of the exact workings of the type inferrer in Scala so consider this ideas not definitive answers.
Type inferring has problems with inferring more then one type at once.
You use an existential type in the definition of FOO, which translates to: there exists a type such, not sure if this is compatible with the specific type given in MyFoo

Creating an instance of a type alias causes "class type required" error

Having created a new type by mixing in a ObservableSet with a HashSet, I was kind of expecting to replace then be able to use the new type to create a new instance as in "foo" below. But this does not compile, although using the original long form of the type seems fine (as shown in "bar", below).
Is this just a feature of the language or have I done something daft?
package whatever
import collection.mutable._
object Whatever {
type ObservableHashSet[T] = HashSet[T] with ObservableSet[T]
class X
def foo {
new ObservableHashSet[X]
}
def bar {
new HashSet[X] with ObservableSet[X]
}
}
The error is ..
error: class type required but scala.collection.mutable.HashSet[scala.Whatever.X] with scala.collection.mutable.ObservableSet[scala.Whatever.X] found
new ObservableHashSet[X]
The brief version is that you've created a type alias for a structural type (that you can not instantiate).
This is a simplified version of what you've done (does not work):
scala> import collection.mutable._
import collection.mutable._
scala> type ObservableHashSet[T] = HashSet[T] with ObservableSet[T]
defined type alias ObservableHashSet
scala> new ObservableHashSet[String]
<console>:12: error: class type required but scala.collection.mutable.HashSet[String] with scala.collection.mutable.ObservableSet[String] found new ObservableHashSet[String]
Now, the error does make some sense, and let me try to explain why.
With type ObservableHashSet[T] = HashSet[T] with ObservableSet[T] you're defining a type alias for something that is not a concrete type (or, as the error message says, not a "class type"), so you can't create an instance of it with new.
But this (with an intermediate step where we do create a class type) works:
scala> class ObservableHashSet[T] extends HashSet[T] with ObservableSet[T]
defined class ObservableHashSet
scala> type obs[T] = ObservableHashSet[T]
defined type alias obs
scala> new obs[String]
res1: ObservableHashSet[String] = Set()
So, the question is: why does scala let you create a type alias that you can't instantiate?
Well, type ObservableHashSet[T] = HashSet[T] with ObservableSet[T] is a structural type. Although, as you've seen in the first snippet of code, you can't create an instance of it, you can still use it: e.g. to put a structural constraint on an argument of a function.
Take a look:
scala> type ObservableHashSet[T] = HashSet[T] with ObservableSet[T]
defined type alias ObservableHashSet
scala> def test(obsHashSet: ObservableHashSet[String]) : String = {"bingo!"}
test: (obsHashSet: ObservableHashSet[String])String
scala> test(new HashSet[String] with ObservableSet[String])
res4: String = bingo!
but if we try to call test with an argument that does not conform to the structural type we get a type mismatch:
scala> test(new HashSet[String])
<console>:13: error: type mismatch;
found : scala.collection.mutable.HashSet[String]
required: ObservableHashSet[String]

Scala prohibiting parameterization of a specific type

Is there a way to prohibit a parameterized type being parameterized by a specific type?
e.g. Suppose I want to create my own specialized List[T] type where I do not want List[Nothing] to be legal, i.e. cause a compile error.
I'm looking for a way to make the following error more easy to catch (yes, I understand this is not very functional or great Scala):
val x = ListBuffer()
x += 2
x has type ListBuffer[Nothing].
This sort of works,
class C[A](x: A*)(implicit ev: A =:= A) { }
There will be a type error if A = Nothing is inferred,
val c1 = new C[Int]() // Ok
val c2 = new C(1) // Ok, type `A = Int` inferred
val c3 = new C() // Type error, found (Nothing =:= Nothing) required: (A =:= A)
But it's still possible to explicitly set the type parameter A to Nothing,
val c4 = new C[Nothing]() // Ok
More generally, it's pretty tricky to ensure that two types are unequal in Scala. See previous questions here and here. One approach is to set up a situation where equal types would lead to ambiguous implicits.
You can define a type A >: Null if you specifically want to avoid Nothing, as Null is second from bottom and also covariant to all types (therefore its contravariance includes all types apart from Nothing).
Not sure how useful this is as its type bounds still includes Null.