5

I'm having trouble mapping a function that takes an optional parameter. I get the same type error as I would if the parameter were not optional. Here's a simple illustration:

scala> def multiple(m: Int, n: Int = 2) = m * n
multiple: (m: Int,n: Int)Int

scala> multiple(5)
res0: Int = 10

scala> multiple(5, 7)
res1: Int = 35

scala> (1 to 10).map(multiple)
<console>:7: error: type mismatch;
 found   : (Int, Int) => Int
 required: (Int) => ?
       (1 to 10).map(multiple)

Here's one way to make it work, but it requires repeating the default argument, which is a maintenance nightmare:

scala> (1 to 5).map { n => multiple(n, 2) } 
res6: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)

Is there a better way to do it? More generally, why does a function with an optional parameter seem to have the same type as it would if the parameter was not optional? What is the actual type of multiple?

0

4 Answers 4

12

This seems to work:

(1 to 10).map(multiple(_)) 

//res0: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks; this is the solution I'll use. Can you explain why the compiler treats map(multiple) and map(multiple(_)) differently?
@dkh: I guess the real reason is that the first parameter of multiple function is required.
6

When used in a situation expecting a function, Scala will "lift" a method to a FunctionN[T1,...,R].

In this situation, because multiple takes 2 parameters, it is lifted effectively as:

(1 to 10).map(new Function2[Int,Int,Int]{ def apply(v1: Int, v2: Int) = multiple(v1, v2) })

Even though the original method has a default argument, FunctionN objects do not. The type error should now be clear here. When multiple(_) is used, this is a call to multiple with a single argument with the second defaulted and so is treated like:

(1 to 10).map(new Function1[Int,Int]{ def apply(v1: Int) = multiple(v1) })

This type checks ok as others have shown.

Note that (multiple _ ) is not the same as multiple(_). The former represents multiple with all arguments wild carded and so is a Function2, whereas the latter is applying multiple to a single wild card argument, causing the other argument to be defaulted at that point, and so is a Function1.

Defaults are implemented at compile time by introducing a new method which returns the default value. Where the defaulted method is called, if arguments are missing, the compiler will add the necessary calls to the extra methods for the default parameters before adding the call to the method itself. This means that the method itself is compiled to code which itself has no knowledge of default parameters. To see this, compile the following sample class:

class Defaults {
  def m(a: Int, b: Int = 3) = a * b
  def a = m(1)
  def b = m(1, 2)
}

then run: javap -c Defaults

Comments

1

For being able to write

scala> (1 to 10).map(multiple)

you can pass a partially applied function

def multiple(m: Int, n: Int) = m * n
val mul2 = multiple(_: Int, 2)
(1 to 10) map mul2

Comments

0

Here's one way to make it work, but it requires repeating the default argument, which is a maintenance nightmare:

Btw this works too:

scala> (1 to 5).map { n => multiple(n) } 
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)

So you don't need to repeat the default argument either ;) [Scala 2.9.0.RC1]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.