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
Related
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 want to define the following two Scala functions:
def Pr(
f: Int => Int,
g: Tuple3[Int, Int, Int] => Int
): Tuple2[Int, Int] => Int = {
def recurse(x: Int, counter: Int): Int = counter match {
case 0 => f(x)
case y => g(x, y-1, recurse(x, y-1))
}
(recurse _).tupled
}
def Pr(
f: Tuple2[Int, Int] => Int,
g: Tuple4[Int, Int, Int, Int] => Int
): Tuple3[Int, Int, Int] => Int = {
def recurse(x1: Int, x2: Int, counter: Int): Int = counter match {
case 0 => f(x1, x2)
case y => g(x1, x2, y-1, recurse(x1, x2, y-1))
}
(recurse _).tupled
}
The basic idea here is that if f is n-ary and g is (n+2)-ary, then Pr(f,g) is (n+1)-ary. The problem, of course, is type erasure. After type erasure f and g are both Function1's, and the two Pr functions are indistinguishable. I thought creating a single Pr function with type tags might help here, but I'm not sure how to make the return type change in each case.
Open to any suggestions; ones that can solve this without making use of additional libraries (Shapeless, Scalaz) are preferred.
For the other 5%, the standard idiom (that predates magnetization) is to add a dummy implicit parameter list:
def Pr(
f: Tuple2[Int, Int] => Int,
g: Tuple4[Int, Int, Int, Int] => Int
)(implicit dummy: DummyImplicit): Tuple3[Int, Int, Int] => Int = {
def recurse(x1: Int, x2: Int, counter: Int): Int = counter match {
case 0 => f(x1, x2)
case y => g(x1, x2, y-1, recurse(x1, x2, y-1))
}
(recurse _).tupled
}
There's surely a gazillion Q&As about that.
Scala overloading only considers the first param list; the dummy param is to disambiguate the erased signature.
This is modulo overloading is evil and Travis Brown, whose blog is about doing impossible things in Scala you probably shouldn't do, says it's probably not a good thing. Full disclosure, tl;dr.
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>
I want to define a function f that takes another function g. We require g to take take n Doubles (for some fixed n) and return a Double. The function call f(g) should return the specific value of n.
For example, f(Math.max) = 2 since Math.sin has type (Double, Double) => Double, and f(Math.sin) = 1 since Math.sin has type Double => Double.
How can I define f using Scala generics?
I've tried several forms without success. For example:
def f[A <: Product](g: Product => Double) = {...}
This doesn't work since we cannot extract the value of n at compile time, and cannot constrain the A to contain only Double values.
There is a pattern called Magnet Pattern, created by the Spray team. It does exectly what you want
This was a good excuse for me to look into Shapeless, something I always wanted to do at some point :)
$ git clone git#github.com:milessabin/shapeless.git
...
$ cd shapeless
(1)
Shapeless provides some abstractions over arity, and especially the representation as heterogeneous list (HList). A function of arbitrary arity can be seen as FnHList (a function that takes an HList as argument).
$ sbt shapeless-core/console
scala> import shapeless._
import shapeless._
scala> def isFunction[A](fun: A)(implicit fnh: FnHLister[A]) {}
isFunction: [A](fun: A)(implicit fnh: shapeless.FnHLister[A])Unit
scala> isFunction(math.sqrt _)
scala> isFunction(math.random _)
(2)
Now let's require that the function returns a Double:
scala> def isFunReturningDouble[A](fun: A)(implicit fnh: FnHLister[A] { type Result = Double }) {}
isFunReturningDouble: [A](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double})Unit
scala> isFunReturningDouble(math.sqrt _)
scala> isFunReturningDouble(math.signum _)
<console>:12: error: could not find implicit value for parameter fnh: shapeless.FnHLister[Int => Int]{type Result = Double}
isFunReturningDouble(math.signum _)
^
(3)
The LUBConstraint type class can witness the upper bound of the argument list:
scala> def isValidFun[A, B <: HList](fun: A)(implicit fnh: FnHLister[A] { type Result = Double; type Args = B }, lub: LUBConstraint[B, Double]) {}
isValidFun: [A, B <: shapeless.HList](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double; type Args = B}, implicit lub: shapeless.LUBConstraint[B,Double])Unit
scala> isValidFun(math.random _)
scala> isValidFun((i: Int) => i.toDouble)
<console>:12: error: could not find implicit value for parameter lub: shapeless.LUBConstraint[B,Double]
isValidFun((i: Int) => i.toDouble)
^
(4)
Now we still need to extract the arity somehow. On the type level this would be Length which is provided for HList. To get a runtime value, another type class ToInt is needed.
Here is the final function:
import shapeless._
def doubleFunArity[A, B <: HList, C <: Nat](fun: A)(implicit
fnh: FnHLister[A] { type Result = Double; type Args = B },
lub: LUBConstraint[B, Double],
len: Length[B] { type Out = C },
res: ToInt[C]
): Int = res()
Test:
scala> doubleFunArity(math.sqrt _)
res15: Int = 1
scala> doubleFunArity(math.random _)
res16: Int = 0
scala> val g: (Double, Double) => Double = math.max _
g: (Double, Double) => Double = <function2>
scala> doubleFunArity(g)
res17: Int = 2
Note that unfortunately many math operations are overloaded, and without strong type constraint, Scala will not give you the Double version automatically, but will use the Int version for some reason:
scala> math.max _
res18: (Int, Int) => Int = <function2>
So I need the indirection math.max _: ((Double, Double) => Double) to make this work.
Not saying that this is the best way to do it in your concrete case, but I think it was a fun exploration.
Probably the easiest solution is to use overloading as
def f(g: () => Double) = 0;
def f(g: (Double) => Double) = 1;
def f(g: (Double, Double) => Double) = 2;
def f(g: (Double, Double, Double) => Double) = 2;
// ...
println(f(Math.pow _));
println(f(Math.sin _));
(You can't check function argument/return types at run time due to type erasure, so I believe you can't create a fully generic function that would satisfy your requirements.)
I have an array of functions. How can I get the names to print in a println() function? In the code below I just get this output:
<function2>
<function2>
<function2>
Assume that in my real code I have a lot more functions with more descriptive names.
def printNames() {
def f1(x: Int, y: Int): Int = x + y
def f2(x: Int, y: Int): Int = x - y
def f3(x: Int, y: Int): Int = x * y
val fnList = Array(f1 _, f2 _, f3 _)
for (f <- fnList) {
println(f.toString());
}
}
Functions in Scala don't have descriptive names any more than Ints or Lists have descriptive names; you could make a case for toString giving a representation of its value, but that wouldn't be a name.
You could, however, extend Function2 thus:
object f1 extends Function2[Int, Int, Int] {
def apply(a: Int, b: Int) = a + b
override def toString = "f1"
}
which will act as you want.
Or more generally
class NamedFunction2[T1,T2,R](name: String, f: Function2[T1,T2,R])
extends Function2[T1,T2,R] {
def apply(a: T1, b: T2): R = f.apply(a, b)
override def toString = name
}
then use as
val f1 = new NamedFunction2[Int, Int, Int]("f1", _ + _)
etc.
You can't; the name is lost during conversion from a method to a Function2.