Suppose I have two methods
scala> def a(a: Int, b: Int, c: Int) : Int = …
a: (a: Int, b: Int, c: Int)Int
scala> def b(i: Int) : Int = …
b: (i: Int)Int
How to define a method c, that is the composition of both ?
Unfortunately, the following code doen't compile :
def c = b(a)
You could convert method a to function and then use method andThen like this:
def a(a: Int, b: Int, c: Int) : Int = a + b + c
def b(i: Int) : Int = i * 2
val c = (a _).tupled andThen b
c(1, 1, 1)
// 6
Note that I have to convert function (Int, Int, Int) => Int to tupled version - ((Int, Int, Int)) => Int - here to use andThen. So result function c accepts Tuple3 as argument.
You could convert c to untupled version ((Int, Int, Int) => Int) using Function.untupled:
val untupledC = Function.untupled(c)
untupledC(1, 1, 1)
// 6
shapeless
There is no untupled method for function arity > 5.
You could also use shapeless toProduct/fromProduct methods for any arity like this:
import shapeless.ops.function._
import shapeless.ops.function._
val c = (a _).toProduct.andThen(b).fromProduct
Scalaz defines Functor instances for higher-arity functions, so you can just write
(a _).map(b)
Related
I'm trying to understand the difference between those 3. In particular their type.
val func0: (Int, Int, Int) => Int = _ + _ + _
//func0: (Int, Int, Int) => Int = <function>
def method0 (a: Int, b:Int, c:Int) = (_:Int) + (_:Int) + (_:Int)
//method0: (a: Int, b: Int, c: Int)(Int, Int, Int) => Int
def method1 (a: Int, b:Int, c:Int) = a + b + c
//method1: (a: Int, b: Int, c: Int)Int
Is there a rule that says that "_" can only be used in function body and not method body ?
Can't understand the type of method0, definitely not like func0 ....
Why is it different from method1 ?
method0 is a method that takes 3 parameters and return a function that takes a 3-tuples and return an int ? How is that possible ?
I have this scala code:
object C extends App{
def sum(a:Int,b:Int,c:Int)=a+b+c
//val d=sum
val d=sum _
println(d(1,2,3))
}
I have to write sum _,not sum
but In f#,I can write this:
let sum a b c=a+b+c
let d=sum
[<EntryPoint>]
let main argv =
printfn "%A" (d 1 2 3)
0 // return an integer exit code
why in scala I can't just write
val d=sum
Scala has
methods (by default def is a method) and
functions
And they are not same.
Taking java methods as example, you can not assign methods to a variable without evaluating the method. But you can do that with function and to achieve that in scala define sum as function.
scala> def sum: (Int, Int, Int) => Int = (a, b, c) => a + b + c
sum: (Int, Int, Int) => Int
scala> val sumVal = sum
sumVal: (Int, Int, Int) => Int = $$Lambda$912/0x0000000801667840#716f94c1
scala> sumVal(1, 2, 3)
res1: Int = 6
Longer version of defining a function is,
scala> def sum = new Function3[Int, Int, Int, Int] {
| def apply(a: Int, b: Int, c: Int): Int = a +b + c
| }
sum: (Int, Int, Int) => Int
scala> val sumVal = sum
sumVal: (Int, Int, Int) => Int = <function3>
scala> sumVal(1, 2, 3)
res2: Int = 6
I am having trouble understanding why I am unable to use a composed function and compose a new one. For eg: I have two functions f and g and I create a composed function composed1 from these. I tried to combine composed with fourth function lastOne and it fails.
I want to make two composed functions and for the second one Is there a way I can reuse the first composed function?
scala> def f(x: Int)(y: Int) = {
| x + y
| }
f: (x: Int)(y: Int)Int
scala> def g(a: Int)(b: Int) = {
| a + b
| }
g: (a: Int)(b: Int)Int
scala> def composed1(a: Int, b: Int) = {
| f(a) _ andThen g(b)
| }
composed1: (a: Int, b: Int)Int => Int
scala> composed1(2, 2)(5)
res1: Int = 9
scala> def lastOne(l: Int)(x: Int) = {
| l + x
| }
lastOne: (l: Int)(x: Int)Int
scala> def composed2(a: Int, b: Int, c: Int) = {
| composed1(a, b) _ andThen lastOne(c)
| }
<console>:14: error: _ must follow method; cannot follow Int => Int
composed1(a, b) _ andThen lastOne(c)
^
<console>:14: error: missing argument list for method lastOne
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `lastOne _` or `lastOne(_)(_)` instead of `lastOne`.
composed1(a, b) _ andThen lastOne(c)
When I use all of them together it works
scala> def test(x: Int, y: Int, z: Int) = {
| f(x) _ andThen g(y) _ andThen lastOne(z)
| }
test: (x: Int, y: Int, z: Int)Int => Int
scala> test(2, 2, 4)(5)
res9: Int = 13
f()() and g()(), as you define them, are methods. Methods are not functions but methods can be promoted to functions via "eta expansion". One way to do that is using the underscore in place of a passed parameter.
andThen() is a method on the Function trait that takes a function as an argument and returns a new function. It looks as if you can also use a method as the passed argument but it is silently being promoted to Function status.
So composed1() looks like a method but it is actually a Function, because that's what andThen() returns, and you can't apply the underscore eta expansion to a Function. It only works on methods.
As an experiment, turn f()() into a Function that does the same thing...
def f :Int => Int => Int = (x: Int) => (y: Int) => x + y
...now composed1() won't compile.
So, now that we know that composed1() is a Function, how do we get what we want from composed2()? Simple. Skip the underscore.
def composed2(a: Int, b: Int, c: Int) =
composed1(a, b) andThen lastOne(c)
composed2(2, 2, 4)(5) //res0: Int = 13
After experimenting with scala's eta expansion, I came across a weird feature.
Let's define a method:
scala> def sum(a: Int, b: Int): Int = a + b
sum: (a: Int, b: Int)Int
Ok, up until now, everything is fine. Now let's assign it to a val using eta expansion:
scala> val f = sum _
f: (Int, Int) => Int = $$Lambda$1051/694580932#55638165
Now, the strange thing is coming. I can apply eta expansion again to f, and it is working (however it adds currying to my method) :
scala> val g = f _
g: () => (Int, Int) => Int = $$Lambda$1055/1351568309#5602e540
Why is this working ? I thought that eta expansion was only valid for methods.
Moreover, I noticed that this is not possible:
scala> ((a: Int, b: Int) => a + b: Int) _
<console>:12: error: _ must follow method; cannot follow (Int, Int) => Int
((a: Int, b: Int) => a + b: Int) _
^
But is it not the same as applying eta expansion to f ?
I am a bit confused and these eta expansions still hide some magic for me.
Thanks a lot !
When you write val f = sum _ at the top level of the REPL or an object/class, Scala defines an accessor method so that you can access it. Here is how Scala desugars this (via scalac -Xprint:typer on val f: (Int, Int) => Int = _ + _):
private[this] val f: (Int, Int) => Int = ((x$1: Int, x$2: Int) => x$1.+(x$2));
<stable> <accessor> def f: (Int, Int) => Int = Foo.this.f;
So, when you subsequently write val g = f _, it's doing eta-expansion on the zero-argument accessor method, which results in the behavior you see. For more verification of this, notice that, if you put the definitions in a method, you get an error:
def foo = {
val f: (Int, Int) => Int = _ + _
val g = f _ // error: _ must follow method; cannot follow (Int, Int) => Int
}
This is because accessors are only generated for fields (and top-level REPL definitions, which are treated like fields).
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.