What is the return type of a scala if statement without else - scala

I made a mistake similar to the following in some Scala code:
val someVal:String = if (someX == someY) n
In retrospect the mistake is clear to me (it has to assign something to someVal, and if the expression is false, it's probably not going to be a string). I want to know what the value returned would be if the expression is false. My best guess is Unit, or perhaps AnyVal.

That code will not compile, because for someVal to be a String, then each execution path (the if and the else) would have to return a String. However since you don't have an else, this is impossible and thus won't compile.
The compiler error will be the following, which is indicating that you are returning a Unit when you are supposed to return String:
error: type mismatch;
found : Unit
required: String
That's because what you have is equivalent to this:
val someVal: String = if (foo == bar) "Hello World" else ()
() is the only value of Unit, which is not a valid value for a String type.
In the future you can use the Scala repl and have it tell you the types (just don't specify one):
scala> val someVal1 = if (true) "Hello World"
someVal1: Any = Hello World
val someVal2 = if (false) "Hello World"
someVal2: Any = ()
As you can see the type is Any because that is the only common parent between Unit and String:

The type of whole if expression will be the smallest common supertype of all branches.
The true path is String in your example
A missing else is the same as an empty else, both evaluate to Unit.
Unit and String are in completely separate inheritance trees, (String is AnyRef and Unit is AnyVal) so the type of the whole expression is Any, the common super type of all types in Scala.
If the true case resulted in an AnyVal type (for example, Int) then the type of the whole expression would be AnyVal
If the true case evaluated to Unit (for example if you just used println in the if instead of returning a value) then the type of the expression would be Unit which makes sense since both paths have the same type.

Related

What is the Unit Type Behavior in Scala

Why does this code works:
val f: String => Unit = {(_:String) => 2}
//or more succinctly
val f: String => Unit = _ => 2
Clearly the body of my function return an int. However the type expected is Unit
But if i do
implicitly[String <:< Unit]
//Cannot prove that String <:< Unit.
Hence i am a bit confused as to how the cast is happening ? Why is it even allowed in the first place
This is called Value Discarding, see 6.26.1 Value Conversions, sub-section Value Discarding of the Scala Language Specification:
If š‘’ has some value type and the expected type is Unit, š‘’ is converted to the expected type by embedding it in the term { š‘’; () }.
In other words, your code is compiled as-if you had written
val f: String => Unit = {(_:String) => { 2; () }}
//or more succinctly
val f: String => Unit = _ => { 2; () }
I guess the intention behind this feature is familiarity for programmers coming from imperative programming languages, where it is often possible to discard values.
E.g. in C, it is legal to write
printf("Hello");
and discard the return value, even though printf returns an int. This goes together with the idea of expression statements and statement expressions that many imperative programming languages have.
In Scala any type A can be transformed into type Unit. It's not subtyping, it's transforming. Like type A means some possible side effect plus returning A while Unit means we disregard return value but keep the side effect.

Swapping tuples of different types in Scala

I'm trying to write a simple function that will swap a (Int, String) tuple in Scala. I've tried a number of things and I keep getting compiler errors, for example:
def swap (p:(Int,String)) : (String,Int) = {
var s = p._1
var t = p._2
var u = (p._2, p.1)
}
[error] found : Unit
[error] required: (String, Int)
[error] }
[error] ^
[error] one error found
Why does it keep saying it finds a "Unit" type? I've tried different variations, and even using the "swap" function built into Scala, but I keep getting these kinds of errors stating that my return type isn't (String, Int). Any suggestions are appreciated. Thank you!
The return value of a method (or more generally, the value of any block) is the value of the last expression inside the block. The last expression in your block is
var u = (p._2, p.1)
The value of an assignment is () (which is the singleton value of the Unit type): an assignment is a side-effect, it doesn't have a value, and () is the value (and Unit the type) which denotes the absence of a value (think "void" if you are familiar with C, C++, D, Objective-C, Objective-C++, Java, or Cā™Æ); ergo, your method returns (), which is of type Unit.
Here's a more Scala-ish way to write your method:
def swap[A, B](p: (A, B)) = (p._2, p._1)
All you need is this:
def swap(p: (Int,String)): (String,Int) = (p._2, p._1)
And to make it work on any tuple (of size 2):
def swap[A,B](p: (A,B)): (B,A) = (p._2, p._1)
In Scala, the last expression in a function is the returned value. It also supports an explicit return expression, that would be like this:
def swap(p: (Int,String)): (String,Int) = {
return (p._2, p._1)
}
or more like what you intended:
def swap(p: (Int,String)): (String,Int) = {
val result = (p._2, p._1)
return result
}
Keep in mind this explicit return syntax is not recommended.
Because Scala is a functional language, everything is an expression. Expressions are anything you can evaluate and get back a resulting value, which, being a value, has a type.
Even things that you would think more like "statements", like println("a") or var a = 1 are expressions. When evaluated, they return a meaningless/empty value, that is of type Unit. Therefore, your function returns the last statement, that is a variable assignment, which has a value of type Unit.
You can also achieve it using pattern matching and function literal:
def swap[X,Y]: ((X,Y)) => ((Y,X)) = { case (x, y) => (y, x) }

Why Unit is a supertype of anything else?

Here's an example:
$ scala
Welcome to Scala 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_112).
Type in expressions for evaluation. Or try :help.
scala> val a: Unit = 1
<console>:11: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
val a: Unit = 1
^
a: Unit = ()
In Scala documentation:
There is only one value of type Unit, ()
Why is Scala compiler silently coercing values to Unit?
A bit of context: I used Future[Unit] type to describe some procedure which does not return anything. And since Future[Unit] is now effectively a subtype of Unit, I got bit by some funny bugs (someFuture.map(a => Future(a)) silently skips calling the operation instead of giving compilation warning). What am I supposed to use as a type of operation that does not return any meaningful result?
Unit is not a supertype of other types. What happens instead is called value discarding: when the expected type of an expression e is Unit, the compiler replaces it by {e; ()}. This is done to make some behavior more familiar. E.g.
val sb = new StringBuilder
val strings: List[String] = ...
for (str <- strings) { sb.append(str) }
By analogy with for loops in other languages, we would expect it to compile. But without value discarding it wouldn't: this is equivalent to strings.foreach(str => sb.append(str)), the type of str => sb.append(str) is String => StringBuilder (because all append methods on StringBuilder return the builder itself) and foreach on List[String] takes String => Unit.
You can add -Ywarn-value-discard compiler option to warn you when it happens (and write for (sb <- sbOpt) { sb.append("a"); () } explicitly).
Or you can actually go with a trick of defining your own Unit (possibly changing the name to avoid confusion for anyone reading your code):
object Unit
type Unit = Unit.type
implicit def unit2scalaunit(a: Unit): scala.Unit = ()
implicit def scalaunit2unit(a: scala.Unit): Unit = Unit
This should avoid running into the problem with Future you describe.
Unit is not a supertype of everything! Scala actually has a pretty wide variety of conversions that happen automatically and this is one of them. From section 6.26.1 Value Conversions of the Scala language spec, one of the conversions is
Value Discarding
If e has some value type and the expected type is Unit, e is converted
to the expected type by embedding it in the term { e; () }.
So when you write something like val a: Unit = 1, it gets processed into val a: Unit = { 1; () }, which is quite different. The warning is actually very helpful here - it is warning you that you probably did something wrong: the expression you are trying to put into statement position is pure (has no side-effects), so executing it has no effect (except possibly to cause the program to diverge) on the final output.

