I was wondering how it works if I want to defining a function that takes one or more parameters and a callable (a function), and why the annotation is like this.
I will take the code from this answer as example:
// Returning T, throwing the exception on failure
#annotation.tailrec
final def retry[T](n: Int)(fn: => T): T = {
util.Try { fn } match {
case util.Success(x) => x
case _ if n > 1 => retry(n - 1)(fn)
case util.Failure(e) => throw e
}
}
In this function there are a few interesting things:
The annotation tailrec.
Generic type function retry[T]
Parameter int
callable fn
My question is on point 4. Why and how the definition of this function takes two round brackets. If you want to pass a callable function to any function should you always use a round brackets next to the "list" of optional parameter? Why not put together with the parameters?
Thank you in advance
You can have multiple parameter lists in function declaration. It is mostly the same as merging all the parameters into one list (def foo(a: Int)(b: Int) is more or less equivalent to def foo(a: Int, b: Int)) with a few differences:
You can reference parameters from previous list(s) in declaration: def foo(a : Int, b: Int = a + 1) does not work, but def foo(a: Int)(b: Int = a +1) does.
Type parameters can be inferred between parameter lists: def foo[T](x: T, f: T => String): String ; foo(1, _.toString) doesn't work (you'd have to write foo[Int](1, _.toString), but def foo[T](x: T)(f: T => String); foo(1)(_.toString) does.
You can only declare the entire list as implicit, so, multiple lists are helpful when you need some parameters to be implicit, and not the others: def foo(a: Int)(implicit b: Configuration)
Then, there are some syntactical advantages - things you could do with the single list, but they'd just look uglier:
Currying:
def foo(a: Int)(b: Int): String
val bar: Int => String = foo(1)
You could write this with the single list too, but it wouldn't look as nice:
def foo(a: Int, b: Int): String
val bar: Int => String = foo(1, _)
Finally, to your question:
def retry[T](n: Int)(f: => T)
is nice, because parenthesis are optional around lists with just a single argument. So, this lets you write things like
retry(3) {
val c = createConnection
doStuff(c)
closeConnection(c)
}
which would look a lot uglier if f was merged into the same list.
Also, currying is useful:
val transformer = retry[Int](3)
Seq(1,2,3).map { n => transformer(n + 1) }
Seq(4,5,6).map { n => transformer(n * 2) }
To be honest you don't have to use multiple parameter lists in order to pass function as an argument. E.g.
def pass(string: String, fn: String => Unit): Unit = fn(string)
would totally work. However, how would you call it?
pass("test", s => println(s))
Some would find it clumsy. You would rather want to pass it like:
pass("test")(s => println(s))
or even
pass("test") { s =>
println(s)
}
to make it look as if function is a block appended to the pass called with one parameter.
With single parameter list you will be forced to write it like:
pass("test", println)
pass("test", s => println(s))
pass("test", { s => println(s) })
With last parameter curried you just get more comfortable syntax. In languages like Haskell, where currying happens automatically, you don't have to bother with syntactic design decisions like this one. Unfortunately Scala requires that you made these decisions explicitly.
If you want to pass a callable function to any function should you
always use a round brackets next to the "list" of optional parameter?
Why not put together with the parameters?
There is no such obligation, it's (mostly) a matter of style. IMO, it also leads to cleaner syntax. With two parameter lists, where the second one is the function yielding the result, we can call the retry method:
val res: Try[Int] retry(3) {
42
}
Instead, of we used a single parameter list, our method call would look like this:
val res: Try[Int] = retry(3, () => {
42
})
I find the first syntax cleaner, which also allows you to use retry as a curried method when only supplying the first parameter list
However, if we think of a more advanced use case, Scala type inference works between parameter lists, not inside them. That means that if we have a method:
def mapFun[T, U](xs: List[T])(f: T => U): List[U] = ???
We would be able to call our method without specifying the type of T or U at the call site:
val res: List[Int] = mapFun(List.empty[Int])(i => i + 1)
But if we used a single parameter list,
def mapFun2[T, U](xs: List[T], f: T => U): List[U] = ???
This won't compile:
val res2 = mapFun2(List.empty[Int], i => i + 1)
Instead, we'd need to write:
val res2 = mapFun2[Int, Int](List.empty[Int], i => i + 1)
To aid the compiler at choosing the right types.
Related
I'm following the excellent Functional Programming in Scala by Paul Chiusano and RĂșnar Bjarnason, and had a question to what I find is odd/unexpected behaviour.
I have defined a foldRight function like so
def foldRight[A,B](l: List[A], z: B)(f: (A,B) => B): B =
l match {
case Nil => z
case x :: xs => f(x, foldRight(xs, z)(f))
}
that works fine as long I pass in concrete arguments, e.g.:
foldRight((1 to 10).toList, 0)(_ + _)
val res6: Int = 55
If I define a similar function that takes the generic list
def sum[A](as: List[A]): Int = foldRight(as, 0)(_ + _)
something odd happens
def sum[A](as: List[A]): Int = foldRight(as, 0)(_ + _)
^
fold.scala:23: error: type mismatch;
found : Int
required: String
Initially, I was super puzzled by this error message given the only types in play are A, B and Int. However, would seem it simply attempts to make sense of + and generic A's by calling .toString on them as I read about here.
If that is the case though, why come it doesn't simply adds a1.toString + a2.toString + ... + an.toString + 0 = something0? String concatenation between String and Int in Scala is fully understood by the compiler.
Hope someone can help clarifying what's happening here.
However, would seem it simply attempts to make sense of + and generic A's by calling .toString
This is not what happens. The result is similar, but it's not calling .toString. You found the underlying problem though.
The sum function operates on a generic A on which you want to call the +method on.
But A doesn't have a + method (Remember that + in infix position is the same as x.+(y)). The compiler then searches the implicit scope for a function or class constructor to convert this A into something that has a + method. It finds it in any2stringadd.
Your method actually looks like
def sum[A](as: List[A]): Int = foldRight(as, 0)(any2Stringadd(_) + _)
Now the error makes sense. Because the + method of the any2Stringadd class expects a string as its argument. But your z argument is of type Int. You can see that, when you explicitly add the types to the inline function argument.
As other pointed out, this is not reconcilable without constraining the type parameter.
As others have said, you can't try to add instances of some type A that you know nothing about, since the Scala compiler gets confused and tries to implicitly turn your A objects into Strings, even though you have 0, which is an Int. However, you can get around this with typeclasses, like this.
trait Sum[T] {
def id: T
def add(_1: T, _2: T): T
}
implicit object IntSum extends Sum[Int] {
def id = 0
def add(i1: Int, i2: Int) = i1 + i2
}
def sum[A](as: List[A])(implicit sum: Sum[A]): A = foldRight(as, sum.id)(sum.add)
Here, IntSum is passed to the sum method implicitly, and its add method is used to sum your list together. You can define as many such implicit objects as you like for Double, String, etc.
Let's name some functions properly by thinking about what they are supposed to do:
def foldRight[A,B](l: List[A], z: B)(f: (A,B) => B): B =
l match {
case Nil => z
case x :: xs => f(x, foldRight(xs, z)(f))
}
def sumInts(numbers: List[Int]): Int = foldRight(numbers, 0)(_ + _)
def concatStrings(strings: List[String]): String = foldRight(strings, "")(_ + _)
def concatStringRepresentations[A](as: List[A]): String = foldRight(as, "")(_ + _)
concatStringRepresentations(List(List(), List(1,2,3), List(0))) // List()List(1, 2, 3)List(0)
concatStringRepresentations(List(1.0, 2.0, 3.0)) // 1.02.03.0
def sumIntRepresentations[A](as: List[A]): Int = foldRight(as, 0)(_ + _)
// --> type mismatch, found: String, required: Int, for second param of (_ + _)
This is what your sum function was probably supposed to be named.
To make it compile, we wish that there is
a method +: Int => Int defined for any type, or...
implicit conversions that convert any type to Int (something like toString with Int), or...
other implicit conversions that make sense, or...
something else, that can make this function succeed and is recognized by the compiler.
It turns out that Any can be implicitly converted to something that has a method +(other: String) => String that makes the String example work, but there is no implicit conversion from Any to something that has a method +(other: Int) => Int.
This could be explained in more detail and other answers already do that.
Opinions:
So, yes, the compiler gives a message that is confusing due to existence of implicit conversions. But let's look at the problem at hand which probably was our wishful thinking. Sometimes stuff just works like in the concatStringRepresentations function. It does not work in general, but in this special case with String it does.
Sometimes something works for a special case. And when we don't see that it is a special case, mistakes can happen, for instance when we apply it to something else.
I'm reading the docs on implicits in Scala, and there is an example of a function with implicit conversion as parameter:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
I understand how it works, but I don't understand what's the point of writing it like that instead of:
def getIndexExplicit[T](seq: Seq[T], value: T) = seq.indexOf(value)
As far as I know, if the conversion from the argument seq to type Seq[T] exists, the compiler would still allow the call to getIndexExplicit?
To illustrate my point, I prepared this simple example:
def equal42[T](a: T)(implicit conv: T => Int) = conv(a) == 42 // implicit parameter version
def equal42Explicit(a: Int) = a == 42 // just use the type in the signature
implicit def strToInt(str: String): Int = java.lang.Integer.parseInt(str) // define the implicit conversion from String to Int
And indeed, both functions seem to work in the same way:
scala> equal42("42")
res12: Boolean = true
scala> equal42Explicit("42")
res13: Boolean = true
If there is no difference, what's the point of explicitly defining the implicit conversion?
My guess is that in this simple case it makes no difference, but there must be some more complex scenarios where it does. What are those?
In your super-simple example:
equal42("42")
equal42Explicit("42")
is equal to
equal42("42")(strToInt)
equal42Explicit(strToInt("42"))
which in case of your definition make no difference.
BUT if it did something else e.g.
def parseCombined[S, T](s1: S, s2: S)
(combine: (S, S) => S)
(implicit parse: S => Option[T]): Option[T] =
parse(combine(s1, s2))
then when you would apply conversion matters:
implicit def lift[T]: T => Option[T] = t => Option(t)
implicit val parseInt: String => Option[Int] = s => scala.util.Try(s.toInt).toOption
implicit def parseToString[T]: T => Option[String] = t => Option(t.toString)
parseCombined[Option[Int], String]("1", "2") { (a, b) => a.zip(b).map { case (x, y) => x + y } } // Some("Some(3)")
parseCombined[String, Int]("1", "2") { _ + _ } // Some(12)
In the first case arguments were converted before passing (and then again inside), while in the other case they were converted only inside a function at a specific place.
While this case is somewhat stretched, it shows that having control over when conversion is made might matter to the final result.
That being said such usage of implicit conversions is an antipattern and type classes would work much better for it. Actually extension methods are the only non-controversial usages of implicit conversions because even magnet pattern - the only other use case that might be used on production (see Akka) - might be seen as issue.
So treat this example from docs as a demonstration of a mechanism and not as a example of good practice that should be used on production.
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?
Give the following scenario:
def add(a: Int, b: Int): Int = a + b
def f1(adder: () => Int) = adder()
f1(add(1,2) _) // Does **NOT** compile, because add seems to be already executed
f1(() => add(1,2)) // This works, but seems to be ugly
Is there any way to make it work with the underscore?
You could make it work without the underscore by using a by-name parameter:
scala> def f2(adder: => Int) = adder
f2: (adder: => Int)Int
scala> f2(add(1,2))
res0: Int = 3
The problem with your 3rd line is that f1 needs a function that is called with empty params. A by-name parameter lets you pass in any arbitrary expression that results in the given type, and doesn't evaluate it until you use it within the method body. If you want to use a by-name argument in multiple places within the body of the method, but don't want it reevaluated, you can cache it by assigning it to a lazy val.
There is a Wrapper class for arbitrary functions. I tried to abstract the input and output (return value) of the function with the two type parameters [I, O] (for input and output).
class Wrapper[I, O](protected val f: I => O {
protected def doIt(input: I): O = f(input)
}
As this should be a wrapper for arbitrary functions, I have a problem with functions that take multiple parameters.
val multiplyFunction = (a: Int, b: Int) => a * b
val multiplyWrapper = new Wrapper[(Int, Int), Int](multiplyFunction)
The second line does not compile, because the wrapper expects a function which takes a Tuple with two Ints as the only parameter.
Is there a way to rewrite this, so that I can abstract the function's parameters no matter how many parameters there are? Ideally the solution would be type safe, by the compiler.
Maybe I there is an alternative to using a tuple to specify the types for the wrapper when creating an instance it.
I hope I don't have to write it like the Tuple classe Tuple2 to TupleN or Function2 to FunctionN. I don't know all the details about this, but that does look more like a workaround and is not a abstract / generic solution.
You could use tupled method on function: new Wrapper(multiplyFunction.tupled).
If you want to make this transparent to the wrapper class's user you could use duck typing:
object Wrapper {
def apply[I, O](e: { def tupled: I => O }) = new Wrapper(e.tupled)
def apply[I, O](e: I => O) = new Wrapper(e)
}
scala> Wrapper( (a: Int) => a )
res0: Wrapper[Int,Int] = Wrapper#29d03e78
scala> Wrapper( (a: Int, b: Int) => a * b )
res1: Wrapper[(Int, Int),Int] = Wrapper#581cdfc2
You'll get some overhead due to reflection.