Scala Syntax Partial - scala

When I create a partial function, why can't I immediately invoke it?
Both res6 and res8 are the same type (function1) so I'm not sure how come res7 works (immediately invoking it) and what would be res9 fails
scala> ((x: Int) => x + 1)
res6: Int => Int = <function1>
scala> ((x: Int) => x + 1)(1)
res7: Int = 2
scala> def adder(a: Int, b: Int) = a + b
adder: (a: Int, b: Int)Int
scala> adder(1, _: Int)
res8: Int => Int = <function1>
scala> adder(1, _: Int)(1)
<console>:12: error: Int does not take parameters
adder(1, _: Int)(1)
^
scala> (adder(1, _: Int))(1)
res10: Int = 2

I think you've just found one of the little Scala compiler quirks.
I don't know how this case is implemented exactly in the parser, but from the looks of it Scala thinks you are invoking a function with multiple parameter lists (of the form f(...)(...)). Thus you need to explicitly surround the partially applied function with brackets here, so that compiler could disambiguate between f()() and f(_)() forms. In res8 you don't have any parameters following the function, so there is no ambiguity. Otherwise, Scala would require you to follow the function with underscore if you ommit parameter list: f _ or f() _.
You can still immediately apply that function as you show in res10.

adder(1, _: Int)
This is caused by scala compiler will expand it to:
((x: Int) => adder(1, x)(1))
and this is caused by the compiler can't infer the wildcard's context, like:
_ + _
scala> _ + _
<console>:17: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
_ + _
^
<console>:17: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
_ + _
so you can do it with context bounds, as your way:
(adder(1, _: Int))(1)
it will be expanded like:
((x: Int) => adder(1, x))(1)

Related

Counterintuitive suggestion in the "missing argument list"-error message for methods with multiple argument lists?

Consider the following code snippet:
def foo(a: Int)(b: Int) = a + b
foo
It does not compile, and produces the following error message:
error: missing argument list for method foo
Unapplied methods are only converted to functions when
a function type is expected.
You can make this conversion explicit by writing
`foo _` or `foo(_)(_)` instead of `foo`.
The foo _ hint works. But if I write the expression
foo(_)(_)
as suggested by the previous error message, I get a new error message:
error: missing parameter type for expanded
function ((x$1: <error>, x$2: <error>) => foo(x$1)(x$2))
This seems rather counterintuitive.
Under what circumstances is the foo(_)(_)-hint supposed to be helpful, what exactly does it tell me?
(noise removed; the more I kept editing the question, the less sense did it make; Kolmar is right)
The type of foo(_)(_) is (Int, Int) => Int. So it works if you specify that type or use it in the context that expects this type:
scala> foo(_: Int)(_: Int)
res1: (Int, Int) => Int = $$Lambda$1120/1321433666#798b36fd
scala> val f: (Int, Int) => Int = foo(_)(_)
f: (Int, Int) => Int = $$Lambda$1121/1281445260#2ae4c424
scala> def bar(f: (Int, Int) => Int): Int = f(10, 20)
bar: (f: (Int, Int) => Int)Int
scala> bar(foo(_)(_))
res2: Int = 30

Partially applied function doesn't work in Scala

Why I can't invoke g2 with the second parameter in the line g2(1, _: Int)(9)?
g2(1, _: Int) returns a function <function1> and then I invoke that <function1> with parameter. At the same time in the example above I can invoke f3(9) however it is the same as g2(1, _: Int)(9) IMHO.
// OK
def f1: (Int, Int) => Int = _ + _
val f2: (Int, Int) => Int = f1
val f3: (Int) => Int = f2(1, _: Int)
println {
f3(9)
}
// FAIL
def g1: (Int, Int) => Int = _ + _
val g2: (Int, Int) => Int = g1
println {
g2(1, _: Int)(9) // Error: 'g2' does not take parameters
}
It's the issue with the scope of underscore in anonymous functions. See here for the rules: What are the rules to govern underscore to define anonymous function?
According to the rule 1 from the link above g2(1, _: Int)(9) is interpreted as (x: Int) => g2(1, x)(9). So you need to add parentheses, for the rule 2 to start working: (g2(1, _: Int))(9)
As you add in a comment, (g2(1, _: Int))(9) works.
Scala has multiple parameter lists, so f(x)(y) parses differently from (f(x))(y). As g takes only one parameter list, it can't take the second parameter list, hence the explicit grouping is necessary.
g2(1, _: Int)(9) means: take the function g2(x, y)(z), and apply parameters x = 1, z = 9.
(g2(1, _: Int))(9) means: take the function g2(x, y), and apply parameter x = 1. Then apply y = 9 to the resulting function.

Curried function in scala

