Function.tupled and placeholder syntax
Asked Answered
S

1

11

I have seen this usage of Function.tupled example in another answer: Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length).

It works:

scala> Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
<console>:5: warning: method tupled in object Function is deprecated: 
Use `f.tuple` instead
       Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
                                                ^
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 2 -> 3)

It seems I can do without if I don't want to use the placeholder syntax.

scala> Map(1 -> "one", 2 -> "two") map (x => x._1 -> x._2.length)
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 2 -> 3)

The direct use of the placeholder syntax does not work:

scala> Map(1 -> "one", 2 -> "two") map (_._1 -> _._2.length)
<console>:5: error: wrong number of parameters; expected = 1
       Map(1 -> "one", 2 -> "two") map (_._1 -> _._2.length)

How does Function.tupled work? There seem to be a lot happening in Function.tupled(_ -> _.length). Also how would I use it to not get the deprecation warning?

Strappado answered 1/3, 2010 at 6:51 Comment(1)
@Strappado The deprecation of Function.tupled is a result of an understanding that a function must know how to tuple itself without help. However, that really doesn't work well with type inference. I sure hope they don't remove Function.tupled without making the type inferencer smart enough to get around this problem.Shoddy
F
15

UPDATE The deprecation was removed today, in response to this problem.


Tupling a function is simply adapting FunctionN[A1, A2, ..., AN, R] to a Function1[(A1, A2, ..., AN), R]

Function.tuple is deprecated in favour of FunctionN#tupled. A (possibly unintended) consequence of this is that the type inferencer can't infer the parameter types in:

scala> Map(1 -> "one", 2 -> "two") map (_ -> _.length).tupled                 
<console>:5: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$minus$greater(x$2.length))
       Map(1 -> "one", 2 -> "two") map (_ -> _.length).tupled
                                        ^
<console>:5: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$minus$greater(x$2.length))
       Map(1 -> "one", 2 -> "two") map (_ -> _.length).tupled

Any of these will work:

scala> Map(1 -> "one", 2 -> "two") map { case (a, b)  => a -> b.length }
res8: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 2 -> 3)

scala> Map(1 -> "one", 2 -> "two") map ((_: Int) -> (_: String).length).tupled           
res9: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 2 -> 3)

scala> Map(1 -> "one", 2 -> "two") map ((p: (Int, String))  => p._1 -> p._2.length)
res12: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 2 -> 3)

I suggest you read the answers to this recent question to get a deeper understanding of '_' in function literals, and how type inferencing works:

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

UPDATE

In answer to the comment, it does.

scala> val f = (x:Int, y:String) => x + ": " + y
f: (Int, String) => java.lang.String = <function2>

scala> f.tupled
res0: ((Int, String)) => java.lang.String = <function1>

scala> Map(1 -> "1") map f.tupled
res1: scala.collection.immutable.Iterable[java.lang.String] = List(1: 1)

This requires Scala 2.8. Note that Map#map can result in another map, if the return type of the function is a Tuple2, otherwise a List, as above.

Fluted answered 1/3, 2010 at 7:17 Comment(2)
Thank you for the answer. What confuses me is this does not work: val f = (x:Int, y:String) => x + ": " + y; f.tupled. Should not that return a version of f that works on a (Int,String) tuple?Strappado
ok, I see, that explains a lot. I retested with 2.8.0.Beta1 and it works as expected. I tried earlier with 2.8.0.Beta1-RC5 which I had in my path. I need to get rid of RC5...Strappado

© 2022 - 2024 — McMap. All rights reserved.