-3

Looking at part of the IO Monad's signatures from Functional Programming in Scala:

trait IO[+A] { self =>
  def run: A
  def map[B](f: A => B): IO[B] =
    new IO[B] { def run = f(self.run) }

As I understand, IO[+A] means that the IO type takes a type parameter of "A or its subclasses."

Looking at def map[B]..., B is a type involved in this function. It's useful to use map[B](f: A => B): IO[B] since, as I understand, you can list B as a return type of f, as well as the returned type parameter of IO?

So, the following implementation would cause a compile-time problem:

map(f: Int => String): IO[Int]

Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384
  • 3
    Sorry, what's the question? ;) – yǝsʞǝla Feb 06 '14 at 03:46
  • basically I'm looking for confirmation/more details in my above example, please :) – Kevin Meredith Feb 06 '14 at 03:47
  • I hope I answered your question. I think it's a good question but it's not easily worded. – yǝsʞǝla Feb 06 '14 at 04:16
  • 1
    @KevinMeredith You're curious about variance in Scala types? Check out this Q&A: http://stackoverflow.com/questions/663254/scala-covariance-contravariance-question – wheaties Feb 06 '14 at 04:27
  • Thanks @wheaties. I've read that post and this solid ones from Rex Kerr (http://stackoverflow.com/questions/4531455/whats-the-difference-between-ab-and-b-in-scala). I'm learning more each time I read it (and review questions like my above one) – Kevin Meredith Feb 06 '14 at 04:40

2 Answers2

3

I'm not sure what you mean by "implementation" in this context: map(f: Int => String): IO[Int]. If you mean calling map passing a function Int => String, then that returns IO[String] -- the callee doesn't get a say in what type is returned.

If you mean overriding the implementation of map (which you shouldn't do) with override def map(f: Int => String): IO[String], then that isn't overriding anything, because you can't remove a type parameter when overriding, and you can't change the type of the parameters it receives either, and you can change the return type to a subtype, but IO[String] isn't a subtype of IO[B] so that's different as well.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
1

So +A means similar thing as T <: A. That means "for all subtypes of A including A". This "limitation" is useful here because you are supposed to pass f function to the map method and you are restricting it to specific type A and it's subtypes. Thus when you extend the trait in your class you allow for substitution of f while being typesafe.

B here is a result of applying f to A. Since it's a Monad you get back not just B but an IO[B]. In other words it might fail and you might get B wrapped in "success" or some sort of "failure".

Notice that the type of self is +A, and a newly created IO[B] does not actually compute anything, it just wraps around result of a run which has to be B. Since you expect failures to occur in IO and since it's a Monad you wrap around the result of an application of f(self.run) creating a monad IO[B] (as promised by map method signature).

Finally, as you said map(f: Int => String): IO[Int] should not compile since Int is not a subtype of String.

yǝsʞǝla
  • 16,272
  • 2
  • 44
  • 65