NewType is New Now!

Existential Types

trait Foo {
type
Type
def get: Type
}
object X {
val
foo: Foo =
new Foo {
override type
Type = Int
override def get: Type = 1
}
val int: Int = foo.get
// Error: type mismatch;
// found : foo.Type
// required: Int
val int1: foo.Type = foo.get // compiles
val int2: Int = foo.get // doesn’t compile

Initial Approach

trait NewType[A] {
type
Type
def wrap(a: A): Type
def unwrap(a: Type): A
}
def newType[A]: NewType[A] =
new NewType[A] {
type Type = A
override def
wrap(a: A): Type = a
override def unwrap(a: Type): A = a
}
val Mult: NewType[Int] = newType[Int]type Mult = Mult.Typeimplicit val multiplyingMonoid =
new Monoid[Mult] {
override def
empty: Mult = Mult.wrap(1)
override def
combine(
x
: Mult, y: Mult
): Mult = Mult.wrap(Mult.unwrap(x) * Mult.unwrap(y))
}

Scala subtyping to get away from boilerplate

trait NewType[A] {
type
Type <: A // changed
def wrap(a: A): Type
def unwrap(a: Type): A
}
val Sum: NewType[Int] = newType[Int]
type
Sum = Sum.Type
implicit val summingMonoid =
new Monoid[Sum] {
override def
empty: Sum = Sum.wrap(0)
override def
combine(x: Sum, y: Sum): Sum = Sum.wrap(x + y)
}

Ergonomics

trait NewType[A] {
type
Type <: A
def apply(a: A): Type
}
def newType[A]: NewType[A] =
new NewType[A] {
type
Type = A
override def apply(a: A): Type = a
}
val Sum: NewType[Int] = newType[Int]
type
Sum = Sum.Type
implicit val summingMonoid = new Monoid[Sum] {
override def
empty: Sum = Sum(0)
override def
combine(x: Sum, y: Sum): Sum = Sum(x + y)
}
trait NewType[A] {
type
Type <: A
def apply(a: A): Type
def toF[F[_]](fa: F[A]): F[Type]
def
fromF[F[_]](fa: F[Type]): F[A]
}
def newType[A]: NewType[A] =
new NewType[A] {
type
Type = A
override def apply(a: A): Type = a
override def
toF[F[_]](fa: F[A]): F[Type] = fa
override def
fromF[F[_]](fa: F[Type]): F[A] = fa
}
val Mult: NewType[Int] = newType[Int]
type
Mult = Mult.Type
val list1: List[Mult] = List(1, 2, 3).map(Mult(_))
val list2: List[Mult] = Mult.toF(List(1, 2, 3))
trait Eq[A] {
def
eqv(x: A, y: A): Boolean
}
implicit val eqMultManual: Eq[Mult] =
new Eq[Mult] {
override def
eqv(x: Mult, y: Mult): Boolean = x == y
}
implicit val eqInt: Eq[Int] = ???implicit val eqMult: Eq[Mult] = Mult.toF(eqInt)…and use them:val sameMult = implicitly[Eq[Mult]].eqv(Mult(1), Mult(1))val sameInt = implicitly[Eq[Int]].eqv(1, 1)Similarly, we can ‘demote’ an instance of Eq[Mult] to Eq[Int], since Mult really is an Int under the covers.implicit val eqMult: Eq[Mult] = ???implicit val eqInt: Eq[Int] = Mult.fromF(eqMult)

Type constructor ergonomics

implicit val eqInt: Eq[Int] = ???implicit val eqMult: Eq[Mult] = Mult.toF(eqInt)If the typeclass parameter is covariant, the fromF method becomes redundant”trait CovariantTypeclass[+A] { … }implicit val eqMult: CovariantTypeclass[Mult] = ???implicit val eqInt: CovariantTypeclass[Int] = Mult.fromF(eqMult)implicit val eqInt2: CovariantTypeclass[Int] = eqMult // fromF not required….. etc

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store