Scala function typing issue - scala

I'm given a list lis of type List[(Char,Int)] and need to sort it by chars, but I want do do this using sortWith function. One way to do this is using:
val newlis = lis.sortWith({case ((a,b),(c,d)) => a<c })
But when I wrote auxilary function:
def aux2(term: ((Char, Int),(Char, Int)) ): Boolean = term match {
case ((a,b),(c,d)) => a<c
case _ => false
}
lis.sortWith(aux2)
I get type mismatch. Why is that?

There is a difference between
def f(a: A, b: B): C
and
def f(ab: (A, B)): C
When lifted and desugared, the first one becomes a Function2[A, B, C], while the second one becomes Function[(A, B), C]. While essentially the same (isomorphic), these two types must be used with a slightly different syntax.
Here is a slightly more complex example with (Char, Int) instead of A and B:
def g1(abcd: ((Char, Int), (Char, Int))): Boolean = abcd._1._2 < abcd._2._2
val g1Fun: (((Char, Int), (Char, Int))) => Boolean = g1
// ^^^ ^
// || \ second component of tuple: (Char, Int)
// || first component of tuple: (Char, Int)
// | \
// | single argument of type ((Char, Int),(Char, Int))
// \
// argument list of `g1Fun`, accepts *one* argument
def g2(ab: (Char, Int), cd: (Char, Int)): Boolean = ab._2 < cd._2
val g2Fun: ((Char, Int), (Char, Int)) => Boolean = g2
// ^^ ^
// || second argument of type (Char, Int)
// | \
// | first argument of type (Char, Int)
// \
// argument list of `g2Fun`, needs *two* arguments
The function lt in sortWith behaves like the second function g2Fun, whereas the function aux2 that you have written is more like the g1Fun.
So, to fix your code, you have to define aux2 as a binary operation:
val lis: List[(Char,Int)] = List(('a', 34), ('b', 42))
println(lis.sortWith({case ((a,b),(c,d)) => a < c }))
def aux2(
firstTerm: (Char, Int),
secondTerm: (Char, Int)
): Boolean = (firstTerm, secondTerm) match {
case ((a,b), (c,d)) => a < c
case _ => false
}
println(lis.sortWith(aux2))
To make it work with the same arity of f as you have written, the sortWith would have to accept a slightly different sort of functions.
Consider this:
def watchTheArity(f: ((Int, Int)) => Int): Int = {
f((4, 5))
}
See the weird double parens wrapping the (Int, Int) part?
This means: "I expect an f that takes a single tuple as argument".
Therefore, this works:
def f(ab: (Int, Int)): Int = ab._1 + ab._2
println(watchTheArity(f))
However, if it were defined as
def watchTheArity2(f: (Int, Int) => Int): Int = {
f(4, 5)
}
then you would need an f that takes two arguments.
Edit notice Added Ascii-art explanation of parentheses.

Related

Scala method that takes a function as a parameter and then executes it

I want to write a simple method that takes a function as a parameter and then executes it.
def exec(f: (a:Int, b:Int) => Boolean): Boolean = f(a,b)
I'm not sure what is wrong with the above, but I get the error:
<console>:1: error: ')' expected but ':' found.
def exec(f: (a:Int, b:Int) => Boolean): Boolean = f(a,b)
^ ^
| |
// These are supposed to be types, but a: Int and b: Int aren't types,
// they are identifiers with type ascriptions.
It should look a little more like:
def exec(f: (Int, Int) => Boolean): Boolean = f(a, b)
Now f is a function (Int, Int) => Boolean. But this doesn't compile, because a and b are not defined.
You either need to pass them in, or fix them to a value.
def exec(a: Int, b: Int)(f: (Int, Int) => Boolean): Boolean = f(a, b)
scala> exec(2, 3)(_ > _)
res1: Boolean = false
If you want to execute a function with parameters in your exec method you need to:
scala> def exec(f: => Unit) = {
| println("Exec:")
| f
| }
scala> def foo(f : (Int, Int)): Unit = println(f._1 + f._2)
scala> exec(foo((3, 4)))
Exec:
7
because foo((3, 4)) type is => Unit
Not quite an answer to your original question (and too big to be a comment), but if you're looking to write a tasteful, nice-looking execution operator and don't particularly like the syntax of the provided answers, perhaps something similar to scalaz's pipe operator (|>) might be what you're thinking of?
scala> // You could just get this from scalaz with import scalaz._ and import Scalaz._
scala> implicit class FancyExec[A](x: A){def |>[B](f: A => B) = f(x)}
scala> 5 |> (_ + 6)
res1: Int = 11
scala> (5, 4) |> ((_: Int) < (_: Int)).tupled
res2: Boolean = false
scala> val f = ((_: Int) < (_: Int)).tupled
f: ((Int, Int)) => Boolean = <function1>
scala> val g = ((_: Int) > (_: Int)).tupled
g: ((Int, Int)) => Boolean = <function1>
scala> List(f, g) map ((5, 4) |> _)
res3: List[Boolean] = List(false, true)

