I am following this tutorial and I see this code:
def totalCostWithDiscountFunctionParameter(donutType: String)(quantity: Int)(f: Double => Double): Double = {
println(s"Calculating total cost for $quantity $donutType")
val totalCost = 2.50 * quantity
println("inside totalCostWithDiscountFunctionParameter")
f(totalCost)
}
def applyDiscount(totalCost: Double): Double = {
val discount = 2 // assume you fetch discount from database
totalCost - discount
}
println(s"Total cost of 5 Glazed Donuts with discount function = ${totalCostWithDiscountFunctionParameter("Glazed Donut")(5)(applyDiscount(_))}")
What is the point of the _ in applyDiscount(_)? I can also remove it and just pass the function by name like this applyDiscount and the code works. What is the point of the underscore? Is it the same thing?
applyDiscount(_) is placeholder syntax for anonymous functions. This expands to:
x => applyDiscount(x)
When you pass the applyDiscount method to the function, i.e:
totalCostWithDiscountFunctionParameter("5")(applyDiscount)
Then scalac will perform eta-expansion, which means turning a method to a function value.
Do these have exactly the same semantics? Close, but not quite. Consider the following example given in What are all the uses of an underscore in Scala? (slightly modified, full credit to #Owen for the example and the provided answer)
trait PlaceholderExample {
def process[A](f: A => Unit)
val set: Set[_ => Unit]
set.foreach(process) // Error
set.foreach(process(_)) // No Error
}
The first errors at compile time while the second succeeds, why is that? Let's look at the code compiled with -Xprint:typer:
λ scalac -Xprint:typer Test.scala
FooBar.scala:11: error: polymorphic expression cannot be instantiated to expected type;
found : [A](A => Unit) => Unit
required: (Function1[_, Unit]) => ?
set.foreach(process) // Error
^
[[syntax trees at end of typer]]
package yuval.tests {
abstract trait PlaceholderExample extends scala.AnyRef {
def /*PlaceholderExample*/$init$(): Unit = {
()
};
def process[A](f: A => Unit): Unit;
<stable> <accessor> val set: Set[Function1[_, Unit]];
PlaceholderExample.this.set.foreach[U](process);
PlaceholderExample.this.set.foreach[Unit](((x$1: Function1[_, Unit]) => PlaceholderExample.this.process[_$1](x$1)))
}
}
First thing you see is a compilation error because scalac was unable to eta-expand the method process from a polymorphic method to a monomorphic function. Meaning, when scalac attempts to bind A to an actual type, it looks for a type (_ => Unit) => ? but fails because _ (an existential) is not a type.
On the other hand, the second example which is expanded to x => process(x) compiles because when scalac encounters a lambda expression with no explicit type annotation, it looks at the type of the method signature (in our case, foreach expects a _ => Unit, which is classified as a type) and successfully binds the type argument to process.
Thus, in most cases you'll find that the two are isomorphic (although they really aren't)(even IntelliJ recommended that I can write one instead of the other), but there are edge cases which does distinguish one from the other.
Related
In scala2 or 3 this function compiles:
def returnsFutureOfFutureButCompiles(): Future[Unit] = {
for {
_ <- Future.unit
} yield Future.sequence(Seq(Future.unit)) // Returns Future[Future[Seq[Unit]]]
}
but this one does not:
def returnsFutureOfFutureButDoesNotCompile(): Future[Unit] = {
val a = for {
_ <- Future.unit
} yield Future.sequence(Seq(Future.unit))
a // Error: Required: Future[Unit], Found: Future[Future[Seq[Unit]]]
}
I am aware of for-comprehension syntaxic sugar, differences between map and flatMap and how monads work.
The compiler should warns for both functions
I don't understand these different behaviours since the functions are almost doing the same job.
Maybe it's related to type inference limitations but then, why?
Also, if I declare these functions:
def a(): Future[Int] = {
for {
_ <- Future.successful(1)
} yield Future.successful(2) // Error: Required: Int, Found: Future[Int]
}
def b(): Future[Int] = {
val value = for {
_ <- Future.successful(1)
} yield Future.successful(2)
value // Error: Required: Future[Int], Found: Future[Future[Int]]
}
then the compiler warns correctly, as expected, about the two functions.
So, I think the problem is maybe related to Future.sequence?
It's related to the fact that you used specifically Unit.
As you know:
val a = for {
_ <- Future.unit
} yield Future.sequence(Seq(Future.unit))
Compiles to:
Future.unit.map { _ =>
Future.sequence(Seq(Future.unit))
}
It should infer to Future[Future[Seq[Unit]]]. But when you told it that the result should be Future[Unit], the compiler started working backward, from the type it should get, to the type it gets.
Therefore it inferred that you wanted:
Future.unit.map[Unit] { _ =>
Future.sequence(Seq(Future.unit))
}
Which is correct because when the type returned is Unit it can treat the code as if you wrote:
Future.unit.map[Unit] { _ =>
Future.sequence(Seq(Future.unit))
()
}
However "dropping non-Unit value" can happen only if the whole inference happens at once. When you do:
val a = for {
_ <- Future.unit
} yield Future.sequence(Seq(Future.unit))
a
You have two statements/expressions, so a single inference cannot work backward. Now not knowing that val a should be constrained to the type Future[Unit] it infer it to Future[Future[Seq[Unit]]]. The next, separate step, tries to match it against Future[Unit] and fails, because it is too late now to change the type of a previous statement and modify the body of map to drop the returned value.
In Scala 2 you could use -Wvalue-discard or -Ywarn-value-discard to force you to manually insert that () if you want to discard non-Unit value in a body that should return Unit. Scala 3 is still waiting to obtain such a warning flag.
Maybe it's related to type inference limitations but then, why?
Yes, it is related to the type inference limitation. Your first code is equivalent to
def returnsFutureOfFutureButCompiles(): Future[Unit] = {
Future.unit.map(x => {
Future.sequence(Seq(Future.unit))
})
}
The whole body is just one statement/expression. The complier has to figure out the type of the inner function (lambda). Argument type is not explicitly specified, but it is from the parameters (it is Unit). What about return type? The expression is of the type Future.sequence(Seq(Future.unit)). But we also know that the whole expression should be of the type Future[Unit]. So the actual type the inner function (lambda) should return should also be Unit. The compiler just runs the computation inside the body and discards this. This is similar to the regular function where some non-unit expression may be compatible with the Unit return type. Compare it with the following:
def echo(x: Int): Int = {
println(x)
x
}
def useEcho(): Unit = {
echo(3)
}
In the useEcho function the result of the expression (3) is "ignored" and converted into the unit.
Things change with the local variable. The second code is equivalent to
def returnsFutureOfFutureButDoesNotCompile(): Future[Unit] = {
val a = Future.unit.map(x => {
yield Future.sequence(Seq(Future.unit))
})
a
}
Type inference boundaries are limited to one statement only. When complier builds types for the declaration of val a there is no clue what the return type of the inner lambda could be. So it makes the best guess and takes the type of the last lambda's expression as the return type. Unfortunately, this later leads to an error.
Your code samples with int does not compile. The reason should be clear now:
def a(): Future[Int] = {
Future.successful(1).map(x => {
Future.successful(2) // Error: Required: Int, Found: Future[Int]
})
}
From the context we know the result should be Int, not Future. And the last example is completely similar to the returnsFutureOfFutureButDoesNotCompile.
The special bit here is the rule that allows to "discard" any value in the place where unit is required. You can even explicitly convert any value to unit like (5: Unit). And I think technically it could be implicit conversion as implicit conversions do apply to make the function match the code. Consider the following:
implicit def str2Int(x: String): Int = x.length
def dontDoThis(): Future[Int] =
for {
_ <- Future.successful(5)
} yield "test"
This code compiles and works. This is because after for comprehension is converted to a call to map, the expected for the anonymous function becomes clear and compiler could apply the implicit converion. So the similar thing happens with the Unit type.
You can also help the compiler to provide proper conversion in you second example by giving some type hint like:
def returnsFutureAndCompiles(): Future[Unit] = {
val a: Future[Unit] = for {
_ <- Future.unit
} yield Future.sequence(Seq(Future.unit))
a
}
Now the expected type from the map function (i.e. yield's body) is known and compiler could apply necessary conversion to the Unit type.
2 different examples, the first one works:
import cats.syntax.either._
val e = 10.asRight[String]
def i2s(i:Int):String = i.toString
e.map(i => List(i2s(i))) //using explicit parameter
e.map(List(i2s(_))) //using no-name _ parameter
Now the same example with Option is not compiled:
e.map(Option(i2s(_)))
The error:
Error:(27, 15) type mismatch;
found : Option[Int => String]
required: Int => ?
e.map(Option(i2s(_)))
With explicit parameter it works fine:
e.map(i => Option(i2s(i)))
In both cases apply method is invoked with List and Option. List.apply signature:
def apply[A](xs: A*): List[A] = ???
Option.apply signature:
def apply[A](x: A): Option[A]
Please explain the difference.
Both of your List examples compile but they don't mean the same thing and don't produce the same results.
e.map(i => List(i2s(i))) //res0: scala.util.Either[String,List[String]] = Right(List(10))
e.map(List(i2s(_))) //java.lang.IndexOutOfBoundsException: 10
The 1st is easy to understand, so what's going on with the 2nd?
What's happening is that you're using eta expansion to create an Int => String function from the i2s() method. You then populate a List with that single function as the only element in the list, and then try to retrieve the value at index 10, which doesn't exist, thus the exception.
If you change the 1st line to val e = 0.asRight[String] then the exception goes away because something does exist at index 0, the function that was just put in there.
This compiles because a List instance will accept an Int as a parameter (via the hidden apply() method), but an Option instance does not have an apply() method that takes an Int (*) so that can't be compiled.
(*) The Option object does have an apply() method, but that's a different animal.
There are multiple things at play here as to why your first example with List[A] works. First, let's look at the expansion that happens on the expression:
val res: Either[String, Int => String] =
e.map[Int => String](List.apply[Int => String](((x$1: Int) => FlinkTest.this.i2s(x$1))));
Notice two things:
The expansion of the lambda expression happens inside List.apply, and perhaps not as you expected, for it to be outside of List.apply, like this:
e.map(i => List(i2s(i))
The return type from .map is somehow not Either[String, List[Int => String]], but Either[String, Int => String]. This is due to the fact that in it's hierarchy chain, List[A] extends PartialFunction[Int, A], thus allowing it to transform the result into a function type.
This doesn't work for Option[A], as it doesn't extend PartialFunction anywhere in it's type hierarchy.
The key takeaway here is that the expansion of the lambda expression doesn't work as you expect, as List(i2s(_)) expands to List(i2s(x => i2s(x)) and not List(i => i2s(i)). For more on underscore expansion, see What are all the uses of an underscore in Scala?
I am new to Scala and while was trying to understand implicits in Scala, I am getting hard time understand [A](f: => A) part. Can any one explain please?
object Helpers {
implicit class IntWithTimes(x: Int) {
def times[A](f: => A): Unit = {
def loop(current: Int): Unit =
if(current > 0) {
f
loop(current - 1)
}
loop(x)
}
}
}
This is being called as:
scala> import Helpers._
import Helpers._
scala> 5 times println("HI")
HI
HI
HI
HI
HI
Reference : https://docs.scala-lang.org/overviews/core/implicit-classes.html
Let's disect this method declaration:
def times[A](f: => A): Unit
def: keyword used to define a method
times: method name
[A]: a type parameter named A. This means that this method takes a type parameter which can be passed either explicitly (e.g. by calling times[Int](...)) or implicitly (calling times(...) and letting compiler infer the type) when the method is called. See https://docs.scala-lang.org/tour/polymorphic-methods.html for more details
(f: => A): these are the method's "value parameters", in this case there's exactly one such parameter, named f, of type A (which is the type parameter we've declared!). The => signifies that this parameter is a call-by-name parameter, see https://docs.scala-lang.org/tour/by-name-parameters.html
: Unit: this is the method's return type - defined to be Unit, practically meaning this method doesn't return any value
I have a function like so:
def ifSome[B, _](pairs:(Option[B], B => _)*) {
for((paramOption, setFunc) <- pairs)
for(someParam <- paramOption) setFunc(someParam)
}
and overloaded functions like these:
class Foo{
var b=""
def setB(b:String){this.b = b}
def setB(b:Int){this.b = b.toString}
}
val f = new Foo
then the following line produces an error:
ifSome(Option("hi") -> f.setB _)
<console>:11: error: ambiguous reference to overloaded definition,
both method setB in class Foo of type (b: Int)Unit
and method setB in class Foo of type (b: String)Unit
match expected type ?
ifSome(Option("hi") -> f.setB _)
But the compiler knows that we're looking for a Function1[java.lang.String, _], so why should the presence of a Function1[Int, _] present any confusion? Am I missing something or is this a compiler bug (or perhaps it should be a feature request)?
I was able to workaround this by using a type annotation like so
ifSome(Option("hi") -> (f.setB _:String=>Unit))
but I'd like to understand why this is necessary.
You'll want to try $ scalac -Ydebug -Yinfer-debug x.scala but first you'll want to minimize.
In this case, you'll see how in the curried version, B is solved in the first param list:
[infer method] solving for B in (bs: B*)(bfs: Function1[B, _]*)Nothing
based on (String)(bfs: Function1[B, _]*)Nothing (solved: B=String)
For the uncurried version, you'll see some strangeness around
[infer view] <empty> with pt=String => Int
as it tries to disambiguate the overload, which may lead you to the weird solution below.
The dummy implicit serves the sole purpose of resolving the overload so that inference can get on with it. The implicit itself is unused and remains unimplemented???
That's a pretty weird solution, but you know that overloading is evil, right? And you've got to fight evil with whatever tools are at your disposal.
Also see that your type annotation workaround is more laborious than just specifying the type param in the normal way.
object Test extends App {
def f[B](pairs: (B, B => _)*) = ???
def f2[B](bs: B*)(bfs: (B => _)*) = ???
def g(b: String) = ???
def g(b: Int) = ???
// explicitly
f[String](Pair("hi", g _))
// solves for B in first ps
f2("hi")(g _)
// using Pair instead of arrow means less debug output
//f(Pair("hi", g _))
locally {
// unused, but selects g(String) and solves B=String
import language.implicitConversions
implicit def cnv1(v: String): Int = ???
f(Pair("hi", g _))
}
// a more heavy-handed way to fix the type
class P[A](a: A, fnc: A => _)
class PS(a: String, fnc: String => _) extends P[String](a, fnc)
def p[A](ps: P[A]*) = ???
p(new PS("hi", g _))
}
Type inference in Scala only works from one parameter list to the next. Since your ifSome only has one parameter list, Scala won't infer anything. You can change ifSome as follows:
def ifSome[B, _](opts:Option[B]*)(funs: (B => _)*) {
val pairs = opts.zip(funs)
for((paramOption, setFunc) <- pairs)
for(someParam <- paramOption) setFunc(someParam)
}
leave Foo as it is...
class Foo{
var b=""
def setB(b:String){this.b = b}
def setB(b:Int){this.b = b.toString}
}
val f = new Foo
And change the call to ifSome accordingly:
ifSome(Option("hi"))(f.setB _)
And it all works. Now of course you have to check whether opts and funs have the same length at runtime.
I have this class in Scala:
object Util {
class Tapper[A](tapMe: A) {
def tap(f: A => Unit): A = {
f(tapMe)
tapMe
}
def tap(fs: (A => Unit)*): A = {
fs.foreach(_(tapMe))
tapMe
}
}
implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)
}
Now,
"aaa".tap(_.trim)
doesn't compile, giving the error
error: missing parameter type for expanded function ((x$1) => x$1.trim)
Why isn't the type inferred as String? From the error it seems that the implicit conversion does fire (otherwise the error would be along the lines of "tap is not a member of class String"). And it seems the conversion must be to Tapper[String], which means the type of the argument is String => Unit (or (String => Unit)*).
The interesting thing is that if I comment out either of tap definitions, then it does compile.
6.26.3 Overloading Resolution
One first determines the set of
functions that is potentially
applicable based on the shape of the
arguments
...
If there is precisely one alternative
in B, that alternative is chosen.
Otherwise, let S1, . . . , Sm be the
vector of types obtained by typing
each argument with an undefined
expected type.
Both overloads of tap are potentially applicable (based on the 'shape' of the arguments, which accounts for the arity and type constructors FunctionN).
So the typer proceeds as it would with:
val x = _.trim
and fails.
A smarter algorithm could take the least upper bound of the corresponding parameter type of each alternative, and use this as the expected type. But this complexity isn't really worth it, IMO. Overloading has many corner cases, this is but another.
But there is a trick you can use in this case, if you really need an overload that accepts a single parameter:
object Util {
class Tapper[A](tapMe: A) {
def tap(f: A => Unit): A = {
f(tapMe)
tapMe
}
def tap(f0: A => Unit, f1: A => Unit, fs: (A => Unit)*): A = {
(Seq(f0, f1) ++ fs).foreach(_(tapMe))
tapMe
}
}
implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)
"".tap(_.toString)
"".tap(_.toString, _.toString)
"".tap(_.toString, _.toString, _.toString)
}