I want to get the sum of a seq of Integers in Scala.
In my mind I want to fold a plus over the integers like this:
val seqOfIntegers:Seq[Int] = Seq(1, 2, 3, 4, 5)
val sumOfIntegers = seqOfIntegers.reduce(+)
This isn't valid.
Instead I have to do:
val sumOfIntegers = seqOfIntegers.reduce(plus)
...
def plus(a:Integer, b:Integer): Integer = { a + b}
(I'm sure you could sugar that up - but my point is that the original plus symbol doesn't work as a function, and the errors messages don't make it clear why.)
My question is: Why can't I reduce(+) on a seq of integers in Scala?
That's because + is not a function (that is, an object of type Function2[Int, Int, Int], which is the same as (Int, Int) => Int). Instead, it's a method on Int, which takes another Int and returns an Int. That is, what you want to pass to reduce is
actually (a: Int, b: Int) => a.+(b), which can be sugared into (a: Int, b: Int) => a + b and then _ + _.
That is,
seq.reduce(_ + _)
would work as expected.
Making .reduce(+) work
If you want to pass + as argument, you have to define a value + that extends (Int, Int) => Int. For example:
object + extends ((Int, Int) => Int) { def apply(a: Int, b: Int): Int = a + b }
Seq(1,2,3,4,5).reduce(+)
// res0: Int = 15
or, additionally relying on eta-expansion:
def +(a: Int, b: Int) = a + b
Seq(1,2,3,4,5).reduce(+)
So, you can invoke .reduce(+) on sequences, you just need either a value + of the right type (Int, Int) => Int (a value, not a method), or you need a binary method + in scope, which takes two integers, and can be eta-expanded into (Int, Int) => Int.
On error messages
The error messages seem quite unambiguous. If I don't define + myself, and enter
Seq(1, 2).reduce(+)
into the console, then I get
error: not found: value +
because there is no value + anywhere. I don't know how the error message could be any clearer.
Related
I understand that when I do this:
val sum = (a: Int, b: Int) => a + b
Scala compiler turns it into this:
val sum = new Function2[Int, Int, Int] {
def apply(a: Int, b: Int): Int = a + b
}
So Function2 is a class made for Int based operations.We use [] to convey that operations in this class involves(input,output,body) only Int.
Isn't only one Int entry in [] sufficient to convey this. But we have 3 Int entries in [].
Nothing in Function2 says that it is "made for Int based operations".
Function2[X, Y, Z] is a class for generic functions that that take two inputs of type X and Y and return a Z.
Specifying a single Int is obviously not sufficient, because Function2 requires exactly three type parameters.
How would you otherwise be able to tell a difference between a
Function2[Int, Int, Int]
and a
Function2[Int, Double, String]
as for example in
val sum = (a: Int, b: Double) => a + " -> " + b
which is syntactic sugar for
val sum = new Function2[Int, Double, String] {
def apply(a: Int, b: Double) = a + " -> " + b
}
?
If you often use Function2[T, T, T] for the same type T, you can define a type alias:
type BinOp[T] = Function2[T, T, T]
or shorter:
type BinOp[T] = (T, T) => T
While trying to grasp Scala implicits, I ran into this type inference problem:
object Demo extends App {
def demo(f: (Int, Int) => Int) = ???
demo((a: Int) => 42)
demo((a) => 42) // <-- any tricks to make this compile?
implicit def f1Tof2(f: Int => Int): (Int, Int) => Int =
(a: Int, b: Int) => f.apply(a)
}
What's the reason behind the compiler's inability to infer the type correctly? Any tricks to make it work?
This isn't possible. When you call demo((a, b) => a + b) (for example), the compiler is already expecting an (Int, Int) => Int, so it will infer (a, b) => a + b as (Int, Int) => Int, since it has the correct shape.
However, when you call demo(a => 42), the compiler sees a Function1[?, Int] as the argument, with no indication as to what the parameter type is. Since demo expects a Function2, the only way this can compile is if the compiler can find an implicit conversion from the passed argument type to (Int, Int) => Int. But it can't, because it doesn't know the type it's converting from. It cannot just assume it will a Int => Int.
There are only two ways to make this work.
1.) Explicitly declare the parameter type of the anonymous function, like you have done already. This is the only way the implicit conversion can be applied.
demo((a: Int) => 42)
2.) Provide an overload for demo that accepts a Int => Int.
def demo(f: (Int, Int) => Int): Int = f(1, 2)
def demo(f: Int => Int): Int = demo((a, b) => f(a))
scala> demo(a => 42)
res3: Int = 42
I'm fairly new to Scala and functional programming in general so I'm having a bit trouble wrapping my head around the concept of partially applied functions and function currying. There's also a pretty high chance that I'm gonna mix up some terminology, so all corrections are appreciated.
Note: I'm using the Scala Play framework but this is more of a Scala problem than a Play problem.
Given a function like this
def create(id: Long, userId: Long, label: String)
I get the userId and label as a (Int, String) tuple and the id as a Long. Basically what I'm trying to do is passing the id and the tuple to the function at the same time.
Now, I've read that passing a tuple to a function can be done something like this
scala> def f(a: Int, b: String) = 0
f: (a: Int, b: String)Int
scala> (f _).tupled((2, "Hello"))
res0: Int = 0
and that it is also possible to partially apply a function like this
scala> def f(a: Int, b: String) = 0
f: (a: Int, b: String)Int
scala> val ff = f(_: Int, "Hello")
ff: Int => Int = <function1>
scala> ff(2)
res1: Int = 0
So my initial idea was to combine these two concepts something like this
scala> def g(a: Long, b: Int, c: String) = 0
g: (a: Long, b: Int, c: String)Int
scala> val h = g(1, _: Int, _: String)
h: (Int, String) => Int = <function2>
scala> (h _).tupled((2, "Hello"))
However this results in an error
<console>:10: error: _ must follow method; cannot follow h.type
(h _).tupled((1, "Hello"))
^
So my question is first of all why doesn't this work because to me this makes sense. And secondly how would I go about achieving this effect?
Thanks for your help!
Just don't abstract it twice: don't do redundand underscore to the h as it's already a function after partial-applying:
scala> def create(a: Long, b: Int, c: String) = 0
create: (a: Long, b: Int, c: String)Int
scala> val h = create(1, _: Int, _: String)
h: (Int, String) => Int = <function2>
scala> h.tupled((1, "Hello"))
res0: Int = 0
More deeply, tupled is defined on functions (means object of Function, like Int => String), not on methods (like def f(i: Int): String) - so sometimes you need to convert a method to the function - it's called eta-expansion (or eta-abstraction more generally). When you do partial applying - eta-expansion is done automatically (you already have a (Int, String) => Int) - so you don't have to do it twice.
See The differences between underscore usage in these scala's methods, Difference between method and function in Scala for more information.
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.
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)