Type bounds, maybe?


#1

How can I “merge” these two methods into one:

  def m[A, B](x: A, f: A => B): Widget[B] = new Widget(f(x))

  def m[A](x: A): Widget[A] = m(x, identity[A])

I’d like to have a single method where f is optional and, when it’s not provided, the method produces a Widget[A]. I’m trying to bring the A =:= B bound into play, but I can’t see how. (I can live with a Curried version of the method where f is implicit, if necessary).

EDIT: This works in Curried form (identity is available implicitly):

  def m[A, B](x: A)(implicit f: A => B): Widget[B] = new Widget(f(x))

How about the non-Curried form?


#2

I don’t know if this will be possible. Possibly with some approach more clever than I am. The big problem you’re going to face is how type inference happens (whole-parameter block at a time, left to right on the type variables).

So doing something like:
def m[A, B](x: A, f: A => B = identity[A] _): Widget[B] = new Widget(f(x))

won’t be too satisfactory:
m(12) will fail because type parameter B will be inferred to be Nothing. You can make this work if you specify that both A and B are the same though:
m[Int, Int](12). However, that’s (to me) more obnoxious than just overloading the function based on arity.


#3

This little tweak fixes the problem virus_dave pointed out:

scala> case class Widget[A](a: A)

defined class Widget

scala> def m[A, B](x: A)(implicit f: A => B = identity[A] _): Widget[B] = new Widget(f(x))

m: [A, B](x: A)(implicit f: A => B)Widget[B]

scala> m(‘x’)(_.toString)

res1: Widget[String] = Widget(x)

scala> m(12)

res2: Widget[Int] = Widget(12)

Best regards,

Brian Maso


#4

Update – since function identity is implicitly provided by Predef, there’s no need to give a default value for implicit parameter f of function m:

scala> case class Widget[A](a: A)

defined class Widget

scala> def m[A, B](x: A)(implicit f: A => B): Widget[B] = new Widget(f(x))

m: [A, B](x: A)(implicit f: A => B)Widget[B]

scala> m(‘x’)(_.toString)

res1: Widget[String] = Widget(x)

scala> m(12)

res2: Widget[Int] = Widget(12)


#5

But that’s the Curried version I already have.


#6

But that’s the Curried version I already have.

Exactly. I’m not a scala language expert, but it seems like you’re asking something that’s not really possible: infer a type for B when B in this case is only specified by the default polymorphic function in the first parameter group, so you’ll get Nothing.

That’s not how scala’s type variable inference rules work (Haskell would be fine here, but scala != haskell). Common ways i’ve handled this problem are:

  1. use multiple parameter sections
  2. use multiple overloaded methods with differing arities.

Usually one or the other makes more sense depending on the context / use case.

It’s not clear why those are undesirable in your case. Can you elaborate? Or just intellectual curiosity?


#7

Mostly intellectual curiosity. I sometimes deal with bloated Java interfaces with multiple overloading of each method, and I’ve often found that this can be avoided in Scala with default values and implicit arguments (and the absence of primitive types, of course). But I can’t find a way to do it here.

Argument f is an optional transformation of one component of the widget. It’d be nice to have a single method (if you don’t specify a transformation, there is none), but the Curried form feels weird (Currying would feel more natural with the arguments reversed, but then implicit would not work).


#8

Why? Putting the function last in its own parameter list is the general idiom in Scala–it’s the norm.


#9

Because m(f) as a widget-creating mechanism makes sense to me while m(x) as a virtual widget in wait of a transformation (e.g., component renaming) doesn’t. For the same reason, I would define a Curried map as map(f)(list), never as map(list)(f). It’s probably all subjective (and a holdover of my SML days).


#10

That’s the style in functional languages with auto-curried functions. Scala is an object-functional language with uncurried methods and functions. Therefore the idioms are different.