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 d开发者_如何学Goefined 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.)
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 Either
class 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
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?…
精彩评论