(1) Having defined two functions in the Scala REPL:
scala> def f(s: String) = "f(" + s + ")"
f: (s: String)String
scala> def g(s: String) = "g(" + s + ")"
g: (s: String)String
(2) Composing them without brackets works as expected:
scala> f _ compose g _
res18: String => String = <function1>
(3) Composing them with brackets doesn't:
scala> f(_).compose(g(_))
<console>:14: error: missing parameter type for expanded function ((x$1) => f(x$1).compose(((x$2) => g(x$2))))
f(_).compose(g(_))
^
<console>:14: error: missing parameter type for expanded function ((x$2) => g(x$2))
f(_).compose(g(_))
^
<console>:14: error: type mismatch;
found : String
required: Int
f(_).compose(g(_))
^
Question 1: Can somebody explain why?
Question 2: Why the type mismatch? Why is Scala expecting an Int at all?
(4) Surrounding f(_) with brackets seems to help a little bit, by making the first two errors go away:
scala> (f(_)).compose(g(_))
<console>:14: error: missing parameter type for expanded function ((x$2) => g(x$2))
(f(_)).compose(g(_))
^
Question 3: Why do these brackets help?
Question 4: Why does Scala need the parameter types, even though they are clearly defined in f and g respectively?
(5) Finally, adding the parameter type makes it work:
scala> (f(_)).compose(g(_:String))
res22: String => String = <function1>
Could you please explain what's going on, and provide alternative syntaxes to achieve the composition?
Thanks.
You can see the (unexpected) expansion using magic show comment:
scala> f(_).compose(g(_)) // show
[snip]
val res0 = ((x$1) => f(x$1).compose(((x$2) => g(x$2))))
Function literals need the params constrained, as you showed. f _ is eta expansion, which is different from f(_) which is sugar for x => f(x).
Since the unintended application f(x$1) returns a string, which is a Int => Char for indexing, you get the added type mismatch.
Underscore is covered by many SO questions, including one canonical.
Related
I came across the following scala codeļ¼
val x = 10 match {
case _: Int => data: Any => println(data) // no issue
}
val y = data: Any => println(data) //compiling error
In the match case clause, I don't have to write data: Any => println(data) as
(data: Any) => println(data)
But in val y = data: Any => println(data), I have to write as val y = (data: Any) => println(data). Why?
The relevant rule is
If an anonymous function (x: T) => e with a single typed parameter appears as the result expression of a block, it can be abbreviated to x: T => e.
The first case (case _: Int => data: Any => println(data)) satisfies this condition, the second one doesn't. But you can rewrite it to
val y = { data: Any => println(data) }
The Difference in one is case statement with pattern matching another is assignment statement. In the first case we are essentially returning the value(result) of the right hand side of => as statement block as shown below.
scala> {data: Any => println(data)}
res0: Any => Unit = $$Lambda$1141/1699449247#71468613
scala> data: Any => println(data)
<console>:1: error: ';' expected but '=>' found.
data: Any => println(data)
scala> val x = 10 match {
| case _: Int => val y = 56;data: Any => println(data) // no issue
| }
x: Any => Unit = $$Lambda$1171/115016870#8f374de
The above statements considers everything after => will be considered as a statement block in case statement, but in the second case ( assignment - ie., assigning a function value ) it is not. Hence brackets around parameter declaration are necessary.
EDIT:
Though I have given the above answer as per my observation in scala REPL, I further thought
that it will not clearly answer the Why? part of the question asked. I further did some more
trails on how scala compiler behaves after investigating the exact error message we get when we type
val y = data:Int => println(data)
scala> val y = data:Int => println(data)
<console>:1: error: ';' expected but '(' found.
val y = data:Int => println(data)
This seems to be due to the scala's feature of type inference of the val y.
In Scala type can be specified both explicitly wherever it is required and Scala compiler can implicitly infer type wherever it can. Here the type of y is to be inferred by the scala compiler in the first instance because it was not explicitly declared here. It is due to this attempt of the compiler the above error message occurred.
Now if we want to explicitly declare the type there are two ways in scala:
First way : val y : Int = 5
Second way : val y = 5:Int
Both of the above assignment statements are valid in scala.
Because in our specific assignment statement ie.,
val y = data:Any => println(data)
we are forcing scala to infer the type of y in the Second way shown above.
To infer the type of y, the scala compiler is attempting as explained below:
Here the compiler is assuming that data is a function defined somewhere before this line of code and the function is of type Int=>println(data). Then it is checking the validity of this type. The compiler determined that Int is a valid type but println(data) is not, because the valid type names cannot contain parentheses('(' char) and that is why the error message as above.
If we insert the right hand side of the above assignment statement in a block ie; between curly
braces or putting the parameter declaration within parentheses, there will be no problem in inferring the type of y.
Or Using the First way, we can do this to compile without curly braces,
val y : Any => Unit = data =>println(data)
scala> val y : Any => Unit = data =>println(data)
y: Any => Unit = $$Lambda$1059/1854873748#799c8758
Hope this explains the why part of the question.
I have a function map which takes a Parser and a function defined as follows:
def map[T1, T2](parser: Parser[T1], func: T1 => T2): Parser[T2]
I've created a Parser object of type [(Char, Char)] and a function (Char, Char) => String.
val parser[(Char,Char)] = //...
val asString: (Char, Char) => String = (a: Char, b: Char) => a.toString + b.toString
And then I pass these two to the map function.
val mParser: Parser[String] = map(parser, asString)
I expect everything to work fine but I get a type mismatch error for asString argument saying
Error:(26, 41) type mismatch;
found : (Char, Char) => String
required: ((Char, Char)) => String
map[(Char, Char), String](parser, asString)
I have tried to explicitly specify the types for map as map[(Char, Char), String](parser, asString) but that didn't help either.
The type T1 here is the char tuple (Char, Char) and T2 is a String. So, the function (Char, Char) => String is what's supposed to be the input but scala is expecting a different type.
What am I missing here? Why is it expecting ((Char, Char)) => String instead of (Char,Char) => String?
I'm using Scala 2.12. Don't know if that is relevant in any way.
Your help is appreciated.
The type (Char, Char) => String corresponds to a function that takes two Char parameters and returns a String.
What you want is a function that takes a Tuple2 and returns a String which is somehow different.
Its type should be Tuple2[Char, Char] => String.
Tuple2[Char, Char] corresponds to the type shorthand (Char, Char) but I guess during function definition the compiler interprets the parentheses as if they are used to group the function parameters.
This is a known issue and it's being addressed in scala3.
https://dotty.epfl.ch/docs/reference/auto-parameter-tupling.html
As others pointed out, defining a function, that accepts a Tuple2 rather than two parameters, gets a little tricky, and ugly-looking.
A nice way around that is to use .tupled:
val asString: (Char, Char) => String = (a: Char, b: Char) => a.toString + b.toString
val mParser: Parser[String] = map(parser, asString.tupled)
FunctionN.tupled converts a function accepting N arguments into an equivalent one taking a TupleN.
This is a bit nicer than defining a tuple-taking function, because of the parenthesis quirks you ran into, and also, because you don't have to deconstruct the tuple in the body.
So, suppose, I want to provide a "catch all" fall back for a PartialFunction:
val foo: PartialFunction[Int, String] = { case 1 => "foo" }
val withDefault = foo orElse { _.toString }
This does not compile: missing parameter type for expanded function ((x$1) => x$1.toString).
This:
val withDefault = foo orElse { case x: Int => x.toString }
Does not compile either (same error).
This:
val withDefault = foo orElse { (x: Int) => x.toString }
fails with type mismatch; found : Int => String; required: PartialFunction[?,?]
The only way I could find to make it work is to spell out the whole thing:
val withDefault = foo orElse PartialFunction[Int, String] { _.toString }
Is there any better syntax for this? I mean, one without having to tell it that I am passing a partial function from int to string to where it expects to receive a partial function from in to string. This is not ambiguous at all, why do I have to do this?
Maybe you need applyOrElse:
val withDefault = foo.applyOrElse(_: Int, (_: Int).toString)
Or maybe you would like something like this:
implicit class PartialFunToFun[A,B](val f: PartialFunction[A,B]) extends AnyVal {
def withDefault(bar: A => B) = f.applyOrElse[A,B](_: A, bar)
}
and use it: foo.withDefault(_.toString)(1)
Also if you want to get just another PartialFunction you can use the next syntax:
val withDefault = foo.orElse[Int, String]{case x => x.toString}
The errors you encountered for the first two are not specific to orElse. They also occur when you attempt to define the same functions separately.
scala> { _.toString }
<console>:12: error: missing parameter type for expanded function ((x$1: <error>) => x$1.toString)
{ _.toString }
scala> { case x: Int => x.toString }
<console>:12: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ?
{ case x: Int => x.toString }
^
For the last one, you are defining a function rather than a PartialFunction, thus leading to the "type mismatch" since orElse expects a PartialFunction to be passed.
scala> { (x: Int) => x.toString }
res3: Int => String = $$Lambda$1127/2044272973#3d5790ea
The final thing I'll add is that orElse is meant as a way to union two PartialFunctions. _.toString in itself is not a PartialFunction, though you could create a PartialFunction that uses it. To me it sounds like you want to have a "default" result for all the values that foo is not defined for, so I think you actually want applyOrElse instead since that is its use case. See the API to learn more.
I'm trying to figure out the difference between def and var/val when declaring a function in Scala.
Say we have a function:
scala> def f(x: Int) = { x * 2 }
f: (x: Int)Int
And another function g:
scala> var g = (x:Int) => x*2
g: Int => Int = <function1>
Apparently they are the same in the following way:
scala> f(2)
res0: Int = 4
scala> g(2)
res1: Int = 4
However, I could do
g = f
g: Int => Int = <function1>
but not
scala> f = g
<console>:13: error: missing arguments for method f;
follow this method with `_' if you want to treat it as a partially applied function
val $ires6 = f
^
<console>:10: error: reassignment to val
f = g
^
Question 1: why does this happen? I'm guessing that def maps to val.
Question 2: if I use val instead of var in declare g, are they equivalent? If not, what is the difference then?
I then try:
scala> def three( timetwo:(Int) => Int ) = { timetwo(3) }
three: (timetwo: Int => Int)Int
scala> three(g)
res47: Int = 6
scala> three(f)
res48: Int = 6
Question 3: does it mean (x: Int)Int is the same as Int => Int = <function1>? If so, is there some situation that we should favor one over the other?
Things is getting wired with the _ (underscore),
scala> three(f _)
res49: Int = 6
scala> three(g _)
<console>:11: error: type mismatch;
found : () => Int => Int
required: Int => Int
three(g _)
^
Question 4: why does this happen? What's the usage of _(underline) in Scala?
why does this happen? I'm guessing that def maps to val.
def is a method (in JVM terms) so it doesn't make sense to assign it.
The parser is then confused and it ultimately tries to save the day by interpreting the assignment f = g as
val $ires6 = f
f = g
Both statements are illegal, so you get two errors:
you can't assign a method to a val without an explicit type annotation or a _ expansion - see below)
you can't reassign a val (in case you are wondering, $ires6 is a fresh val introduced by the REPL)
if I use val instead of var in declare g, are they equivalent? If not, what is the difference then?
The difference is that val cannot be reassigned (i.e. it's a constant reference), whereas var can (i.e. it's a mutable reference).
More on the subject here: What is the difference between a var and val definition in Scala?
does it mean (x: Int)Int is the same as Int => Int = ? If so, is there some situation that we should favor one over the other?
Methods and functions are not the same, although the compiler does its best to make you believe they are, through a transformation called eta-expansion. In some cases such transformation can be performed automatically, in some others you need to be explicit and trigger it with a trailing _.
In your specific example (passing a method where a function is expected) the expansion can been performed automatically.
You can read this Q/A for a more in-depth discussion about which style to prefer.
why does this happen? What's the usage of _(underline) in Scala?
The underscore (_) has many uses in scala, one of which is the one I mentioned before, i.e. triggering the eta expansion of a method into a function.
That's a special syntax for methods, so you simply can't apply it to a function, as it would make no sense.
That's why you can do f _ (which will turn the f method into a function), but you can't do g _ (since g it's already a function).
In my code, I have the following:
type Occurrences = List[(Char, Int)]
def subtract(x: Occurrences, y: Occurrences): Occurrences = {
val yMap = y.toMap
x foldLeft (List[(Char, Int)]()) { // ERROR
case (a: List[(Char, Int)], xe: (Char, Int)) =>
if(yMap.contains(xe._1)) (xe._1 -> (xe._2 - yMap(xe._1))) :: a
else a
}
}
It fails on compile-time, at the { that is right before the error mark in the code. The error message is the following:
missing parameter type for expanded function The argument types of an
anonymous function must be fully known. (SLS 8.5) Expected type was:
Int
1) How could that be? As far as I can see, there is no room for misinterpretation of type information here, and I find a lot of such examples over the internet. How can I fix that?
2) Why is it thinking that the expected type is Int after all?
The error occurs because you wrote xs foldLeft (init) (f) instead of (xs foldLeft init)(f) or xs.foldLeft(init)(f).
The former does not work, because Scalas operator notation rules allow only to leave the dot and parentheses if a call occurs in form obj method param, which is not the case with foldLeft because it has two parameter lists.