Local assignment affects type? - scala

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!

Related

Scala cast to generic type

I'm confused about the generic type. I expect that 2.asInstanceOf[A] is cast to the type A, meanwhile, it's cast to Int.
Besides that, the input is java.lang.Long whereas the output is a list of Int (according to the definition the input and the output should be the same type). Why is that?
def whatever[A](x: A): List[A] = {
val two = 2.asInstanceOf[A]
val l = List(1.asInstanceOf[A],2.asInstanceOf[A])
println(f"Input type inside the function for 15L: ${x.getClass}")
println(f"The class of two: ${two.getClass}, the value of two: $two")
println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
l
}
println(f"Returned from whatever function: ${whatever(15L)}")
the outupt:
Input type inside the function for 15L: class java.lang.Long
The class of two: class java.lang.Integer, the value of two: 2
The class of the first element of l: class java.lang.Integer, first element value: 1
Returned from whatever function: List(1, 2)
a.asInstanceOf[B] means:
Dear compiler;
Please forget what you think the type of a is. I know better. I know that if a isn't actually type B then my program could blow up, but I'm really very smart and that's not going to happen.
Sincerely yours, Super Programmer
In other words val b:B = a.asInstanceOf[B] won't create a new variable of type B, it will create a new variable that will be treated as if it were type B. If the actual underlying type of a is compatible with type B then everything is fine. If a's real type is incompatible with B then things blow up.
Type erasure. For the purposes of type checking 2 is cast to A; but at a later compilation stage A is erased to Object, so your code becomes equivalent to
def whatever(x: Object): List[Object] = {
val two = 2.asInstanceOf[Object]
val l = List(1.asInstanceOf[Object],2.asInstanceOf[Object])
println(f"Input type inside the function for 15L: ${x.getClass}")
println(f"The class of two: ${two.getClass}, the value of two: $two")
println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
l
}
2.asInstanceOf[Object] is a boxing operation returning a java.lang.Integer.
If you try to actually use the return value as a List[Long] you'll eventually get a ClassCastException, e.g.
val list = whatever(15L)
val x = list(0)
x will be inferred to be Long and a cast inserted to unbox the expected java.lang.Long.
The answer from #jwvh is on point. Here I'll only add a solution in case you want to fix the problem of safely converting an Int to an A in whatever, without knowing what A is. This is of course only possible if you provide a way to build a particular A from an Int. We can do this in using a type-class:
trait BuildableFromInt[+A] {
def fromInt(i: Int): A
}
Now you only have to implicitly provide BuildableFromInt for any type A you wish to use in whatever:
object BuildableFromInt {
implicit val longFromInt: BuildableFromInt[Long] = Long.box(_)
}
and now define whatever to only accept compliant types A:
def whatever[A : BuildableFromInt](x: A): List[A] = {
val two = implicitly[BuildableFromInt[A]].fromInt(2)
// Use two like any other "A"
// ...
}
Now whatever can be used with any type for which a BuildableFromInt is available.

Strange implicit def with function parameter behaviour in Scala

