The following:
val add = (a: Int, b: Int) => a + b
gets converted to:
object add extends Function2[Int, Int, Int] {
def apply(a: Int, b: Int) = a + b
}
while
val a1 = add(_: Int, 3)
gets converted to:
object a1 extends Function1[Int, Int] {
def apply(x: Int): Int = {
add(x, 3)
}
}
But when I do:
scala> val a2 = add _
a2: () => (Int, Int) => Int = <function0>
And then call a2, it throws an error:
scala> a2(1, 2)
<console>:11: error: too many arguments for method apply: ()(Int, Int) => Int in trait Function0
a2(1, 2)
^
Why is this? Why does the following work?
a2()(1, 2)
add is already a Function2[Int, Int, Int]. If you want a2 to have the same type, then a simple assignment will suffice.
scala> val a2 = add
a2: (Int, Int) => Int = <function2>
scala> a2(1, 2)
res3: Int = 3
What you're thinking of is eta-expansion of a method into a function. If we had:
def add(a: Int, b: Int): Int = a + b
Then, we would use add _ to get the eta-expansion to assign to a value.
scala> def a2 = add _
a2: (Int, Int) => Int
scala> a2(1, 2)
res4: Int = 3
But add is already a function, so the underscore has a different meaning. add is now a value and not a method. Since add is a value, it is like a parameter-less method that returns a Function2[Int, Int, Int]. And if we try to get the eta-expansion of that, we get () => Function2[Int, Int, Int].
Consider a simpler example where we have a simple val a = 1. a is essentially the same as a parameter-less method that returns 1 (def a = 1). If I try to obtain the eta-expansion, I will get () => Int.
scala> val a = 1
a: Int = 1
scala> val a2 = a _
a2: () => Int = <function0>
Related
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
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).
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)
How can I refer to one overload of a function? Does this require reflection?
----- Define two functions with the same signature
scala> def f( x:Int ) = x + 1
f: (x: Int)Int
scala> def g( x:Int ) = x + 2
g: (x: Int)Int
----- Define a function that returns one or the other
scala> def pick( a:Boolean ) = if (a) f _ else g _
pick: (a: Boolean)Int => Int
scala> pick(true)(0)
res24: Int = 1
scala> pick(false)(0)
res25: Int = 2
----- All good so far; now overload f to also take a String
scala> def f( x:String ) = x.toInt + 1
f: (x: String)Int
scala> def pick( a:Boolean ) = if (a) f _ else g _
pick: (a: Boolean)String with Int => Int
scala> pick(false)(0)
<console>:12: error: type mismatch;
found : Int(0)
required: String with Int
pick(false)(0)
^
I understand why this doesn't work. But how can I define pick to use the f that takes an Int, and ignore the f that takes a String?
Again, I don't want to write a function that calls either f or g. I want to write a function that returns f or g, which I can then call gazillions of times.
Just add a type annotation:
def pick( a:Boolean ) = if (a) f(_: Int) else g(_: Int)
Supplement: Don't be fooled by how the REPL constructs what it runs:
scala> :pa
// Entering paste mode (ctrl-D to finish)
object Foo {
def f(i: Int) = i.toString
def f(s: String) = s
def pick( a:Boolean ) = if (a) f _ else "nope"
}
// Exiting paste mode, now interpreting.
<console>:10: error: ambiguous reference to overloaded definition,
both method f in object Foo of type (s: String)String
and method f in object Foo of type (i: Int)String
match expected type ?
def pick( a:Boolean ) = if (a) f _ else "nope"
^
Using the REPL, the other answer to your question is, define the one you want last, because it becomes most specific:
scala> def f(s: String) = s
f: (s: String)String
scala> def f(i: Int) = i.toString
f: (i: Int)String
scala> f _
res0: Int => String = <function1>
After having discovered that currying multi parameter-groups method is possible, I am trying to get a partially applied function which requires implicit parameters.
It seams not possible to do so. If not could you explain me why ?
scala> def sum(a: Int)(implicit b: Int): Int = { a+b }
sum: (a: Int)(implicit b: Int)Int
scala> sum(3)(4)
res12: Int = 7
scala> val partFunc2 = sum _
<console>:8: error: could not find implicit value for parameter b: Int
val partFunc2 = sum _
^
I use a singleton object to create this partially applied function and I want to use it in a scope where the implicit int is defined.
That is because you don't have an implicit Int in scope. See:
scala> def foo(x: Int)(implicit y: Int) = x + y
foo: (x: Int)(implicit y: Int)Int
scala> foo _
<console>:9: error: could not find implicit value for parameter y: Int
foo _
^
scala> implicit val b = 2
b: Int = 2
scala> foo _
res1: Int => Int = <function1>
The implicit gets replaced with a real value by the compiler. If you curry the method the result is a function and functions can't have implicit parameters, so the compiler has to insert the value at the time you curry the method.
edit:
For your use case, why don't you try something like:
object Foo {
def partialSum(implicit x: Int) = sum(3)(x)
}
scala> object MySingleton {
| def sum(a: Int)(implicit b: Int): Int = { a+b }
|
|
| def caller(a: Int) = {
| implicit val b = 3; // This allows you to define the partial below
| def pf = sum _ // and call sum()() without repeating the arg list.
| pf.apply(a)
| }
| }
defined module MySingleton
scala> MySingleton.caller(10)
res10: Int = 13