Related
As a part of learning Scala I try to implement Haskell's flip function (a function with signature (A => B => C) => (B => A => C)) in Scala - and implement it as a function (using val) and not as a method (using def).
I can implement it as a method, for instance this way:
def flip[A, B, C](f: (A, B) => C):((B, A) => C) = (b: B, a: A) => f(a, b)
val minus = (a: Int, b: Int) => a - b
val f = flip(minus)
println(f(3, 5))
However, when I try to implement it as a function, it does not work:
val flip = (f: ((Any, Any) => Any)) => ((a: Any, b: Any) => f(b, a))
val minus = (a: Int, b: Int) => a - b
val f = flip(minus)
println(f(3, 5))
When I try to compile this code, it fails with this message:
Error:(8, 18) type mismatch;
found : (Int, Int) => Int
required: (Any, Any) => Any
val f = flip(minus)
I understand why it fails: I try to pass (Int, Int) => Int where (Any, Any) => Any is expected. However, I don't know how to fix this problem. Is it possible at all?
Scala doesn't support polymorphic functions, unlike methods which are. This is due to the first class value nature of functions, which are simply instances of the FunctioN traits. These functions are classes, and they need the types to be bound at declaration site.
If we took the flip method and tried to eta expand it to a function, we'd see:
val flipFn = flip _
We'd get back in return a value of type:
((Nothing, Nothing) => Nothing) => (Nothing, Nothing) => Nothing
Due to the fact that none of the types were bound, hence the compiler resorts to the buttom type Nothing.
However, not all hope is lost. There is a library called shapeless which does allow us to define polymorphic functions via PolyN.
We can implement flip like this:
import shapeless.Poly1
object flip extends Poly1 {
implicit def genericCase[A, B, C] = at[(A, B) => C](f => (b: B, a: A) => f(a, b))
}
flip is no different from the FunctionN trait, it defines an apply method which will be called.
We use it like this:
def main(args: Array[String]): Unit = {
val minus = (a: Int, b: Int) => a - b
val f = flip(minus)
println(f(3, 5))
}
Yielding:
2
This would also work for String:
def main(args: Array[String]): Unit = {
val stringConcat = (a: String, b: String) => a + b
val f = flip(stringConcat)
println(f("hello", "world"))
}
Yielding:
worldhello
I have the following Scala program:
object Test extends App {
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
zip.foreach(f)
}
Why I get the following compiler error:
Error:(6, 15) type mismatch;
found : ((Int, Int)) => Unit
required: (Int, Int) => ?
zip.foreach(f)
when there is an implicit conversion from Tuple2Zipped to Traversable where foreach[U](f: ((El1, El2)) => U): Unit is defined.
NOTE: I edited my question to clarify.
I know that the foreach defined in Tuple2Zipped has the following signature:
def foreach[U](f: (El1, El2) => U): Unit
and that my function f does not fit as the argument.
But in Tuple2Zipped.scala is defined the trait ZippedTraversable2 and his companion object this way:
trait ZippedTraversable2[+El1, +El2] extends Any {
def foreach[U](f: (El1, El2) => U): Unit
}
object ZippedTraversable2 {
implicit def zippedTraversable2ToTraversable[El1, El2](zz: ZippedTraversable2[El1, El2]): Traversable[(El1, El2)] = {
new scala.collection.AbstractTraversable[(El1, El2)] {
def foreach[U](f: ((El1, El2)) => U): Unit = zz foreach Function.untupled(f)
}
}
}
so as my function f does match the argument of the foreach defined in the Traversable returned by zippedTraversable2ToTraversable and the companion object defines an implicit conversion from ZippedTraversable2 to this Traversable and Tuple2Zipped is a ZippedTraversable2, i think that this conversion has to be tried and my function be accepted.
The Intellij Idea editor accepts my construct without reporting any error but the compiler fails with the shown error.
Here is yet another, and slightly more esoteric, way to fix your code.
zip.foreach(Function.untupled(f))
Function.untupled() will take a ((Int, Int)) => Unit, which is what you've got in f, and return a (Int, Int) => Unit, which is what you need to process the zip elements.
Your function f is wrong ( it's also explicitly stated in the error )
zip.foreach expectes Function2 that takes type T1, type T2 and returns R, but you are passing a Function1 that takes Tuple2[Int,Int] as type T1
Here's an explaination regarding your compilation error :
found : ((Int, Int)) => Unit EQUALS Function1[Tuple2[Int,Int]] => Unit
Whereas
required: (Int, Int) => Unit EQUALS Function2[Int,Int,Unit]
(Keep in mind that () is syntactic sugar for Tuple* , that's why your are seeing the double parenthesis)
Here's an example that compiles :
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f:Function2[Int,Int,Unit] = (x,y) => println(x + y)
zip.foreach[Unit](f)
Your object is a runtime.Tuple2Zipped, if you look at the signature
def foreach[U](f: (El1, El2) => U): Unit
It takes a function f: (El1, E1l2) => U, but your function is f: ((El1, El2)) => U, hence the error.
I found it confusing too, as Tuple2Zipped almost looks like a List[(A,B)], in fact the toList method would produce just that:
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped.toList
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
zip.foreach(f)
Edit
I think you are looking for an implicit conversion from ((A, B)) => U to (A, B) => U which is not what zippedTraversable2ToTraversable does. You can define something like this that would work:
implicit def tupconv[A,B] (f: (((A,B)) => Unit)): (A, B) => Unit = Function.untupled(f)
Edit V2
For the code above you'd need an implicit variable in scope that would map from ((A,B)) => Unit to (A, B) => Unit as that's where the mismatch is. Whereas the one you quoted does ZippedTraversable2[El1, El2] to Traversable[(El1, El2)]. If you'd like to make use of that implicit conversion, you could do this:
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
def thisUsesTheImplicitYouWant(t: Traversable[(Int,Int)]) = t.foreach(f)
thisUsesTheImplicitYouWant(zip)
Thanks to Jasper-M I have known that a Scala bug has been reported on this issue: https://github.com/scala/bug/issues/9523. This bug was reported by Michael Pollmeier that asked this question one and a half years ago: Why does Scala implicit resolution fail for overloaded method with type parameter?
jamborta posted a question with a more generic example on this same issue here.
I would like to have groupByIndex function that groups values based on their index (and not the value).
A concrete method definition for Vector[A] could look like the following:
def groupByIndex[A, K](vector: Vector[A], f: Int => K): immutable.Map[K, Vector[(A, Int)]] = {
vector.zipWithIndex.groupBy { case (elem, index) => f(index) }
}
Testing this function in the REPL gives indeed the correct result:
scala> val vec = Vector.tabulate(4)(i => s"string ${i+1}")
vec: scala.collection.immutable.Vector[String] = Vector(string 1, string 2, string 3, string 4)
scala> groupByIndex(vec, i => i%2)
res2: scala.collection.immutable.Map[Int,Vector[(String, Int)]] = Map(1 -> Vector((string 2,1), (string 4,3)), 0 -> Vector((string 1,0), (string 3,2)))
Now, I would like to apply the "enrich-my-library" pattern to give this method to all the classes that should support it, i.e. classes that implement zipWithIndex and groupBy. Those two methods are defined in GenIterableLike (zipWithIndex) and GenTraversableLike/TraversableLike (groupBy).
With all this in mind, I tried to mimic the method definitions of zipWithIndex (this is the problematic) and groupBy to build my own groupByIndex:
implicit class GenIterableLikeOps[A, Repr](val iterable: GenIterableLike[A, Repr] with TraversableLike[A, Repr]) extends AnyVal {
def groupByIndex[K, A1 >: A, That <: TraversableLike[(A1, Int), OtherRepr], OtherRepr](f: Int => K)(implicit bf: CanBuildFrom[Repr, (A1, Int), That]): immutable.Map[K, OtherRepr] = {
val zipped = iterable.zipWithIndex
zipped.groupBy{ case (elem, index) => f(index) }
}
}
First, this seems way too complicated to me - is there a way to simplify this? For example, can we somehow drop the second OtherRepr? (I was not able to.)
Second, I am not able to call this function without explicitly specifying the generic parameters. Using the example from above I get the following error:
scala> vec.groupByIndex(i => i%2)
<console>:21: error: Cannot construct a collection of type scala.collection.TraversableLike[(String, Int),Nothing] with elements of type (String, Int) based on a collection of type scala.collection.immutable.Vector[String].
vec.groupByIndex(i => i%2)
^
scala> vec.groupByIndex[Int, String, Vector[(String, Int)], Vector[(String, Int)]](i => i%2)
res4: scala.collection.immutable.Map[Int,Vector[(String, Int)]] = Map(1 -> Vector((string 2,1), (string 4,3)), 0 -> Vector((string 1,0), (string 3,2)))
How do I a) simplify this method and b) make it work without having to specify the generic parameters?
You can substitute the OtherThat type parameter by That. That way you get rid of OtherThat and solve the problem of having to specify the generic type parameters. The compiler is then able to resolve That by looking at the implicit value for CanBuildFrom[Repr, (A1, Int), That].
implicit class GenIterableLikeOps[A, Repr]
(val iterable: GenIterableLike[A, Repr] with TraversableLike[A, Repr])
extends AnyVal {
def groupByIndex
[K, A1 >: A, That <: TraversableLike[(A1, Int), That]]
(f: Int => K)(implicit bf: CanBuildFrom[Repr, (A1, Int), That])
: Map[K, That] = {
val zipped = iterable.zipWithIndex
zipped.groupBy{ case (elem, index) => f(index) }
}
}
This isn't as good as the other answer, but if you don't care about what you're building, one way to simplify and avoid building the zipped collection:
implicit class gbi[A](val as: Traversable[A]) extends AnyVal {
def groupByIndex[K](f: Int => K) = (as, (0 until Int.MaxValue)).zipped.groupBy { case (x, i) => f(i) }
}
The range is a benign way to avoid taking the size of the traversable.
I want to implicitly convert functions from A => B to List[A] => List[B].
I wrote the following implicit definition:
implicit def lift[A, B](f: A => B): List[A] => List[B] = ...
Unfortunately, when I write the following code, implicit aren't applied:
val plusOne: (List[Int]) => List[Int] = (x: Int) => (x + 1)
If I annotate the function with explicit time, it works fine.
Why? How can I fix it?
UPDATE. It seems that the problem is specific to anonymous functions. Compare:
#Test
def localLiftingGenerics {
implicit def anyPairToList[X, Y](x: (X, Y)): List[X] => List[Y] = throw new UnsupportedOperationException
val v: List[String] => List[Int] = ("abc", 239)
}
#Test
def localLiftingFuns {
implicit def fun2ListFun[X, Y](f: X => Y): List[X] => List[Y] = throw new UnsupportedOperationException
val v: List[String] => List[Int] = ((x: String) => x.length)
}
The first one is compiled well. The second one is marked as error
According to The Scala Language Specification / Expressions / Anonymous Functions (6.23):
If the expected type of the anonymous function is of the form
scala.Functionn[S1, …, Sn, R], the expected type of e is R ...
So, the result type of the function will be inferred as List[Int] unless you separate the function definition from the function value assignment (to get rid of the expected type):
val function = (x: Int) => (x + 1)
val plusOne: (List[Int]) => List[Int] = function
or specify the function type explicitly:
val plusOne: (List[Int]) => List[Int] = ((x: Int) => (x + 1)): Int => Int
(Scala 2.9.1-1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_05)
A first observation: If you duplicate fun2ListFun and rename it to, e.g.,``fun2ListFun, you'll get
found : String => <error>
required: List[String] => List[Int]
Note that implicit conversions are not applicable because they are ambiguous:
both method fun2ListFun2 of type [X, Y](f: X => Y)List[X] => List[Y]
and method fun2ListFun of type [X, Y](f: X => Y)List[X] => List[Y]
are possible conversion functions from String => <error> to List[String] => List[Int]
val v: List[String] => List[Int] = ((x: String) => x.length)
It looks as if the compiler considers both implicits as applicable.
A second observation:
Splitting
val v: List[String] => List[Int] = ((x: String) => x.length) /* Error*/
into
val f = ((x: String) => x.length)
val v: List[String] => List[Int] = f /* Works */
makes the compiler happy.
The implicit conversion for the input value compiles. So we just have a problem for the output of the anonymous function
def localLiftingFuns {
implicit def fun2ListFun[X, Y](f: X => Y): List[X] => Y = throw new UnsupportedOperationException
val v: List[String] => Int = ((x: String) => x.length)
}
A possible fix using a second implicit conversion:
def localLiftingFuns {
implicit def fun2ListFun[X, Y](f: X => List[Y]): List[X] => List[Y] = throw new UnsupportedOperationException
implicit def type2ListType[X](x:X): List[X] = throw new UnsupportedOperationException
val v: List[String] => List[Int] = ((x: String) => x.length)
}
This version compiles.
Seems the compiler has hard time figuring what is going on with the type of the function. If you will give him a little help, it would work:
scala> implicit def lift[A,B](f: A => B) = (_:List[A]).map(f)
lift: [A, B](f: (A) => B)(List[A]) => List[B]
scala> val f: List[Int] => List[Int] = ((_:Int) + 1):(Int => Int)
f: (List[Int]) => List[Int] = <function1>
I would like to map the elements of a Scala tuple (or triple, ...) using a single function returning type R. The result should be a tuple (or triple, ...) with elements of type R.
OK, if the elements of the tuple are from the same type, the mapping is not a problem:
scala> implicit def t2mapper[A](t: (A,A)) = new { def map[R](f: A => R) = (f(t._1),f(t._2)) }
t2mapper: [A](t: (A, A))java.lang.Object{def map[R](f: (A) => R): (R, R)}
scala> (1,2) map (_ + 1)
res0: (Int, Int) = (2,3)
But is it also possible to make this solution generic, i.e. to map tuples that contain elements of different types in the same manner?
Example:
class Super(i: Int)
object Sub1 extends Super(1)
object Sub2 extends Super(2)
(Sub1, Sub2) map (_.i)
should return
(1,2): (Int, Int)
But I could not find a solution so that the mapping function determines the super type of Sub1 and Sub2. I tried to use type boundaries, but my idea failed:
scala> implicit def t2mapper[A,B](t: (A,B)) = new { def map[X >: A, X >: B, R](f: X => R) = (f(t._1),f(t._2)) }
<console>:8: error: X is already defined as type X
implicit def t2mapper[A,B](t: (A,B)) = new { def map[X >: A, X >: B, R](f: X => R) = (f(t._1),f(t._2)) }
^
<console>:8: error: type mismatch;
found : A
required: X
Note: implicit method t2mapper is not applicable here because it comes after the application point and it lacks an explicit result type
implicit def t2mapper[A,B](t: (A,B)) = new { def map[X >: A, X >: B, R](f: X => R) = (f(t._1),f(t._2)) }
Here X >: B seems to override X >: A. Does Scala not support type boundaries regarding multiple types? If yes, why not?
I think this is what you're looking for:
implicit def t2mapper[X, A <: X, B <: X](t: (A,B)) = new {
def map[R](f: X => R) = (f(t._1), f(t._2))
}
scala> (Sub1, Sub2) map (_.i)
res6: (Int, Int) = (1,2)
A more "functional" way to do this would be with 2 separate functions:
implicit def t2mapper[A, B](t: (A, B)) = new {
def map[R](f: A => R, g: B => R) = (f(t._1), g(t._2))
}
scala> (1, "hello") map (_ + 1, _.length)
res1: (Int, Int) = (2,5)
I’m not a scala type genius but maybe this works:
implicit def t2mapper[X, A<:X, B<:X](t: (A,B)) = new { def map[A, B, R](f: X => R) = (f(t._1),f(t._2)) }
This can easily be achieved using shapeless, although you'll have to define the mapping function first before doing the map:
object fun extends Poly1 {
implicit def value[S <: Super] = at[S](_.i)
}
(Sub1, Sub2) map fun // typed as (Int, Int), and indeed equal to (1, 2)
(I had to add a val in front of i in the definition of Super, this way: class Super(val i: Int), so that it can be accessed outside)
The deeper question here is "why are you using a Tuple for this?"
Tuples are hetrogenous by design, and can contain an assortment of very different types. If you want a collection of related things, then you should be using ...drum roll... a collection!
A Set or Sequence will have no impact on performance, and would be a much better fit for this kind of work. After all, that's what they're designed for.
For the case when the two functions to be applied are not the same
scala> Some((1, "hello")).map((((_: Int) + 1 -> (_: String).length)).tupled).get
res112: (Int, Int) = (2,5)
The main reason I have supplied this answer is it works for lists of tuples (just change Some to List and remove the get).