scala : it is impossible to put a tuple as a function's argument

I can't pass a tuple as a method parameter:
scala> val c:Stream[(Int,Int,Int)]= Stream.iterate((1, 0, 1))((a:Int,b:Int,c:Int) => (b,c,a+b))
<console>:11: error: type mismatch;
found : (Int, Int, Int) => (Int, Int, Int)
required: ((Int, Int, Int)) => (Int, Int, Int)
thanks.
Just as the function literal:
(x:Int) => x + 1
is a function of one argument, the following
(x:Int, y: Int, z: Int) => x + y + z
is a function of three arguments, not one argument of a 3tuple
You can make this work neatly using a case statement:
scala> val c: Stream[(Int,Int,Int)] =
Stream.iterate((1, 0, 1)){ case (a, b, c) => (b, c, a+b) }
c: Stream[(Int, Int, Int)] = Stream((1,0,1), ?)
An alternative is to pass the tuple, but that's really ugly due to all the _1 accessors:
scala> val c:Stream[(Int,Int,Int)] =
Stream.iterate((1, 0, 1))( t => (t._2, t._3, t._1 + t._2) )
c: Stream[(Int, Int, Int)] = Stream((1,0,1), ?)
The lambda (a:Int,b:Int,c:Int) => (b,c,a+b) is a function taking three arguments. You want it to take one tuple, so you can write ((a:Int,b:Int,c:Int)) => (b,c,a+b). But this gives an error!
error: not a legal formal parameter.
Note: Tuples cannot be directly destructured in method or function parameters.
Either create a single parameter accepting the Tuple3,
or consider a pattern matching anonymous function: `{ case (param1, ..., param3) => ... }
(((a:Int,b:Int,c:Int)) => (b,c,a+b))
^
Luckily, the error suggests a solution: { case (a, b, c) => (b, c, a+b) }
It is also possible to use the tupled option:
def a = (x:Int, y: Int, z: Int) => x + y + z).tupled
val sum = a((1,2,3))
My guess is that the expression (a:Int,b:Int,c:Int) => (b,c,a+b) defines a lambda with three arguments, and you need one decomposed argument.

a shortcut to extract a tuple in a function paramenter in scala

val list = List((1,2), (3,4))
list.map(tuple => {
val (a, b) = tuple
do_something(a,b)
})
// the previous can be shortened as follows
list.map{ case(a, b) =>
do_something(a,b)
}
// similarly, how can I shorten this (and avoid declaring the 'tuple' variable)?
def f(tuple: (Int, Int)) {
val (a, b) = tuple
do_something(a,b)
}
// here there two ways, but still not very short,
// and I could avoid declaring the 'tuple' variable
def f(tuple: (Int, Int)) {
tuple match {
case (a, b) => do_something(a,b)
}
}
def f(tuple: (Int, Int)): Unit = tuple match {
case (a, b) => do_something(a,b)
}
Use tupled
scala> def doSomething = (a: Int, b: Int) => a + b
doSomething: (Int, Int) => Int
scala> doSomething.tupled((1, 2))
res0: Int = 3
scala> def f(tuple: (Int, Int)) = doSomething.tupled(tuple)
f: (tuple: (Int, Int))Int
scala> f((1,2))
res1: Int = 3
scala> f(1,2) // this is due to scala auto-tupling
res2: Int = 3
tupled is defined for every FunctionN with N >= 2, and returns a function expecting the parameters wrapped in a tuple.
While this might look like a trivial suggestion, the f function, can be further simplified by just using _1 and _2 on a tuple.
def f(tuple: (Int, Int)): Unit =
do_something(tuple._1, tuple._2)
Obviously by doing this you're affecting readability (some meta-information about the meaning of the 1st and 2nd parameter of the tuple is removed) and should you wish to use elements of the tuple somewhere else in the f method you will need to extract them again.
Though for many uses this might be still the easiest, shortest and most intuitive alternative.
If I understand correctly you are trying to pass a tuple to a method with 2 args?
def f(tuple: (Int,Int)) = do_something(tuple._1, tuple._2)
by more readable, I mean giving variable names instead of using the _1 an _2 on the tuple
In this case, it's a good idea to use a case class instead of a tuple, especially since it only takes one line:
case class IntPair(a: Int, b: Int)
def f(pair: IntPair) = do_something(pair.a, pair.b)
If you get (Int, Int) from external code which can't be changed (or you don't want to change), you could add a method converting from a tuple to IntPair.
Another option: {(a: Int, b: Int) => a + b}.tupled.apply(tuple). Unfortunately, {case (a: Int, b: Int) => a + b}.apply(tuple) doesn't work.

How does a "case" anonymous function really work in Scala?

Dеar Scala,
scala> val f1: ((Int, Int)) => Int = { case (a, b) => a + b }
f1: ((Int, Int)) => Int = <function1>
scala> val f2: (Int, Int) => Int = { case (a, b) => a + b }
f2: (Int, Int) => Int = <function2>
huh?!
scala> f1(1, 2)
res2: Int = 3
Ok...
scala> def takesIntInt2Int(fun: (Int, Int) => Int) = fun(100, 200)
takesIntInt2Int: (fun: (Int, Int) => Int)Int
scala> def takesTuple2Int(fun: ((Int, Int)) => Int) = fun(100, 200)
takesTuple2Int: (fun: ((Int, Int)) => Int)Int
scala> takesIntInt2Int(f2)
res4: Int = 300
scala> takesIntInt2Int(f1)
<console>:10: error: type mismatch;
found : ((Int, Int)) => Int
required: (Int, Int) => Int
takesIntInt2Int(f1)
^
scala> takesTuple2Int(f1)
res6: Int = 300
scala> takesTuple2Int(f2)
<console>:10: error: type mismatch;
found : (Int, Int) => Int
required: ((Int, Int)) => Int
takesTuple2Int(f2)
Right. And now, look at this!
scala> takesTuple2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
found : (T1, T2, T3)
required: (Int, Int)
takesTuple2Int { case (a, b, c) => a + b + c }
^
scala> takesIntInt2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
found : (T1, T2, T3)
required: (Int, Int)
takesIntInt2Int { case (a, b, c) => a + b + c }
Like, srsly? o_O Both result in required: (Int, Int) error.
Why then use case at all in such anonymous functions?
See section 8.5 of the Scala reference (http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf). The expression { case (a, b) => a + b } is interpreted differently based on the expected type. In your definition of f1 it created a PartialFunction[(Int, Int), Int] which was cast to a Function1[(Int, Int), Int], i.e. ((Int, Int)) => Int whereas in the definition of f2 it created a Function2[Int, Int, Int], i.e. (Int, Int) => Int.
These two interpretations relate to the two situations where you would commonly use case in an anonymous function.
One is for writing anonymous functions that accept tuples and work on their components, as you did with f1. An example would be the function you pass to the foreach or map method on a Map, e.g. Map(1 -> 2, 3 -> 4) map { case (k, v) => k + v }.
The second is for writing an anonymous function that does a match on its sole parameter. Your f2 is doing this, but not in any useful way. An example would be the anonymous function passed to collect, e.g. List(1, -2, 3) collect { case x if x > 0 => -x }.
Note that the two can be combined, that is functions like f1 can do complex matching as well. For example, Map(1 -> 2, 3 -> 4) collect { case (k, v) if k < 2 => v }.
Edit: res2 works because of tupling. If an application doesn't type check, the compiler will try wrapping the args in a tuple before failing.
But that is tried just for applications; it's not a general conversion, as you discovered. It will not try to upgrade a value Function2[A, B, C] to Function1[(A, B), C].

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)