I have a definition of next methods:
def add1(x: Int, y: Int) = x + y
def add2(x: Int)(y: Int) = x + y
the second one is curried version of first one. Then if I want to partially apply second function I have to write val res2 = add2(2) _. Everything is fine. Next I want add1 function to be curried. I write
val curriedAdd = (add1 _).curried
Am I right that curriedAdd is similiar to add2?
But when I try to partially apply curriedAdd in a such way val resCurried = curriedAdd(4) _ I get a compilation error. Then I fix it to
val resCurried = curriedAdd(4)
Why the result of a Functions.curried differs from curried version of add function(from add2)?
Firstly curriedAdd is same as add2 _ and not add2. add2 is just a method.
scala> curriedAdd
res52: Int => (Int => Int) = <function1>
scala> add2 _
res53: Int => (Int => Int) = <function1>
About the second question. I think the below is the reason. Doing
scala> val i = curriedAdd(23)
i: Int => Int = <function1>
scala> i _
res54: () => Int => Int = <function0>
scala> curriedAdd(23) _
<console>:10: error: _ must follow method; cannot follow Int => Int
curriedAdd(23) _
curriedAdd(23) _ does not work. Lets look at scala manual (§6.7)-
The expression e _ is well-formed if e is of method type or if e is a
call-by-name parameter. If e is a method with parameters, e _
represents e converted to a function type by eta expansion (§6.26.5).
If e is a parameterless method or call-by-name parameter of type =>T ,
e _ represents the function of type () => T , which evaluates e when
it is applied to the empty parameterlist ().
Remember it only evaluates if it is a method or call-by-name parameter. In curriedAdd(23) _, it does not evaluate curriedAdd(23) but checks if it is a method or call-by-name. It is not a method nor a call-by-name parameter.
It is not by-name because by-name is the property of variable. Above you get a by-name parameter after evaluating curriedAdd(23) but curriedAdd(23) in itself is not a by-name variable. Hence the error (Ideally the compiler should have coverted it). Note that the below works:
scala> curriedAdd(23)
res80: Int => Int = <function1>
scala> res80 _
res81: () => Int => Int = <function0>
The above works because res80 _, here you are applying _ to a call-by-name parameter and hence does the conversion.
To answer this question, let's take a look at the REPL.
First we define the two functions as you did.
scala> def add1(x: Int, y: Int) = x + y
add1: (x: Int, y: Int)Int
scala> def add2(x: Int)(y: Int) = x + y
add2: (x: Int)(y: Int)Int
We have defined two functions. The first one expects two parameters in one parameterlist. The second one expects two parameters, each one in an own parameterlist. The result type is the same.
Let's move on.
scala> val curriedAdd = (add1 _).curried
curriedAdd: Int => (Int => Int) = <function1>
You just created a partial applied function, that expects one parameter and returns a partial applied function of type Int => Int. This is not as similar to add2 as you expect it to be.
To achieve the same for add2, you would need to call
scala> val curriedAdd2 = add2 _
curriedAdd2: Int => (Int => Int) = <function1>

How does the type inferencer work on reduceLeft?

Further to my other question about reduceLeft, the signature of reduceLeft on Seq is
def reduceLeft [B >: A] (f: (B, A) ⇒ B): B
and we can call it with expressions such as
List(1,2,3,4) reduceLeft (_ + _)
In this example A is Int, so reduceLeft expects a Function2[B >: Int, Int, B]. Regardless of how reduceLeft works (which is irrelevant), how does the type inferencer know that B has a + method, when it could be of type Any?
I think the section 6.26.4 Local Type Inference of the spec sort of explains what's going on. The compiler will look for an optimal type. When the type parameter is contravariant the type chosen will be maximal (in this case Any) and otherwise (invariant or covariant) minimal (in this case Int).
There are a couple examples which I can't really relate to reduceLeft.
What I did notice is the inference seems to happen before looking at the anonymous function passed:
scala> List(1,2).reduceLeft[Any](_.toString + _)
res26: Any = 12
But If I don't help the type inferencer:
scala> List(1,2).reduceLeft(_.toString + _)
<console>:8: error: type mismatch;
found : java.lang.String
required: Int
List(1,2).reduceLeft(_.toString + _)
Edit, I'm wrong the anonymous function is taken into account, this works:
List(1,2).reduceLeft((_:Any).toString + (_:Any).toString)
There is a compiler -Ytyper-debug option that you can run on:
List(1,2).reduceLeft(_+_)
It will show you that somehow the compiler assumes the expected type of the anonymous function is (Int, Int) => Int, then it proceeds to check the _ + _ against it and succeeds and then infers B as Int. Snippet here:
typed immutable.this.List.apply[Int](1, 2).reduceLeft: [B >: Int](f: (B, Int) => B)B
adapted immutable.this.List.apply[Int](1, 2).reduceLeft: [B >: Int](f: (B, Int) => B)B to ?, undetparams=type B
typing ((x$1, x$2) => x$1.$plus(x$2)): pt = (Int, Int) => Int: undetparams=,
// some time later
typed ((x$1: Int, x$2: Int) => x$1.+(x$2)): (Int, Int) => Int
adapted ((x$1: Int, x$2: Int) => x$1.+(x$2)): (Int, Int) => Int to (Int, Int) => Int,
typed immutable.this.List.apply[Int](1, 2).reduceLeft[Int](((x$1: Int, x$2: Int) => x$1.+(x$2))): Int
I don't know why in absence of type ascription the anonymous function is assumed to be (Int, Int) => Int.
If B >: X and the compiler knows X but cannot resolve B it simply assumes B = X.
It is somewhat practical since it only has two options for B and only one is known. So absent knowing which super class it assumes that B is X. You can test the compilers decision making process with the following code.
class Y {
def bar(y:Y) = this
}
case class X( i: Int ) extends Y {
def foo(x:X)=X(i+x.i)
}
val t = new Y bar X(7)
val t2 = X(8) bar X(7)
val res = List(X(1),X(2),X(3)) reduceLeft { _ foo _ }
val res2 = List(X(1),X(2),X(3)) reduceLeft { _ bar _ } // will not compile

