2

When I write a function like def foo[A,B], what exactly does the [A,B] mean? I know it's a Polymorphic Method; but when do you use foo [A] versus foo [A,B]?

Here's an example where I don't understand the difference. This function compiles:

def map[B](f: A => B): Stream[B] =                                               
  foldRight(empty[B])((h,t) => cons(f(h), t))

Whereas this one doesn't compile. I don't understand why the A isn't required, after all A is referenced by the f: A => B:

def map[A,B](f: A => B): Stream[B] =                                            
  foldRight(empty[B])((h,t) => cons(f(h), t))

[error] ..../Stream.scala:61: type mismatch;
[error]  found   : h.type (with underlying type A)
[error]  required: A
[error]     foldRight(empty[B])((h,t) => cons(f(h), t))

(this is from one of the FP in Scala exercises)

Addendum

After reading the answers I'm adding some context, to help future readers. The function is defined inside a trait:

trait Stream[+A] { 
  ...
  def map[B](f: A => B):Stream[B] = 
    foldRight(empty[B])((h,t) => cons(f(h), t))
  ...
}

So the error was being caused by type shadowing, but see @acjay's excellent answer below.

Googling scala type shadowing doesn't result in a direct definition in any of the scala docs, which is interesting because as @travisbrown says below, it's "one of the most common sources of beginner confusion I've seen". There is a discussion here: Why does Scala support shadow variables?

Community
  • 1
  • 1
Sonia Hamilton
  • 4,229
  • 5
  • 35
  • 50
  • where did you see the `map` method? If it's a member of a class, the class itself might define the `A` type parameter – mucaho Feb 22 '15 at 13:11
  • You are defining `map` in a class that defines `Stream[A]` (I assume), so the abstract type `A` is already defined and `map` only needs to have an abstract name for the result type of the function. – fedragon Feb 22 '15 at 13:22
  • It means that you're defining a function that deals with two different types that you don't know yet, and since you don't know them you're going to give them names (A. B) as placeholders. You're telling the compiler that when a user calls the function, it should replace A and B with the right types with respect to the place where the function was used. – Rich Henry Feb 22 '15 at 14:30
  • Thanks @mucaho and @ Freidereikhs, it was indeed part of a stream trait, I'll update the question to add some context. – Sonia Hamilton Feb 22 '15 at 21:47

1 Answers1

4

In the first example, there's only one type parameter because that's not a function in isolation, it's a method in class Stream[A], which declares the first type parameter. Approximately like so:

class Stream[A] {
  // ...

  def map[B](f: A => B): Stream[B] =                                               
    foldRight(empty[B])((h,t) => cons(f(h), t))

  // ...
}

So you get the A from the surrounding class scope. Suppose instead that you made map a method on the companion object instead. In this case, the companion object has no type parameter, so you have to define both parameters on the method:

class Stream[A] {
  // ... methods, with foldRight, but no `map`
}

object Stream {
  // ...

  def map[A, B](stream: Stream[A])(f: A => B): Stream[B] =                                               
    stream.foldRight(empty[B])((h,t) => cons(f(h), t))

  // ...
}

This second option would be used slightly differently. You would say Stream.map(myStream)(myMappingFunction) instead of myStream.map(myMappingFunction). Both options are perfectly valid, but it's probably more idiomatic to put the method in the class.

So, to answer your question, you use multiple type parameters on a method when two or more of the arguments and/or return type need to be generic. You might also use two type parameters for classes as well, like (disregarding variance):

Type Map[A, B] - A is the key type and B is the value type

Type Tuple2[A, B] (better known as (A, B)) - A is the type of the 1st element, B is the type of the 2nd element

Type Function1[A, B] (better known as A => B) - A is the argument type, B is the return type

...and so on.

acjay
  • 34,571
  • 6
  • 57
  • 100
  • Thank you very much @acjay for your excellent answer, I now understand. I'll add an addendum to my question to provide some context. – Sonia Hamilton Feb 22 '15 at 21:45