Currying Type Parameters in Scala

May 31, 2016

Scala doesn’t provide facilities to supply only some of the type parameters required by a method. If you wish to supply any of them, you must supply all of them.

For instance, let’s say we wished to create a method to safely down-cast a value from one type to a possible subtype, but enforce at compile-time that the target type is indeed a subtype of the value’s class. Here’s a possible implementation -

While this works, we must specify both the target subtype and the input type -

scala> down[Any, Int](1)
Option[Int] = Some(1)

scala> down[Any, Int]("foo")
Option[Int] = None

scala> down[CharSequence, String]("foo": CharSequence)
Option[String] = Some(foo)

Ideally, we’d like the input type inferred by the compiler, but unfortunately, Scala syntax doesn’t permit this.

One clever way of getting around this might be to use an anonymous class to create a structural type -

Note that when using our new method, we’ll get the following warning -

scala> down[Int](1: Any)
<console>:13: warning: reflective access of structural type member method apply should be enabled
by making the implicit value scala.language.reflectiveCalls visible.
This can be achieved by adding the import clause 'import scala.language.reflectiveCalls'
or by setting the compiler option -language:reflectiveCalls.
See the Scala docs for value scala.language.reflectiveCalls for a discussion
why the feature should be explicitly enabled.
       down[Int](1: Any)
           ^
Option[Int] = Some(1)

In order to continue using the method and not receive warnings from its usage, we’d need to avoid the anonymous class or enable reflectiveCalls. I’ll demonstrate how we can avoid the anonymous class and reflectiveCalls, but first let’s ensure our logic works as intended.

While this works, it doesn’t seem to enforce our compile-time check that the input type is a proper supertype of our target subtype. The problem here has to do with type lubbing, which basically means that the compiler will infer type parameters as the nearest common supertype. This becomes more apparent if we output the input type’s class -

scala> down[String](1: Int)
** B == class java.lang.Object
Option[String] = None

To avoid type lubbing, we can enforce that evidence exists proving that A is a subtype of B. This works since the evidence is checked after the B type parameter is inferred.

Now the compiler rejects the cases it can prove will never succeed. While you could stop here, I have a few issues with this implementation -

  • We have warnings to address (as I prefer to use -Xfatal-warnings)
  • The method return type is inferred, while I prefer public methods to have explicit return types to make things simpler for us humans.
  • The inferred return type is a structural type, of course, and that’s messy to annotate.

Ok, so let’s knock out three birds with one stone and wrap it in a utility object -

We define an explicit class to handle our downcasting. This avoids the anonymous class, avoids reflectiveCalls, and gives us clean and explicit return types. We define the lazy val _down to give us a singleton instance of the _Down class to avoid creating a new instance every time we call the method.

scala> CastUtil.down[Int]
CastUtil._Down[Int] = CastUtil$_Down@2e6a8155

scala> CastUtil.down[Int]
CastUtil._Down[Int] = CastUtil$_Down@2e6a8155

scala> // ^^ same instance both times

scala> CastUtil.down[String]("foo": CharSequence)
Option[CharSequence] = Some(foo)

scala> CastUtil.down[String](1: Any)
res9: Option[String] = None

scala> CastUtil.down[String](null: CharSequence)
Option[CharSequence] = None

scala> CastUtil.down[String](1: Int)
<console>:10: error: Cannot prove that String <:< Int.
              CastUtil.down[String](1: Int)
                                   ^