I've written a simple code in Scala with implicit conversion of Function1 to some case class.
object MyApp extends App{
case class FunctionContainer(val function:AnyRef)
implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)
def someFunction(i:Int):String = "someString"
def abc(f : FunctionContainer):String = "abc"
println(abc(someFunction))
}
But it doesn't work. Compiler doesn't want to pass someFunction as an argument to abc. I can guess its reasons but don't know exactly why it doesn't work.
When you use a method name as you have, the compiler has to pick how to convert the method type to a value. If the expected type is a function, then it eta-expands; otherwise it supplies empty parens to invoke the method. That is described here in the spec.
But it wasn't always that way. Ten years ago, you would have got your function value just by using the method name.
The new online spec omits the "Change Log" appendix, so for the record, here is the moment when someone got frustrated with parens and introduced the current rules. (See Scala Reference 2.9, page 181.)
This has not eliminated all irksome anomalies.
Conversions
The rules for implicit conversions of methods to functions (§6.26) have been tightened. Previously, a parameterized method used as a value was always implicitly converted to a function. This could lead to unexpected results when method arguments were forgotten. Consider for instance the statement below:
show(x.toString)
where show is defined as follows:
def show(x: String) = Console.println(x)
Most likely, the programmer forgot to supply an empty argument list () to toString. The previous Scala version would treat this code as a partially applied method, and expand it to:
show(() => x.toString())
As a result, the address of a closure would be printed instead of the value of s. Scala version 2.0 will apply a conversion from partially applied method to function value only if the expected type of the expression is indeed a function type. For instance, the conversion would not be applied in the code above because the expected type of show’s parameter is String, not a function type. The new convention disallows some previously legal code. Example:
def sum(f: int => double)(a: int, b: int): double =
if (a > b) 0 else f(a) + sum(f)(a + 1, b)
val sumInts = sum(x => x) // error: missing arguments
The partial application of sum in the last line of the code above will not be converted to a function type. Instead, the compiler will produce an error message which states that arguments for method sum are missing. The problem can be fixed by providing an expected type for the partial application, for instance by annotating the definition of sumInts with its type:
val sumInts: (int, int) => double = sum(x => x) // OK
On the other hand, Scala version 2.0 now automatically applies methods with empty parameter lists to () argument lists when necessary. For instance, the show expression above will now be expanded to
show(x.toString())
Your someFunction appears as a method here.
You could try either
object MyApp extends App{
case class FunctionContainer(val function:AnyRef)
implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)
val someFunction = (i:Int) => "someString"
def abc(f : FunctionContainer):String = "abc"
println(abc(someFunction))
}
or
object MyApp extends App{
case class FunctionContainer(val function:AnyRef)
implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)
def someFunction(i:Int): String = "someString"
def abc(f : FunctionContainer):String = "abc"
println(abc(someFunction(_: Int)))
}
By the way: implicitly casting such common functions to something else can quickly lead to problems. Are you absolutely sure that you need this? Wouldn't it be easier to overload abc?
You should use eta-expansion
println(abc(someFunction _))

Demystifying a function definition

I am new to Scala, and I hope this question is not too basic. I couldn't find the answer to this question on the web (which might be because I don't know the relevant keywords).
I am trying to understand the following definition:
def functionName[T <: AnyRef](name: Symbol)(range: String*)(f: T => String)(implicit tag: ClassTag[T]): DiscreteAttribute[T] = {
val r = ....
new anotherFunctionName[T](name.toString, f, Some(r))
}
First , why is it defined as def functionName[...](...)(...)(...)(...)? Can't we define it as def functionName[...](..., ..., ..., ...)?
Second, how does range: String* from range: String?
Third, would it be a problem if implicit tag: ClassTag[T] did not exist?
First , why is it defined as def functionName...(...)(...)(...)? Can't we define it as def functionName[...](..., ..., ..., ...)?
One good reason to use currying is to support type inference. Consider these two functions:
def pred1[A](x: A, f: A => Boolean): Boolean = f(x)
def pred2[A](x: A)(f: A => Boolean): Boolean = f(x)
Since type information flows from left to right if you try to call pred1 like this:
pred1(1, x => x > 0)
type of the x => x > 0 cannot be determined yet and you'll get an error:
<console>:22: error: missing parameter type
pred1(1, x => x > 0)
^
To make it work you have to specify argument type of the anonymous function:
pred1(1, (x: Int) => x > 0)
pred2 from the other hand can be used without specifying argument type:
pred2(1)(x => x > 0)
or simply:
pred2(1)(_ > 0)
Second, how does range: String* from range: String?
It is a syntax for defining Repeated Parameters a.k.a varargs. Ignoring other differences it can be used only on the last position and is available as a scala.Seq (here scala.Seq[String]). Typical usage is apply method of the collections types which allows for syntax like SomeDummyCollection(1, 2, 3). For more see:
What does `:_*` (colon underscore star) do in Scala?
Scala variadic functions and Seq
Is there a difference in Scala between Seq[T] and T*?
Third, would it be a problem if implicit tag: ClassTag[T] did not exist?
As already stated by Aivean it shouldn't be the case here. ClassTags are automatically generated by the compiler and should be accessible as long as the class exists. In general case if implicit argument cannot be accessed you'll get an error:
scala> import scala.concurrent._
import scala.concurrent._
scala> val answer: Future[Int] = Future(42)
<console>:13: error: Cannot find an implicit ExecutionContext. You might pass
an (implicit ec: ExecutionContext) parameter to your method
or import scala.concurrent.ExecutionContext.Implicits.global.
val answer: Future[Int] = Future(42)
Multiple argument lists: this is called "currying", and enables you to call a function with only some of the arguments, yielding a function that takes the rest of the arguments and produces the result type (partial function application). Here is a link to Scala documentation that gives an example of using this. Further, any implicit arguments to a function must be specified together in one argument list, coming after any other argument lists. While defining functions this way is not necessary (apart from any implicit arguments), this style of function definition can sometimes make it clearer how the function is expected to be used, and/or make the syntax for partial application look more natural (f(x) rather than f(x, _)).
Arguments with an asterisk: "varargs". This syntax denotes that rather than a single argument being expected, a variable number of arguments can be passed in, which will be handled as (in this case) a Seq[String]. It is the equivalent of specifying (String... range) in Java.
the implicit ClassTag: this is often needed to ensure proper typing of the function result, where the type (T here) cannot be determined at compile time. Since Scala runs on the JVM, which does not retain type information beyond compile time, this is a work-around used in Scala to ensure information about the type(s) involved is still available at runtime.
Check currying:Methods may define multiple parameter lists. When a method is called with a fewer number of parameter lists, then this will yield a function taking the missing parameter lists as its arguments.
range:String* is the syntax for varargs
implicit TypeTag parameter in Scala is the alternative for Class<T> clazzparameter in Java. It will be always available if your class is defined in scope. Read more about type tags.

