*Explicit conversion*: When and What do you use?

Sometimes it is nice to have different interpretations of the same data (for example, Point as (x, y) or (dist, angle)). Coming from Rust, there is a standard, almost built-in trait to express that one can perform a conversion from one to the other:

trait From<U> {
  fn from(u: U) -> Self;
}

trait Into<T> {
  fn into(self: Self) -> T;
}

(normally you define From instances, and Into is provided for free).
Their docs are From and Into.

I suppose the equivalent Scala trait would be

trait From[U, T]:
  def from(u: U): T

This is fairly useful in Rust, especially in argument-position, like so:

fn norm(intoPt: impl Into<Point>) -> f64 {
  val pt = intoPt.into();
  (pt.x * pt.x + pt.y * pt.y).sqrt()
}

The above function is implicitly generic on the type of pt, effectively creating a hidden type variable with a context bound Into<Point>. Once inside the function, we can just call the .into() method to convert the argument into type Point.
This is not as convenient as having implicit parameters on the implementor’s side, but is just as convenient on the user’s.

Some interesting built-in trait implementations are:

  • From<T> for T (obviously identity)
  • From<T> for Option<T> (makes optional parameters nicer!)

In Scala, I found the Conversion trait to be really close to From/Into, but it seems to be more of a implicit conversion thing than explicit conversions.
Furthermore, the obvious instance of

inline given[T]: Conversion[T, T] = identity

is somehow missing, making using it quite… annoying to use.

For reference, norm can be implemented in Scala with Conversion like so:

type Into[T] = [U] =>> Conversion[U, T]

def norm[T: Into[Point]](intoPt: T): Double =
  val pt = intoPt.convert
  (pt.x * pt.x + pt.y + pt.y).sqrt

Since Conversion doesn’t seem to be widely used, I wanted to ask that:

  • Does explicit conversions come up often in your code? and if so, …
  • How do you manage them? Through your own trait, with .asX methods, or something else?

It comes up occasionally, enough that I slapped together a typeclass called Convertible for that purpose, but it really depends on the domain I’m working in (it doesn’t come up much at work, as an example).

1 Like

Existing trait Conversion[X,Y] (Conversion) ideally fit for this purpose. Maybe it lacks facilities because ‘explicit magnet’ is not a widely used technique [yet?].

As far as I know, nothing requires the developer to make conversion implicit.

(Except in documentation, we see 'A class for implicit values that can serve as implicit conversions. '.
Maybe it is better to change the documentation and say that this is a class for Conversion and that conversion can be implicit (or can not) ). ?

We have the last sentence about explicit conversion:
The Conversion class can also be used to convert explicitly, using the convert extension method.

Maybe make it first ? (and add identity conversion)

P.S.

Does explicit conversions come up often in your code? and if so, ….

– interesting, not at all. Sometimes I have conversions from F[X] to G[X], which is require other typeclass.

Not quite the same thing, since it’s aimed primarily at serialization and similar large-scale conversion problems, but Rally’s weePickle library (a deep fork of Haoyi’s microPickle) follows a similar From/To philosophy.

It’s a remarkably powerful way of thinking about conversion; I wish more folks thought in these terms…