2

This example is taken from an exercise of "Essential Scala" from Underscore.io. Following is definition of algebraic sum type Maybe[A] which mimics some basic features of Option[A] and a list of Maybe[Int].

sealed trait Maybe[A] {
  def flatMap[B](fn: A => Maybe[B]): Maybe[B] =
    this match {
      case Full(v) => fn(v)
      case Empty() => Empty[B]()
    }

  def map[B](fn: A => B): Maybe[B] =
    this match {
      case Full(v) => Full(fn(v))
      case Empty() => Empty[B]()
    }
}
final case class Full[A](value: A) extends Maybe[A]
final case class Empty[A]() extends Maybe[A]

val list = List(Full(3), Full(2), Full(1))

I tried to replace the elements in list with odd values with Empty[Int] therefore resulting List(Empty(), Full(2), Empty()) by this statement:

list.map(maybe => maybe flatMap { x => if(x % 2 == 0) Full(x) else Empty() })

and this was the exactly same with the answer from the book.

But I got an error:

Error:(41, 26) no type parameters for method flatMap: (fn: Int => A$A22.this.Maybe[B])A$A22.this.Maybe[B] exist so that it can be applied to arguments (Int => Product with Serializable with A$A22.this.Maybe[_ <: Int])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Int => Product with Serializable with A$A22.this.Maybe[_ <: Int]
 required: Int => A$A22.this.Maybe[?B]
list.map(maybe => maybe flatMap { x => if(x % 2 == 0) Full(x) else Empty() })
                        ^

So I wrote type parameter then It worked well:

list.map(maybe => maybe flatMap[Int] { x => if(x % 2 == 0) Full(x) else Empty() })

I thought it would be ok even if I don't provide the type parameter Int because it seems that it was possible to infer the type parameter from the type of argument for flatMap x => if(x % 2 == 0) Full(x) else Empty(). What is wrong? I heard that Product and Serializable automatically mix-in into case class so that the type of the function literal above is Int => Product with Serializable with A$A22.this.Maybe[_ <: Int] as shown in the error message. Is this related to the issue?

1 Answer 1

1

The problem is Empty(): the compiler has no reason to pick any type parameter for it other than Nothing (in particular, it doesn't look at if's other branch). So it ends up with Full[Int] in one branch and Empty[Nothing] in another, and their common type is Product with Serializable with Maybe[_ <: Int], as shown in the error message. From this result type, obviously the type parameter for flatMap can't be inferred.

When defining a trait which should only be extended by case classes, it's normal to include trait Maybe[A] extends Product with Serializable so they'll be subsumed in such upper bound calculation, but you'll still end up with Maybe[_ <: Int] (in case you are unfamiliar with this notation, it means "Maybe whose type parameter is some subtype of Int"): the compiler notices that Full[Int] is a subtype of Maybe[Int] and Empty[Nothing] is a subtype of Maybe[Nothing], but Maybe[Nothing] is not a subtype of Maybe[Int].

I expect you'll see the better way to fix the problem in following exercises, but for now the fix is to specify Empty[Int]().

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

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