9
votes

Suppose this function

def func[A](data: List[A], mapper: A => String) = { 
  data.map(item => mapper(item)) 
}

Why this code doesn't compile:

val list = List(1, 2, 3)
func(list, a => a.toString)

But this one does:

val list = List(1, 2, 3)
func[Int](list, a => a.toString)

Or

val list = List(1, 2, 3)
func(list, (a: Int) => a.toString)

While a type can be inferred from list which is List of Int. Why doesn't scala infer the type here?

Is there any other way?

1

1 Answers

13
votes

There is another way! It also happens to make for some nice syntatic sugar:

def func[A](data: List[A])(mapper: A => String) = data map mapper

which looks like:

func(myList){
  case Left(one) => one
  case Right(_) => default
}

The reason that you can not get the type information to flow the way you'd expect is that type information in Scala is left to right. In other systems, type information is known and deduced for useage where it is defined. You sometimes have to work around these limitations but at the same time, in this case, you can get to work with something that looks akin to your own defined control structure.

So...

func[Int]( //I've just told the typer what the information is and it can flow to the right.
func(list //the typer has to deduce the type and without a guide can not figure out what a => a.toString should be

This is also an old "issue" you can see here SI-4773.

Response to Q in Comment:

If you want to have a Seq[A => B] then I'd do something similar to

func[A, B](data: List[A])(actions: A => B*) = actions map { 
  data map
}

which is using varargs (translates to a WrappedArray, hence the map) to accept any list of commands so that you can pass is

func(list)(_.name, _.age, _.sex, _.stalker)

as far as pulling out and matching on what you've passed in:

func[A, B](data: List[A])(actions: (String, A => B)*) = actions map { 
  case (name, f) => (name, data map f)
}

wherein you're using the case statement to pattern match and extract the tuple.