Skip to content

Instantly share code, notes, and snippets.

@b-studios
Created September 20, 2016 10:18
Show Gist options
  • Save b-studios/a09e9966778bcbd09a777f078ae822f3 to your computer and use it in GitHub Desktop.
Save b-studios/a09e9966778bcbd09a777f078ae822f3 to your computer and use it in GitHub Desktop.
Trying to help the compiler inferring type constructors
// Default functor and monad TCs
trait Functor[F[_]] {
def map[A, B](fa: F[A], f: A => B): F[B]
}
trait Monad[M[_]] {
def unit[A](value: A): M[A]
def flatMap[A, B](ma: M[A], f: A => M[B]): M[B]
def map[A, B](ma: M[A], f: A => B): M[B] = flatMap(ma, f andThen unit)
}
def unit[A, M[_]: Monad](value: A): M[A] = implicitly[Monad[M]].unit(value)
// For type constructors with multiple arguments, such as
trait Foo[A, B]
// we can give monad instances, but they will not automatically be found by implicit resolution:
// (using kind projector syntax here)
implicit def monadFoo[A]: Monad[Foo[A, ?]] = ???
// idea: have a typeclass that witnesses that a type is an application of a typeconstructor F to
// an argument A
trait App[X] {
type F[X]
type A
// these should not be too hard to provide :)
implicit val ev1: X =:= F[A]
implicit val ev2: F[A] =:= X
}
object App {
type Aux[X, FF[_], AA] = App[X] { type F[X] = FF[X]; type A = AA }
}
// now for providing monad ops as syntax, we just require that X is a type constructor
// application and app.F is a monad.
implicit class MonadOps[X](ma: X)(implicit val app: App[X]) {
import app._
def flatMap[B](f: A => F[B])(implicit m: Monad[F]): F[B] = m.flatMap(ma, f)
def map[B](f: A => B)(implicit m: Monad[F]): F[B] = m.map(ma, f)
}
implicit def appFoo[A, B]: App.Aux[Foo[A, B], Foo[A, ?], B] = ???
// However, as it turns out, this is not enough...
val foo: Foo[Int, String] = ???
// ... since we loose type information in the inferred argument `app`:
// val app = implicitly[App[Foo[Int, String]]]
// which is the same as:
// val app: App[Foo[Int, String]] = appFoo[Int, String]
// but not the same as:
val app = appFoo[Int, String]
// for which we can prove (but cannot for the above two variants):
implicitly[app.A =:= String]
implicitly[app.F[String] =:= Foo[Int, String]]
val m = implicitly[Monad[app.F]]
// but even then this is not enough:
//
// [error] test.scala:60: type mismatch;
// [error] found : Monad[app.F]
// [error] required: Monad[_5.app.F] where val _5: MonadOps[Foo[Int,String]]
new MonadOps(f)(app).map[Int](x => ???)(m)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment