Proposal: 'As' type class for concept of converting from A to B

I propose to introduce a type class into the standard library that can capture the idea of converting from one type to another in a lossless way. Note, I’ll use Scala 2 syntax here because I’m more familiar with it but happy to switch to 3 if this discussion progresses.

trait As[A, B] {
  def apply(a: A): B
}

trait LowPrioImplicits {
  // Etc.
  implicit def seq[A, B](implicit AAsB: A As B): Seq[A] As Seq[B] = _.map(AAsB.apply)
}

object As extends LowPrioImplicits {
  // Extension method
  implicit class Ops[A](private val a: A) extends AnyVal {
    def as[B](implicit AsB: A As B): B = AsB(a)
  }
}

Why a new type class? Throughout Scala, both in the standard library and elsewhere, we see many examples of converting from one type to another. E.g, scala/AsScalaExtensions.scala at 986dcc160aab85298f6cab0bf8dd0345497cdc01 · scala/scala · GitHub

import scala.jdk.CollectionConverters.ListHasAsScala

val javaList: java.util.List[Int] = ...
val scalaSeq: Seq[Int] = javaList.asScala

With the As type class, we could represent this as:

implicit def javaListAsScalaSeq[A]: java.util.List[A] As Seq[A] = ...

Now, we could just do: val scalaSeq: Seq[Int] = javaList.as

This doesn’t stop at just JDK/Scala type conversions, imagine third-party libraries representing e.g. JSON codecs like:

type Decoder[A] = Json As A
type Encoder[A] = A As Json

...

case class MyDomainType(wow: Int)
object MyDomainType {
  implicit val asJson: MyDomainType As Json = derive
}

val payload: Json = myDomainType.as

In fact having a standardized As type class` would make redundant a bunch of specialized types and implicits that exist just to represent the concept of a conversion from one type to another. The type class just generalizes the concept of ‘convert type A to B’.

You might ask, doesn’t a function type A => B represent that already? Yes it does but I think it’s too general. It can mean anything in different contexts. We need something that semantically means ‘transform A to represent it as B’.

Prior art: Rust’s From trait which captures the concept of ‘convert from type T to this type’. However in Scala we can do a type class with two parameters which is more general-purpose.

1 Like

It exists already:

given Conversion[Int, String] = _.toString
val a: String = 1.convert
4 Likes

Ooh, very nice. Thanks!