“Missing parameter type” in for-comprehension when overloading flatMap

614 views
Skip to first unread message

Holger

unread,
Aug 25, 2016, 11:22:53 AM8/25/16
to scala-user
Hello,

I wrote (haven't we all :-) my own Either-like monad class called Maybe with either a value or an error object inside it. I want objects of this class to combine with Future, so that I can turn a Maybe[Future[T], E]] into a Future[Maybe[T, E]]. Therefore I implemented two flatMap methods:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

sealed abstract class Maybe[+E, +V] {

 
def map[W](f: V W ): Maybe[E, W] = this match {
   
case Value(v) Value(f(v))
   
case Error(_) this.asInstanceOf[Error[E, W]]
 
}

 
def flatMap[F >: E, W](f: V Maybe[F, W]): Maybe[F, W] = this match {
   
case Value(v) f(v)
   
case Error(_) this.asInstanceOf[Error[F, W]]
 
}

 
def flatMap[W](f: V Future[W]): Future[Maybe[E, W]] = this match {
   
case Value(v) f(v).map(Value(_))
   
case Error(_) Future.successful(this.asInstanceOf[Error[E, W]]) }
 
}

final case class Value[+E, +V](value: V) extends Maybe[E, V]
final case class Error[+E, +V](error: E) extends Maybe[E, V]

However, when I use the for comprehension to combine a Maybe and a Future which holds another Maybe the Scala compiler gives me the error message missing parameter type at the line of the outer generator:

def retrieveStreet(id: String): Future[Maybe[String, String]] = ...

val outerMaybe
: Maybe[String, String] = ...

val result
= for {
  id
outerMaybe // error message "missing parameter type" here!
  street
retrieveStreet(id)
} yield street

But when instead of using for I call the flatMap and map methods explicitly, it works:

val result2 =
  outerMaybe
.flatMap( id => retrieveStreet(id) )
           
.map( street => street )

(I also get this error message when I try to combine a Maybe with another Maybe in a for comprehension.)

So the questions are:
  1. Shouldn't these two alternatives behave exactly the same? Why does the compiler figure out the correct flatMap method to call, when calling flatMap explicitly?
  2. Since apparently the compiler is confused by the two flatMap implementations, is there a way to tell it (by a type specification anywhere) which one should be called in the for comprehension
I am using Scala 2.11.8 in Eclipse.

Any help would be appreciated.


Greetings,

Holger

Stephen Compall

unread,
Sep 10, 2016, 4:11:34 AM9/10/16
to Holger, scala-user
On 8/25/16 10:22 PM, Holger wrote:
However, when I use the for comprehension to combine a Maybe and a Future which holds another Maybe the Scala compiler gives me the error message missing parameter type at the line of the outer generator:

def retrieveStreet(id: String): Future[Maybe[String, String]] = ...

val outerMaybe
: Maybe[String, String] = ...

val result
= for {
  id
outerMaybe // error message "missing parameter type" here!
  street
retrieveStreet(id)
} yield street

But when instead of using for I call the flatMap and map methods explicitly, it works:

val result2 =
  outerMaybe
.flatMap( id => retrieveStreet(id) )
           
.map( street => street )

This is not the expansion of result.  Here it is:

  val result3 =
    outerMaybe.flatMap( id =>
      retrieveStreet(id).map(street => street) )

Which gives

[error] ....scala:37: missing parameter type
[error]     outerMaybe.flatMap( id =>
[error]                         ^

(I also get this error message when I try to combine a Maybe with another Maybe in a for comprehension.)

So the questions are:
  1. Shouldn't these two alternatives behave exactly the same? Why does the compiler figure out the correct flatMap method to call, when calling flatMap explicitly?

It doesn't in either case, because there's no notion of similarity between overloads.  As soon as you have multiple overloads, you lose the benefit of the "expected type" for the argument, which gives you the lambda argument type, and this is required for both forms.

Expressions of the form x => f(x) where f is a method are treated specially; you get the benefit of the whole type of the f method.  It is like you wrote outerMaybe.flatMap(retrieveStreet).  The expansion you gave for result2 fits this pattern, and therefore does not require an "expected type" for the lambda argument in order to work.  But the true expansion of the for-comprehension, result3 above, does not fit this pattern.


  1. Since apparently the compiler is confused by the two flatMap implementations, is there a way to tell it (by a type specification anywhere) which one should be called in the for comprehension

The problem is overloading, so get rid of overloading.  If you wish to provide these two variants of flatMap, provide a typeclass and two instances, one for Future and one for Maybe.  The signature for the only flatMap method would look something like

  def flatMap[W, Z](f: V => W)(implicit ev: Maybe.FlatMap[E, V, W, Z]): Z

and instances for FlatMap[E, V, Maybe[F, W], Maybe[F, W]] and FlatMap[E, V, Future[W], Future[Maybe[E, W]]] in Maybe.FlatMap's companion.

Greetings,

Stephen.
Reply all
Reply to author
Forward
0 new messages