Using Tuples in map, flatmap,... partial functions - scala

If I do:
val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { t => t._1 + t._2 }
It's ok.
If I do:
val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { case (b, n) => b + n }
It's ok too.
But if I do:
val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map { (b, n) => b + n }
It will not work.
Why should I use "case" keyword to use named tuples?

The error message with 2.11 is more explanatory:
scala> l map { (b, n) => b + n }
<console>:9: error: missing parameter type
Note: The expected type requires a one-argument function accepting a 2-Tuple.
Consider a pattern matching anonymous function, `{ case (b, n) => ... }`
l map { (b, n) => b + n }
^
<console>:9: error: missing parameter type
l map { (b, n) => b + n }
^
For an apply, you get "auto-tupling":
scala> def f(p: (Int, Int)) = p._1 + p._2
f: (p: (Int, Int))Int
scala> f(1,2)
res0: Int = 3
where you supplied two args instead of one.
But you don't get auto-untupling.
People have always wanted it to work that way.

This situation can be understand with the types of inner function.
First, the type syntax of parameter function for the map function is as follows.
Tuple2[Int,Int] => B //Function1[Tuple2[Int, Int], B]
The first parameter function is expand to this.
(t:(Int,Int)) => t._1 + t._2 // type : Tuple2[Int,Int] => Int
This is ok. Then the second function.
(t:(Int, Int)) => t match {
case (a:Int, b:Int) => a + b
}
This is also ok. In the failure scenario,
(a:Int, b:Int) => a + b
Lets check the types of the function
(Int, Int) => Int // Function2[Int, Int, Int]
So the parameter function type is wrong.
As a solution, you can convert multiple arity functions to tuple mode and backward with the helper functions in Function object. You can do following.
val l = Seq(("un", ""), ("deux", "hehe"), ("trois", "lol"))
l map(Function.tupled((b, n) => b + n ))
Please refer Function API for further information.

The type of a function argument passed to map function applied to a sequence is inferred by the type of elements in the sequence. In particular,
scenario 1: l map { t => t._1 + t._2 } is same as l map { t: ((String, String)): (String) => t._1 + t._2 } but shorter, which is possible because of type inference. Scala compiler automatically inferred the type of the argument to be (String, String) => String
scenario 2: you can also write in longer form
l map { t => t match {
case(b, n) => b + n
}
}
scenario 3: a function of wrong type is passed to map, which is similar to
def f1 (a: String, b: String) = a + b
def f2 (t: (String, String)) = t match { case (a, b) => a + b }
l map f1 // won't work
l map f2

Related

Why is the return type of the fold operation Serializable and not String

