Skip to content

Instantly share code, notes, and snippets.

@milessabin
Last active September 16, 2019 13:44
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save milessabin/cadd73b7756fe4097ca0 to your computer and use it in GitHub Desktop.
Save milessabin/cadd73b7756fe4097ca0 to your computer and use it in GitHub Desktop.
A new approach to encoding dependently-typed chained implicits, using singleton types ...
object Demo {
// A couple of type classes with type members ...
trait Foo[T] {
type A
}
object Foo {
implicit val fooIS = new Foo[Int] { type A = String }
}
trait Bar[T] {
type B
val value: B
}
object Bar {
implicit val barSB = new Bar[String] {
type B = Boolean
val value = true
}
}
// What we want to write ...
//
// def run[T](t: T)(implicit foo: Foo[T], bar: Bar[foo.A]): bar.B = bar.value
//
// or maybe ...
//
// def run[T](t: T)(implicit foo: Foo[T])(implicit bar: Bar[foo.A]): bar.B = bar.value
//
// but can't ... in the first case the compiler complains about a dependent type (foo.A)
// appearing in the same parameter block as its prefix (foo); in the second the compiler
// chokes on the multiple implicit parameter blocks.
// But we can encode the above with the help of singleton types ...
// SingletonOf[T, U] represents an implicit value of type T narrowed to its
// singleton type U.
case class SingletonOf[T, U](value: U)
object SingletonOf {
implicit def mkSingletonOf[T <: AnyRef](implicit t: T): SingletonOf[T, t.type] = SingletonOf(t)
}
// The implicit resolution of SingletonOf[Foo[T], fooT] will result in the type
// fooT being inferred as the singleton type of the in-scope Foo[T] value.
// We then rely on the equivalence between,
//
// foo.A
//
// and,
//
// foo.type#A
//
// to rewrite the problematic dependently chained parameter block to a form
// that scalac is happy to digest ...
def run[T, fooT <: { type A }](t: T)
(implicit sFoo: SingletonOf[Foo[T], fooT], bar: Bar[fooT#A]): bar.B = bar.value
val value = run(23)
assert(value: Boolean)
}
@ikhoon
Copy link

ikhoon commented Jun 18, 2016

👍

@Atry
Copy link

Atry commented Mar 14, 2017

This gist does not work on Dotty because Dotty does not support fooT#A.
https://scastie.scala-lang.org/Atry/MVT3eSKASre9efquImTopA

@Atry
Copy link

Atry commented Mar 14, 2017

I guess we need scala/docs.scala-lang#520

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment