views:

215

answers:

2

If I have a function:

f : A => B => C

I can define an implicit conversion such that this can be used where a function (A, B) => C is expected. This goes in the other direction also.

Why are these conversions not implicit (or available implicitly)? I am assuming that bad things could happen for some value of bad things. What value is this?

+8  A: 

I don't think anything bad will happen. The conversion is completely unambiguous. Worst case, Scala will not be able to figure out that the implicit conversion applies.

implicit def curryImplicitly[A,B,C](f: (A, B) => C) =
  (a: A) => (b: B) => f(a, b)
implicit def uncurryImplicitly[A,B,C](f: A => B => C) =
  (a: A, b: B) => f(a)(b)

Then again, these would also be helpful.

implicit def flipImplicitly[A,B,C](f: (A, B) => C) =
  (b: B, a: A) => f(a, b)
implicit def flipImplicitlyCurried[A,B,C](f: A => B => C) =
  (b: B) => (a: A) => f(a)(b)

But those aren't transitive, so you need these:

implicit def flipAndCurry[A,B,C](f: (A, B) => C) =
  (b: B) => (a: A) => f(a, b)
implicit def flipAndUncurry[A,B,C](f: A => B => C) =
  (b: B, a: A) => f(a)(b)

But now the conversion is ambiguous. So it's not all roses.

Let's know how it works out in practise. You might need equivalents for Function3, Function4, etc.

Apocalisp
I've not played with this in 2.8, but I tried this back in the dark days of 2.7.X and it tended to lead to compiler crashes, in the type inferencer iirc. Things have improved a fair bit on that front, so maybe it's all good now...
Nick Partridge
Yeah, it's still all too easy to crash the compiler if you try to make it infer a higher-kinded type, but it's a huge improvement over 2.7.
Apocalisp
+6  A: 

You don't want them implicitly available by default (always-on) because then the type system has trouble helping you out when you have overloaded with arguments of a bunch of similar types:

A => B => C
D => C      // D is allowed to be a tuple (A,B)...

(A,B) => C  // If I have this, to whom should I convert?

Part of the advantage of strong typing is warning you when you've done something foolish. Trying too hard to make things work reduces the benefits. Here, if the conversions were done automatically, you might not call the method you meant to call.

Having them available implicitly upon request is fine, but it's not that hard to do it yourself if you need it. This is something that I would use quite rarely; I wouldn't put it in my top ten or probably even top hundred things that I'd like in the library (in part because I might prefer the automatic conversion to a tuple instead of the automatic currying/uncurrying).

Rex Kerr
Note that you will not be able to overload a function with arguments `A => B => C` and `D => C`, because they have the same erasure. So the problem will not come up this way in practise.
Apocalisp
Ah, right you are. It can still be helpful to have type signatures as a double-check if you use different method names but get the method name wrong.
Rex Kerr