Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I improve Scala's type inference with type parameters that don't show up in the first parameter list?

To illustrate my point, here an example:

abstract class Wrapper[A](wrapped: A) {

  protected def someCondition: Boolean

  def fold[B](whenTrue: => B)(whenFalse: => B): B =
    if (someCondition) whenTrue else whenFalse

}

I'm trying to add a fold method based on an arbitrary condition defined on a wrapped type A. The problem with the code above is that this wouldn't compile, although it could conceivably return Any:

wrapper.fold("hi")(42)

because by the time the compiler reaches the second parameter list, B has already been inferred to be String. Suppose we don't want to have to write the type annotation. We can try changing fold to this:

def fold[B, B0 >: B](whenTrue: => B)(whenFalse: => B0): B0

but this also doesn't work, since B0 has already been resolved as String at the end of the first parameter list, although it doesn't appear in it at all! The simple solution, of course, is to have a single parameter list, but f the sake of the example, let's say I want to keep the two parameter lists and try to make it work… Ideally, we should be able to delay the resolution of B0. It would be great if we could write something like this:

def fold[B](whenTrue: => B)[B0 >: B](whenFalse: => B0): B0

But unfortunately this doesn't work. Are there any workarounds?

(I'm providing a first answer, but I'm of course looking for other workarounds as well.)

like image 584
Jean-Philippe Pellet Avatar asked Aug 05 '11 09:08

Jean-Philippe Pellet


2 Answers

It seems that you want to mimic, thanks to compilation, the behavior and the goal of the Either class. What you can do is the following: your fold will return an Either object and get your B0 value from it:

abstract class Wrapper[A](wrapped: A) {

  protected def someCondition: Boolean

  def fold[A, B](whenTrue: => B)(whenFalse: => A): Either[A, B] =
    Either.cond(someCondition, whenTrue, whenFalse)

}

And let the implicit conversion either2mergeable of the Eitherclass do the work:

scala> new Wrapper[Unit] {def someCondition = true}
res0: Wrapper[Unit] = $anon$1@77026e40

scala> res0.fold(42)("hi").merge
res1: Any = 42

Pro:

  • The Either structure allow you to directly retrieve A and B types
  • You can check what part was applied during fold

Con:

  • You do not obtain the result directly
like image 138
Nicolas Avatar answered Sep 19 '22 02:09

Nicolas


One solution is to create a temporary instance of a class that defines an apply method simulating the second parameter list, which has itself the B0 type parameter:

abstract class Wrapper[A](wrapped: A) {

  // ... as before...

  def fold[B](whenTrue: => B) = new FoldRequest[B](whenTrue)

  class FoldRequest[B](whenTrue: => B) {
    def apply[B0 >: B](whenFalse: => B0) =
      if (someCondition) whenTrue else whenFalse
  }
}

Then everything works correctly. Could we even imagine that def fold[B](whenTrue: => B)[B0 >: B](whenFalse: => B0): B0 could be interpreted as syntactic sugar for this?…

like image 31
Jean-Philippe Pellet Avatar answered Sep 21 '22 02:09

Jean-Philippe Pellet