Kevin Meredith
Kevin Meredith

Reputation: 41939

Using "_" in Scala instead of Explicit Types

I tried to write the following method for concatenating 3 lists together:

def f[A]: List[A] => List[A] => List[A] => List[A] = _ ++ _ ++ _

But got this compile-time error message:

<console>:7: error: missing parameter type for expanded function 
   ((x$1, x$2, x$3) => x$1.$plus$plus(x$2).$plus$plus(x$3))
       def f[A]: List[A] => List[A] => List[A] => List[A] = _ ++ _ ++ _

Is it possible to write it more concisely than the following:

scala> def f[A](xs: List[A], ys: List[A], zs: List[A]) = xs ++ ys ++ zs
f: [A](xs: List[A], ys: List[A], zs: List[A])List[A]

Upvotes: 1

Views: 97

Answers (2)

Eugene Zhulenev
Eugene Zhulenev

Reputation: 9734

I know at least 2 ways to do it:

  def f[A]: List[A] => List[A] => List[A] => List[A] =
    a => b => c => a ++ b ++ c

  def f2[A]: (List[A], List[A], List[A]) => List[A] = 
    _ ++ _ ++ _

  val l1 = List(1)
  val l2 = List(2)
  val l3 = List(3)

  println(f(l1)(l2)(l3))
  println(f2(l1, l2, l3))

Your original _ ++ _ ++ _ is a placeholder for function with 3 parameters, but your f is curried function TypeA => TypeB => TypeC => TypeD (all types are equal to List)

So of you still want to use _ ++ _ ++ _ you need to transform your function to 3-input function (or function value of Tuple3 => something)

Upvotes: 6

Erik Kaplun
Erik Kaplun

Reputation: 38247

The problem you're having is due to the variety of ways that in Scala functions can be defined. First there are the "normal" non-curried functions with a signature like (T1, T2, ...) => RetType.

Then there are curried functions of the form T1 => T2 => ... => RetType which is essentially a chain of higher order 1-argument functions returning other 1-argument functions and the end of the chain returns the actual return value. For why this is useful, google for something like "scala curried functions".

The expression _ <someoperator> _ <someoperator> _ ... returns non-curried functions because those are the default in Scala. Therefore, if you declare foo to be returning T1 => T2 => ... => RetType but you actually return (T1, T2, ...) => RetType, the type signatures will mismatch and scalac will complain.

The solution is to either use both a curried signature as well as a curried function as the return value:

def foo1[T]: List[T] => List[T] => List[T] =
  a => b => a ++ b

or use an uncurried signature and an uncurried return value:

def foo2[T]: (List[T], List[T]) => List[T] =
  _ ++ _

the latter of which is the equivalent of:

def foo2[T]: (List[T], List[T]) => List[T] =
  (a, b) => a ++ b

Yet another alternative is to have Scala convert your uncurried function into a curried one:

def foo2[T]: List[T] => List[T] = (_ ++ _).curried

...but, due to reasons that are out of the scope of this question, the type inferencer won't be able to be of much use, so you have to annotate and lose the concise nature of _ in the first place:

def foo2[T]: List[T] => List[T] = ((_: List[T]) ++ (_: List[T])).curried

Upvotes: 1

Related Questions