So, I was trying to "pimp" my Future
s (among other things) a little bit with something like this:
implicit class Pimp[T](val x: T) extends AnyVal {
def ->>[R](f: T => R): R = f(x)
def ->>[R](r: => R): R = r
def <<-(f: T => Unit): T = f.andThen(_ => x).apply(x)
def <<-(f: => Unit): T = <<- { _ => f }
}
For some reason, when I try to do this now:
Future(1) ->> { Await.result(_, 1 seconds) }
it fails with error: missing parameter type for expanded function ((x$1) => Await.result(x$1, 1.seconds))
But this works:
Future(1) <<- { Await.result(_, 1 seconds) }
So, the question is what is wrong with the first case. What am I doing wrong, and how to make it work.
I even tried this (not at all what I want, because it is way too verbose): Future(1) --> { case x: Future[Int] => Awayt.result(_, 1 seconds) }
, but even this fails for some reason (still says the parameter type is unknown - how is it unknown, if I explicitly specify it???)
I suspect, the difference between the first case and second is that ->>
has a type param, while <<-
does not ... But even this Future(1) ->>[Int] { Await.result(_, 1 seconds) }
still does not work :(
Update Ok, so, I found one way in which it works:
Future(1) ->> { (f:Future[Int]) => Await.result(f, 1 seconds) }
This kinda defeats the purpose of my implicit though, because of how verbose it is :( Is there any way to make it infer the parameter type without spelling it out like it does in the other case?
Update 2 One more thing. Removing the overloaded "flavor" of ->>
from Pimp
makes the other case work. I.e., if I only leave
def ->>[R](f: T => R): R = f(x)
in the definition of Pimp
, then
Future(1) ->> { Await.result(_, 1 seconds) }
beautifully works as intended.
Having thought about it for a while, I think, that the problem is that in the overloaded case foo ->> { _ => bar }
is ambiguous: it could mean "return bar", or "return a function foo => bar". This does not explain however why foo ->>[Bar] { _ => bar }
does not work.
You can shorten the call to Future(1) ->> { Await.result(_: Future[Int], 1 seconds) }
. Not perfect, but better than your Update 1. Even better would be to avoid overloading in "pimped" methods: unsurprisingly, it complicates type inference.
After reading http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html carefully, it seems that specifying [Int]
shouldn't work:
The entire expression is a function application (6.6).
Since Future(1).->>
is overloaded, we go to 6.23.
The set of alternatives A is the set of members referenced by ->>
; [Int]
isn't taken into account, because the section talks about "identifier or selection e
", and the selection is Future(1).->>
.
shape({ Await.result(_, 1 seconds) })
is Any => Nothing
.
"Let B be the set of alternatives in A that are applicable to expressions (e1,…,en) of types (shape(e1),…,shape(en))". Both methods in A are applicable to Any => Nothing
(as you say, "it could mean "return bar", or "return a function foo => bar"). So we go to "Otherwise, let S1,…,Sm be the vector of types obtained by typing each argument with an undefined expected type" and this typing fails.
For the <<-
case, => Unit
is not applicable (because the expected type is not Unit
at this step, value discarding doesn't happen).
But it's subtle enough that I've changed my mind twice while writing this update; I think I've got it right now.