Here is a simple scala code
scala> val x = scala.collection.immutable.TreeMap[String, String]("a"->"a", "b"->"b")
x: scala.collection.immutable.TreeMap[String,String] = Map(a -> a, b -> b)
scala> val y = x.fold(""){case (acc: String, (k: String, v: String)) => acc + s""", "$k":"$v""""}
y: java.io.Serializable = , "a":"a", "b":"b"
Why is the return type of y not String but java.io.Serializable?
I thought it could be because I am using pattern matching and the match can be non-exhaustive. So I changed my code to
scala> val y = x.fold(""){case (acc: String, (k:String, v:String)) => acc + s""", "$k":"$v"""" case _ => ""}
y: java.io.Serializable = , "a":"a", "b":"b"
The signature of fold is:
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1
Both operands of the the folding operation (op) must be of the same type, but in your case they are String and (String, String), so the compiler instead tries to look for the least-upper bound between the two types, and finds Serializable.
foldLeft and foldRight allow the accumulator and next element to be different types, so those can work.
scala> x.foldLeft("") { case (acc, (k, v)) => acc + s""", "$k":"$v"""" }
res8: String = , "a":"a", "b":"b"
Folding isn't really what you want here, though, because you'll end up with that extra comma unless you handle it specially. Instead, you could use map and mkString.
scala> x.map { case (k, v) => s""""$k":"$v"""" }.mkString(", ")
res10: String = "a":"a", "b":"b"
I also removed the type ascriptions, as they were not necessary.

Difficulty understanding this type signature

merge sort type signature :
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {
The function is called using :
msort[Int]((a, b) => a < b) _
Does the type msort[Int] type the parameters a & b to Int ?
To better understand this type signature I've tried to extract the less function :
def lessFunc[Int]((a , b) : (Int , Int)) : Boolean = {
true
}
But this is not correct ?
Entire code :
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {
def merge(xs: List[T], ys: List[T], acc: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys.reverse ::: acc
case (_, Nil) => xs.reverse ::: acc
case (x :: xs1, y :: ys1) =>
if (less(x, y)) merge(xs1, ys, x :: acc)
else merge(xs, ys1, y :: acc)
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(less)(ys), msort(less)(zs), Nil).reverse
}
}
This is a function which takes two lists of parameters. The first list contains a less function, which as you've guessed correctly when invoked with [Int] is typing the parameters to Int.
You have just expanded it wrong. What you should have done is
def less(a: Int, b: Int) = true
or to match your anonymous function
def less(a: Int, b: Int) = a < b
Now when you call your msort like msort[Int](less) _ (see currying) you'll get a new function which is able to sort Lits[Int].
val listSorter = msort[Int](less) _
listSorter(List(1, 2, 3))
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T]
is a function with two parameter lists that returns List of type T. First parentheses let you pass a function that will be used for sorting the list.
(T,T) => Boolean - means that the function will take two parameters and yield boolean.
The second parentheses take a List of type T . This T after name of the function is like generics in Java. You use it to pass a type. It can be called like:
def msort[String]((a,b) => a.length < b.length)(some list) if you want to sort List of String's by their length. Or you can call it like in the example to sort List of Ints
def msort[Int]((a,b) => a < b)(some list)
Because function is defined with two sets of parameters we can take advantage of it by applying only part of them and build specialised functions based on that one. Like for example:
val stringSort = msort[String]((a,b) => a.length < b.length) _
val ascendingIntSort = msort[Int]((a,b) => a < b) _
These are curried functions because stringSort's signature is List[Strint] => List[String]. Now you can reuse these methods by passing only instances of Lists to them:
stringSort(List("cat", "elephant", "butterfly"))
ascendingIntSort(List(4,1,3,2))

scala map / type instantiation

can someone explain the best way to get around the following,
rather curious type error. Suppose I create a list of tuples like so:
scala> val ys = List((1,2), (3,4), (5,6))
ys: List[(Int, Int)] = List((1,2), (3,4), (5,6))
Now, if I want to map this to a List(Int)
scala> ys.map((a: Int, b: Int) => a + b)
<console>:9: error: type mismatch;
found : (Int, Int) => Int
required: ((Int, Int)) => ?
ys.map((a: Int, b: Int) => a + b)
^
Any clues? I know I can use the for comprehension
scala> for ((a, b) <- ys) yield a + b
res1: List[Int] = List(3, 7, 11)
But it feels wrong to bust out a comprehension in this setting. Thanks!
try:
ys.map { case (a: Int, b: Int) => a + b }
or:
ys.map(p: (Int, Int) => p._1 + p._2)
What's happening is that ys is a List of (Int,Int), so map expects a function from a single argument, which happens to be a tuple (Int,Int), to something else (technically, map expects an argument of Function1[(Int,Int),Int]. The function (a: Int, b: Int) => a+b is not actually a function from a single argument (Int, Int) to Int; instead it's a function of two arguments, both Ints, to an Int (a Function2[Int,Int,Int]). The difference is subtle, but important since Scala makes a distinction:
val f: Function1[(Int,Int),Int] = (p: (Int,Int)) => p._1 + p._2
ys.map(f) // fine
val g: Function1[(Int,Int),Int] = { case (a: Int, b: Int) => a + b }
ys.map(g) // fine, technically a PartialFunction[(Int,Int),Int]
val h: Function2[Int,Int,Int] = (a: Int, b: Int) => a + b
ys.map(h) // ERROR!
To explain my suggestions at the top of the answer: In the first example, we have changed the definition of the function given to map to use case, which tells Scala to unpack the single (Int,Int) argument into its two parts. (Note also the use of curly braces instead of parentheses.) In the second example, we have a function of a single tuple argument, p, and we manually extract each part of the tuple.
Finally, note that you don't need the type annotations either. These work just as well:
ys.map { case (a,b) => a + b }
ys.map(p => p._1 + p._2)
try:
val ys = List((1,2),(3,4),(5,6))
ys map (t => t._1 + t._2)

Is there a cleaner way to pattern-match in Scala anonymous functions?

I find myself writing code like the following:
val b = a map (entry =>
entry match {
case ((x,y), u) => ((y,x), u)
}
)
I would like to write it differently, if only this worked:
val c = a map (((x,y) -> u) =>
(y,x) -> u
)
Is there any way I can get something close to this?
Believe it or not, this works:
val b = List(1, 2)
b map {
case 1 => "one"
case 2 => "two"
}
You can skip the p => p match in simple cases. So this should work:
val c = a map {
case ((x,y) -> u) => (y,x) -> u
}
In your example, there are three subtly different semantics that you may be going for.
Map over the collection, transforming each element that matches a pattern. Throw an exception if any element does not match. These semantics are achieved with
val b = a map { case ((x, y), u) => ((y, x), u) }
Map over the collection, transforming each element that matches a pattern. Silently discard elements that do not match:
val b = a collect { case ((x, y), u) => ((y, x), u) }
Map over the collection, safely destructuring and then transforming each element. These are the semantics that I would expect for an expression like
val b = a map (((x, y), u) => ((y, x), u)))
Unfortunately, there is no concise syntax to achieve these semantics in Scala.
Instead, you have to destructure yourself:
val b = a map { p => ((p._1._2, p._1._1), p._2) }
One might be tempted to use a value definition for destructuring:
val b = a map { p => val ((x,y), u) = p; ((y, x), u) }
However, this version is no more safe than the one that uses explicit pattern matching. For this reason, if you want the safe destructuring semantics, the most concise solution is to explicitly type your collection to prevent unintended widening and use explicit pattern matching:
val a: List[((Int, Int), Int)] = // ...
// ...
val b = a map { case ((x, y), u) => ((y, x), u) }
If a's definition appears far from its use (e.g. in a separate compilation unit), you can minimize the risk by ascribing its type in the map call:
val b = (a: List[((Int, Int), Int)]) map { case ((x, y), u) => ((y, x), u) }
In your quoted example, the cleanest solution is:
val xs = List((1,2)->3,(4,5)->6,(7,8)->9)
xs map { case (a,b) => (a.swap, b) }
val b = a map { case ((x,y), u) => ((y,x), u) }

Example in Scala of hashmap forall() method?

Can someone please give an example of how to use the HashMap forall() method? I find the Scala docs to be impenetrable.
What I want is something like this:
val myMap = HashMap[Int, Int](1 -> 10, 2 -> 20)
val areAllValuesTenTimesTheKey = myMap.forall((k, v) => k * 10 == v)
but this gives:
error: wrong number of parameters; expected = 1
You need instead
val myMap = HashMap[Int, Int](1 -> 10, 2 -> 20)
val areAllValuesTenTimesTheKey = myMap.forall { case (k, v) => k * 10 == v }
The problem is that forall wants a function that takes a single Tuple2, rather than two arguments. (We're thinking of a Map[A,B] as an Iterable[(A,B)] when we use forall.) Using a case statement is a nice workaround; it's really using pattern matching here to break apart the Tuple2 and give the parts names.
If you don't want to use pattern matching, you could have also written
val areAllValuesTenTimesTheKey = myMap.forall(p => p._1 * 10 == p._2 }
but I think that's less helpful.
forall is passed a single (Int, Int) Tuple (as opposed to multiple parameters). Consider this (which explicitly shows a single tuple value is decomposed):
val areAllValuesTenTimesTheKey = myMap.forall(t => t match { case (k, v) => k * 10 == v })
Or, the short-hand (which actually passes a PartialFunction):
val areAllValuesTenTimesTheKey = myMap.forall {case (k, v) => k * 10 == v}
(These both decompose the tuple take in.)
Additionally, the function can be "tupled"ed:
val myMap = Map((1,10), (2,20))
val fn = (k: Int, v: Int) => k * 10 == v
val tupled_fn = fn.tupled
val areAllValuesTenTimesTheKey = myMap.forall(tupled_fn)
myMap: scala.collection.immutable.Map[Int,Int] = Map((1,10), (2,20))
fn: (Int, Int) => Boolean = // takes in two parameters
tupled_fn: ((Int, Int)) => Boolean = // note that it now takes in a single Tuple
areAllValuesTenTimesTheKey: Boolean = true
Happy coding.
The problem with your code, is that you give forall method a function, that accepts 2 arguments and returns Boolean, or in other words (Int, Int) => Boolean. If you will look in the documentation, then you will find this signature:
def forall (p: ((A, B)) => Boolean): Boolean
in this case forall method expects Tuple2[A, B] => Boolean, so it also can be written like this:
def forall (p: Tuple2[A, B] => Boolean): Boolean
In order to fix your example you can either call forall and give it function, that accepts 1 tuple argument:
myMap.forall(keyVal => keyVal._1 * 10 == keyVal._2)
or you make patterns match and extract key and value:
myMap.forall {case (k, v) => k * 10 == v}
In this case you are giving PartialFunction[(Int, Int), Boolean] to the forall method