Convert a list to a map and get item in one line

Try:
(List(('c', 1)).toMap)('c')
Error:
found : Char('c')
required: <:<[(Char, Int),(?, ?)]
However, this works fine:
val m = List(('c', 1)).toMap
m('c') // gives 1 as expected
Why would it matter whether to store it first in a variable or not? Could this be a bug?
The problem is that the full signature of toMap is the following:
def toMap[T, U](implicit ev: <:<[A, (T, U)]): Map[T, U]
The collections API designers didn't want List(1).toMap to compile, so they require you to provide implicit evidence that the contents of the list are tuples.
Normally you don't have to think about this—you just call toMap without an argument and the compiler finds the implicit evidence. When you write something that looks like you're calling toMap with an argument, though, you get into trouble, because it's always possible to provide implicit arguments explicitly.
The easiest workaround (assuming you don't want to define a separate variable) is to call apply explicitly. whatever(foo) is just syntactic sugar for whatever.apply(foo) (unless whatever is a method), and if you write the following your code will compile:
scala> List(('c', 1)).toMap.apply('c')
res0: Int = 1
Now it's perfectly clear that you're not trying to call the toMap method with c as an argument.

First parameter as default in Scala

Is there another way of making this work?
def b(first:String="hello",second:String) = println("first:"+first+" second:"+second)
b(second="geo")
If I call the method with just:
b("geo")
I get:
<console>:7: error: not enough arguments for method b: (first: String,second: String)Unit.
Unspecified value parameter second.
b("geo")
Here is one of the possible ways: you can use several argument lists and currying:
scala> def b(first:String="hello")(second:String) = println("first:"+first+" second:"+second)
b: (first: String)(second: String)Unit
scala> b()("Scala")
first:hello second:Scala
scala> val c = b() _
c: (String) => Unit = <function1>
scala> c("Scala")
first:hello second:Scala
See scala language specifications 6.6.1 (http://www.scala-lang.org/docu/files/ScalaReference.pdf):
"The named arguments form a suffix of the argument list e1, ..., em, i.e. no positional argument follows a named one."
Providing a single string parameter (without naming it) is too ambiguous for the compiler. Probably you meant the value for the non-default parameter, but... maybe not. So the compiler wants you to be more specific.
Generally you put all your default parameters at the end of the method signature (if you did in this case, b("geo") would work) so that they can be left out less ambiguously.