In Scala, what is the difference between using the `_` and using a named identifier?

Why do i get an error when I try using _ instead of using a named identifier?
scala> res0
res25: List[Int] = List(1, 2, 3, 4, 5)
scala> res0.map(_=>"item "+_.toString)
<console>:6: error: missing parameter type for expanded function ((x$2) => "item
".$plus(x$2.toString))
res0.map(_=>"item "+_.toString)
^
scala> res0.map(i=>"item "+i.toString)
res29: List[java.lang.String] = List(item 1, item 2, item 3, item 4, item 5)
Underscores used in place of variable names like that are special; the Nth underscore means the Nth argument to an anonymous function. So the following are equivalent:
List(1, 2, 3).map(x => x + 1)
List(1, 2, 3).map(_ + 1)
But, if you do this:
List(1, 2, 3).map(_ => _ + 1)
Then you are mapping the list with a function that ignores its single argument and returns the function defined by _ + 1. (This specific example won't compile because the compiler can't infer what type the second underscore has.) An equivalent example with named parameters would look like:
List(1, 2, 3).map(x => { y => y + 1 })
In short, using underscores in a function's argument list means "I am ignoring these arguments in the body of this function." Using them in the body means "Compiler, please generate an argument list for me." The two usages don't mix very well.
To complement the other answers, here are some examples showing why you get the "missing parameter type" in some cases when using '_' as a placeholder parameter.
Scala's type inference considers the 'expected' type of an expression based on its context. If there is no context, it cannot infer the type of the parameters. Notice in the error message the first and second instances of _ are replaced with the compiler generated identifiers x$1 and x$2.
scala> _ + _
<console>:5: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
_ + _
^
<console>:5: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
_ + _
^
Adding a type ascription to the entire expression provides enough context to help the inferencer:
scala> (_ + _) : ((Int, Int) => Int)
res3: (Int, Int) => Int = <function2>
Alternatively, you can add a type ascription to each parameter placeholder:
scala> (_: Int) + (_: Int)
res4: (Int, Int) => Int = <function2>
In the function call below with type arguments provided, the context is unambigous and the function type is inferred.
scala> def bar[A, R](a1: A, a2: A, f: (A, A) => R) = f(a1, a2)
bar: [A,R](a1: A,a2: A,f: (A, A) => R)R
scala> bar[Int, Int](1, 1, _ + _)
res5: Int = 2
However, if we ask the compiler to infer the type parameters, if fails:
scala> bar(1, 1, _ + _)
<console>:7: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
bar(1, 1, _ + _)
^
<console>:7: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
bar(1, 1, _ + _)
^
We can help it, though, by currying the parameter lists. Here, the arguments to the first parameter list (1, 1), tell the inference that the type parameter A should be Int. It then knows that the type of the argument f must be (Int, Int) => ?), and the return type R is inferred as Int, the result of integer addition. You will see the same approach used in Traversable.flatMap in the standard library.
scala> def foo[A, R](a1: A, a2: A)(f: (A, A) => R) = f(a1, a2)
foo: [A,R](a1: A,a2: A)(f: (A, A) => R)R
scala> foo[Int, Int](1, 1) { _ + _ }
res1: Int = 2
scala> foo(1, 1) { _ + _ }
res0: Int = 2
If you're not going to bind an identifier, just leave that part out.
res0.map("item "+_.toString)