Why the following scala code is valid?

My understanding is Unit = void, but why I can pass in multiple argument?
So can anyone explain why the following code is valid?
def foo(x: Unit) = println("foo")
foo("ss", 1)
If you run your snippet with scala -print you'll roughly get the following output for the code:
/* Definition of foo */
private def foo(x: scala.runtime.BoxedUnit): Unit = {
/* Invocation of foo */
foo({
new Tuple2("ss", scala.Int.box(1));
scala.runtime.BoxedUnit.UNIT
});
As you can see, the arguments to foo are rewritten into a code block that creates a tuple but then returns UNIT.
I can't see a good reason for this behaviour and I'd rather get a compiler error thrown instead.
A related question which gives a decent answer to this is here:
Scala: Why can I convert Int to Unit?
From Section 6.26.1 of the Scala Language Specification v2.9, "Value Discarding":
If e has some value type and the expected type is Unit, e is converted to the expected type by embedding it in the term { e; () }.
So, in your case it seems ("ss", 1) is being converted to a tuple so that it can be treated as a single argument, then as that argument type is not Unit, it is converted to a block which computes that tuple value then returns unit to match with the required type of the parameter.

redundant parameter type info in partially applied function definition

def foo(num:Int, str:String):Int = 1
val bar = foo(3, _) // compiler complains "missing parameter type for expanded function ((x$1) => test(3, x$1))"
val baz = foo(3, _:String) // compiles fine
Why do I have to explicitly specify the type of _ when it looks inferrable from the context?
EDIT: Renamed to avoid name collision following David Soergel's suggest.
First of all, to avoid confusion between "def test" and "val test", let's write:
def foo(num:Int, str:String):Int = 1
val bar = foo(3, _) // compiler complains "missing parameter type for expanded function ((x$1) => foo(3, x$1))"
val baz = foo(3, _:String) // compiles fine
What's inferrable from context is only that the argument to bar must somehow be convertible to a String. That could be due to inheritance (if instead of String you use some non-final type there), or due to an implicit conversion.
Basically the potential for implicits means that the argument to bar could be just about any type at all, so the code as written is indeed underspecified. I don't know whether the compiler actually checks whether there are any appropriate implicit conversions in scope before issuing the "missing type" error, but I would guess not. (In the case of String there are likely to be a bunch present, anyway). It would be brittle and confusing if the signature of baz changed as a result of importing a new implicit that could produce a String.
I think David Soergel's explanation is essentially correct: if type T has an implicit conversion to String then val bar = foo(3, _:T) is valid, giving a function of type T => Int, which is unrelated to String => Int.
Why the compiler doesn't make a sensible assumption (in the absence of explicit typing) that the type is in fact the same as in the method (which is the essence of your question), I don't know - I can only guess it's because it would complicate the language spec.
Where no types are specified, i.e. val bar = foo(_, _), it seems the compiler interprets it as simple eta-conversion, the same as val bar = foo _, which does give a String => Int.
My preferred idiom would be to give the function type on the left hand side, which has the benefit of allowing you to easily see bar's type:
val bar: String => Int = foo(3, _)
If you're allergic to re-typing the word String, you could write
val bar = (foo _).curried(3)