Check this REPL session, under Scala 2.12.10:
scala> val a = 3 match { case 3 => 1 case 4 => println("why") }
a: AnyVal = 1
scala> val a: Int = 3 match { case 3 => 1 case 4 => println("why") }
<console>:11: error: type mismatch;
found : Unit
required: Int
val a: Int = 3 match { case 3 => 1 case 4 => println("why") }
scala> val (a, b) = 3 match { case 3 => (1, 2) case 4 => println("why") }
a: Any = 1
b: Any = 2
scala> val (a: Int, b) = 3 match { case 3 => (1, 2) case 4 => println("why") }
a: Int = 1
b: Any = 2
scala> val (a, b) = 4 match { case 3 => (1, 2) case 4 => println("why") }
why
scala.MatchError: () (of class scala.runtime.BoxedUnit)
... 36 elided
I would expect the snippets with tuples to not compile as if Unit is ever returned from the second match, then you always get a runtime error. Why does Unit match successfully to Tuple2 in the compiler view?
Unit cannot be assigned to a Tuple
val t: (Int, Int) = () // Error: type mismatch; found: Unit required: (Int, Int)
However the following syntax represents proper pattern matching
val (a: Int, b: Int) = ...
which in your case desugars to something like so
val x: Any = 3 match {
case 3 => (1, 2)
case 4 => println("why")
}
val a: Int = x match {
case t: (_, _) if t._1.isInstanceOf[Int] => t._1.asInstanceOf[Int]
}
val b: Int = x match {
case t: (_, _) if t._2.isInstanceOf[Int] => t._2.asInstanceOf[Int]
}
Notice those asInstanceOf[Int] which convince compiler static type of a and b is Int, however what would happen at runtime is a different story. For example, consider
val t: (Int, Int) = println("why").asInstanceOf[(Int, Int)]
which compiles but fails a runtime.
Analysing -Xprint:jvm output of
lazy val (a: Int, b: Int) = (): Any
we have approximately
val t: Tuple2 = {
val x1: Any = ()
if (x1.isInstanceOf[Tuple2]) {
val x2: Tuple2 = x1.asInstanceOf[Tuple2]
val a: Any = x2._1
val b: Any = x2._2
if (a.isInstanceOf[Int]) {
val x3: Int = scala.Int.unbox(a) // in effect asInstanceOf[Int]
if (b.isInstanceOf[Int]) {
val x4: Int = scala.Int.unbox(b) // in effect asInstanceOf[Int]
new Tuple2(x3, x4)
} else throw new MatchError(x1)
} else throw new MatchError(x1)
} else throw new MatchError(x1)
}
def a: Int = t._1
def b: Int = t._2
whilst
lazy val (a: Int, b: Int) = ()
does not compile, hence if expression on the right of =, in pattern value definition, types to Any it makes all the difference.
Since your partial function can return either a Tuple2[Int, Int] or a Unit, the compiler considers its return type to be the "smallest common supertype" of those two types, that is Any :
scala> val x = 4 match { case 3 => (1, 2) case 4 => println("why") }
why
x: Any = ()
Notice how the return value is x: Any = () and not x: Unit = ().
What you're doing when extracting the tuple is equivalent to this, which compiles (since Any is a supertype of Tuple2), but produces a MatchError:
scala> val (a, b) = ().asInstanceOf[Any]
scala.MatchError: () (of class scala.runtime.BoxedUnit)
... 28 elided
Related
Suppose I have functions that takes 2 Ints and returns 1 Int:
val sub : (Int, Int) => Int = (x,y) => x - y
val mul : (Int, Int) => Int = (x,y) => x * y
How will I create a method that accepts the above type. I tried:
def test(f:(Int,Int) => Int) : Unit = {
val a = f
println(a)
}
Which does not seem to work.
I guess by "Does not seem to work.", you mean you're trying to call the function and expected an output in the println.
The function works as designed. It's just not the way you probably hoped it will work...
So this for instance will not work:
scala> test(sub(5, 3))
<console>:14: error: type mismatch;
found : Int
required: (Int, Int) => Int
test(sub(5, 3))
But you can modify test() slightly:
scala> def test(f:(Int,Int) => Int, x: Int, y: Int) : Unit = {
| val a = f(x, y)
| println(a)
| }
test: (f: (Int, Int) => Int, x: Int, y: Int)Unit
And call it with the params:
scala> test(sub, 5, 3)
2
scala> test(mul, 5, 3)
15
What is the logical reason that the first form works and not the second?
scala> val d = (a: Int, b: Int) => a + b
d: (Int, Int) => Int = <function2>
scala> val d = (a: Int)(b: Int) => a + b
<console>:1: error: not a legal formal parameter.
Note: Tuples cannot be directly destructured in method or function parameters.
Either create a single parameter accepting the Tuple1,
or consider a pattern matching anonymous function: `{ case (param1, param1) => ... }
val d=(a:Int)(b:Int)=>a+b
Because multiple parameter lists aren't allowed on function declarations. If you want to curry a function, you do:
scala> val d: Int => Int => Int = a => b => a + b
d: Int => (Int => Int) = $$Lambda$1106/512934838#6ef4cbe1
scala> val f = d(3)
f: Int => Int = $$Lambda$1109/1933965693#7e2c6702
scala> f(4)
res6: Int = 7
You can also create a single parameter list and partially apply it:
scala> val d = (a: Int, b: Int) => a + b
d: (Int, Int) => Int = $$Lambda$1064/586164630#7c8874ef
scala> d(4, _: Int)
res2: Int => Int = $$Lambda$1079/2135563436#4a1a412e
We partially applied d with 4, and we got back a function, Int => Int, which means when we supply the next argument, we'll get the result:
scala> res2(3)
res3: Int = 7
We can also create a named method, and use eta-expansion to create a curried function out of it:
scala> def add(i: Int)(j: Int): Int = i + j
add: (i: Int)(j: Int)Int
scala> val curriedAdd = add _
curriedAdd: Int => (Int => Int) = $$Lambda$1115/287609100#f849027
scala> val onlyOneArgumentLeft = curriedAdd(1)
onlyOneArgumentLeft: Int => Int = $$Lambda$1116/1700143613#77e9dca8
scala> onlyOneArgumentLeft(2)
res8: Int = 3
Function currying is possible.
val curryFunc = (a: Int) => (b: Int) => a + b
curryFunc now has the type Int => (Int => Int)
object MatchTest4 extends App{
def matchTest(x: Any): Any = x match {
case 1 => def num(p: Int, q: Int): Unit = {
val sum = p + q
println(sum)
}
case 2 => def num(p: Int, q: Int): Unit = {
val sub = p - q
println(sub)
}
case 3 => def num(p: Int, q: Int): Unit = {
val mul = p * q
println(mul)
}
case 4 => def num(p: Int, q: Int): Unit = {
val div = p / q
println(div)
}
case _ => println("Invalid Choice")
}
println("Enter Your Choice")
val b= readInt()
println(matchTest(b))
}
Now I want to give parameter to the function num().Is it possible?
The below code should accomplish what you are trying to do. The changes I made were:
Removed the return type for the matchTest. Scala will automatically infer the return type as a function value.
Replaced the method definitions "num" by anonymous functions.
Modified the signature of your wildcard pattern match to also return a function consistent with other pattern matches. (This is a hack, I hope someone knows a better way)
You can run your returned anonymous functions like matchTest(4)(2,3) etc.
def matchTest(x: Any) = x match {
case 1 => (p: Int, q: Int) => {
val sum = p + q
println(sum)
}
case 2 => (p: Int, q: Int) => {
val sub = p - q
println(sub)
}
case 3 => (p: Int, q: Int) => {
val mul = p * q
println(mul)
}
case 4 => (p: Int, q: Int) => {
val div = p / q
println(div)
}
case _ => println("Invalid Choice"); (a: Int, b: Int) => println("Invalid Choice")
}
There are some discussions here about this, but I have some specific questions I wasn't able to find an answer for. So, by call-by-name, I mean =>T type, and by 0-arity function I mean () => T
I understand (I think) the conceptual difference, but probably I am missing something as I still have lots of questions:
Why do we have the concept of =>T at all if we could always use () => T?
Is there any syntax/functional limitations of each? For now I found only that => cannnot be used as a class field. Is this the only limitation?
Is the generated code always the same for both?
Should I always prefer =>T? And why?
Is it correct to call =>T a type? It looks for me like it does not have any type representation in scala.
1) It's just more handy to use it, especially inside DSLs:
def printAndGet[T](f: => T) = {
val res = f
println(res + " printed")
res
}
scala> :paste
// Entering paste mode (ctrl-D to finish)
val k = printAndGet {
val a = 5
5 * a
}
// Exiting paste mode, now interpreting.
25 printed
k: Int = 25
2) => T can only be a parameter of method or function. And actually => T and () => T aren't interchangable:
scala> def aaa(f: => String) = f
aaa: (f: => String)String
scala> val a: Function1[() => String, String] = aaa _
<console>:8: error: type mismatch;
found : (=> String) => String
required: (() => String) => String
val a: Function1[() => String, String] = aaa _
^
Thanks to #som-snytt, fоund this one:
scala> object O { def f(i: Int) = i; def f(i: => Int) = i + 1 }
defined object O
scala> O.f(5)
res12: Int = 5
scala> O.f(5: (=> Int))
<console>:1: error: no by-name parameter type allowed here
O.f(5: (=> Int))
^
Even this which should work if it compiles - but it doesn't (scala 2.11.2, 2.11.5 REPL just crashes):
scala> val k: (=> Int) => Int = O.f _
k: (=> Int) => Int = <function1>
scala> k(5) //should be 6
res18: Int = 5 //WTF?
Last one seems like a bug
3) Not exactly, if you want the same, just convert => T into () => T:
scala> def aaa(f: => String) = {f _}
aaa: (f: => String)() => String
Bytecode may also differ. For instance, compiler will more likely inline code from => T without generating lambda for it. So, the key difference is that () => T is actually an object (first class citizen), => T isn't.
4) see 1, but sometimes you may need to ensure that user knows that computation might be delayed - () => T is better then.
5) It's part of a type signature, just look at eta-expansion of:
scala> def aaa(f: => String) = {f}
aaa: (f: => String)String
scala> aaa _ //convert method into a function
res7: (=> String) => String = <function1>
scala> val a: ( => String) => String = aaa _
a: (=> String) => String = <function1>
However scala doesn't recognize it as independent type:
scala> val a: Function1[( => String), String] = aaa _
<console>:1: error: no by-name parameter type allowed here
val a: Function1[( => String), String] = aaa _
^
My code is as follows
val hash = new HashMap[String, List[Any]]
hash.put("test", List(1, true, 3))
val result = hash.get("test")
result match {
case List(Int, Boolean, Int) => println("found")
case _ => println("not found")
}
I would expect "found" to be printed but "not found" is printed. I'm trying to match on any List that has three elements of type Int, Boolean, Int
You are checking for a list containing the companion objects Int and Boolean. These are not the same as the classes Int and Boolean.
Use a Typed Pattern instead.
val result: Option[List[Any]] = ...
result match {
case Some(List(_: Int, _: Boolean, _: Int)) => println("found")
case _ => println("not found")
}
Scala Reference, Section 8.1 describes the different patterns you can use.
The first problem is that the get method returns an Option:
scala> val result = hash.get("test")
result: Option[List[Any]] = Some(List(1, true, 3))
So you'd need to match against Some(List(...)), not List(...).
Next, you are checking if the list contains the objects Int, Boolean and Int again, not if it contains objects whose types are Int, Boolean and Int again.
Int and Boolean are both types and object companions. Consider:
scala> val x: Int = 5
x: Int = 5
scala> val x = Int
x: Int.type = object scala.Int
scala> val x: Int = Int
<console>:13: error: type mismatch;
found : Int.type (with underlying type object Int)
required: Int
val x: Int = Int
^
So the correct match statement would be:
case Some(List(_: Int, _: Boolean, _: Int)) => println("found")
The following also works for Scala 2.8
List(1, true, 3) match {
case List(a:Int, b:Boolean, c:Int) => println("found")
case _ => println("not found")
}