Skip to content

Instantly share code, notes, and snippets.

@djspiewak
Last active December 20, 2015 02:50
Show Gist options
  • Save djspiewak/da1189c49887b04d1dc7 to your computer and use it in GitHub Desktop.
Save djspiewak/da1189c49887b04d1dc7 to your computer and use it in GitHub Desktop.
import scalaz.{Applicative, Bind, Functor, Monad, Traverse}
// > import scalaz.{Applicative, Bind, Functor, Monad, Traverse}
import scalaz.std.option._
// > import scalaz.std.option._
import scalaz.std.list._
// > import scalaz.std.list._
import scalaz.syntax.applicative._
// > import scalaz.syntax.applicative._
sealed trait CoPConst {
type Point[A]
}
// > warning: there was one feature warning; re-run with -feature for details
// > defined trait CoPConst
final class |:[F[_], T <: CoPConst] extends CoPConst {
type Point[A] = F[T#Point[A]]
}
// > warning: there was one feature warning; re-run with -feature for details
// > defined class $bar$colon
final class CCNil extends CoPConst {
type Point[A] = A
}
// > defined class CCNil
object CoPConst {
sealed trait CApplicative[C <: CoPConst] {
def point[A](a: A): C#Point[A]
def map[A, B](fa: C#Point[A])(f: A => B): C#Point[B]
def ap[A, B](fa: C#Point[A])(f: C#Point[A => B]): C#Point[B]
}
object CApplicative {
implicit def head[F[_]](implicit F: Applicative[F]): CApplicative[F |: CCNil] = new CApplicative[F |: CCNil] {
def point[A](a: A) = F.point(a)
def map[A, B](fa: F[A])(f: A => B): F[B] = F.map(fa)(f)
def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = F.ap(fa)(f)
}
implicit def corecurse[F[_], C <: CoPConst](implicit P: CApplicative[C], F: Applicative[F]): CApplicative[F |: C] = new CApplicative[F |: C] {
def point[A](a: A) = F.point(P.point(a))
def map[A, B](fa: F[C#Point[A]])(f: A => B): F[C#Point[B]] =
F.map(fa) { ca => P.map(ca)(f) }
def ap[A, B](fa: F[C#Point[A]])(f: F[C#Point[A => B]]): F[C#Point[B]] = {
val f2 = F.map(f) { cf =>
{ ca: C#Point[A] => P.ap(ca)(cf) }
}
F.ap(fa)(f2)
}
}
}
sealed trait CTraverse[C <: CoPConst] {
def traverse[G[_]: Applicative, A, B](ca: C#Point[A])(f: A => G[B]): G[C#Point[B]]
}
object CTraverse {
implicit def head[F[_]](implicit F: Traverse[F]): CTraverse[F |: CCNil] = new CTraverse[F |: CCNil] {
def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] = F.traverse(fa)(f)
}
implicit def corecurse[F[_], C <: CoPConst](implicit C: CTraverse[C], F: Traverse[F]): CTraverse[F |: C] = new CTraverse[F |: C] {
def traverse[G[_]: Applicative, A, B](fca: F[C#Point[A]])(f: A => G[B]): G[F[C#Point[B]]] = {
F.traverse(fca) { ca =>
C.traverse(ca)(f)
}
}
}
}
sealed trait CJoin[C <: CoPConst] {
def join[A](cca: C#Point[C#Point[A]]): C#Point[A]
}
object CJoin {
implicit def head[F[_]](implicit F: Bind[F]): CJoin[F |: CCNil] = new CJoin[F |: CCNil] {
def join[A](ffa: F[F[A]]) = F.join(ffa)
}
implicit def corecurse[F[_], C <: CoPConst](implicit C: CJoin[C], T: CTraverse[C], F: Applicative[F], B: Bind[F]): CJoin[F |: C] = new CJoin[F |: C] {
def join[A](fcfa: F[C#Point[F[C#Point[A]]]]): F[C#Point[A]] = {
val ffca = F.map(fcfa) { cfa =>
F.map(T.traverse(cfa) { fa => fa }) { cca =>
C.join(cca)
}
}
B.join(ffca)
}
}
}
sealed trait Lifter[F[_], C <: CoPConst] {
def apply[A](fa: F[A]): C#Point[A]
}
object Lifter {
implicit def exacthead[F[_]]: Lifter[F, F |: CCNil] = new Lifter[F, F |: CCNil] {
def apply[A](fa: F[A]): F[A] = fa
}
implicit def head[F[_], C <: CoPConst](implicit P: CApplicative[C], F: Functor[F]): Lifter[F, F |: C] = new Lifter[F, F |: C] {
def apply[A](fa: F[A]): F[C#Point[A]] = F.map(fa) { a => P.point(a) }
}
implicit def corecurse[F[_], G[_], C <: CoPConst](implicit L: Lifter[F, C], G: Applicative[G]): Lifter[F, G |: C] = new Lifter[F, G |: C] {
def apply[A](fa: F[A]): G[C#Point[A]] = G.point(L(fa))
}
}
sealed trait Contains[F[_], C <: CoPConst]
object Contains {
implicit def head[F[_], C <: CoPConst]: Contains[F, F |: C] =
new Contains[F, F |: C] {}
implicit def corecurse[F[_], G[_], C <: CoPConst](implicit C: Contains[F, C]): Contains[F, G |: C] =
new Contains[F, G |: C] {}
}
}
// > warning: there were 18 feature warnings; re-run with -feature for details
// > defined object CoPConst
// > warning: previously defined trait CoPConst is not a companion to object CoPConst.
// > Companions must be defined together; you may wish to use :paste mode for this.
final case class Emm[C <: CoPConst, A](run: C#Point[A]) {
def map[B](f: A => B)(implicit C: CoPConst.CApplicative[C]): Emm[C, B] = Emm(C.map(run)(f))
def flatMap[B](f: A => Emm[C, B])(implicit A: CoPConst.CApplicative[C], B: CoPConst.CJoin[C]): Emm[C, B] =
Emm(B.join(A.map(run) { a => f(a).run }))
def flatMapM[G[_], B](f: A => G[B])(implicit A: CoPConst.CApplicative[C], B: CoPConst.CJoin[C], L: CoPConst.Lifter[G, C]): Emm[C, B] =
flatMap { a => Emm(L(f(a))) }
}
// > warning: there was one feature warning; re-run with -feature for details
// > defined class Emm
trait EmmLowPriorityImplicits1 {
import CoPConst._
implicit def applicativeInstance[C <: CoPConst](implicit C: CApplicative[C]): Applicative[({ type λ[α] = Emm[C, α] })#λ] = new Applicative[({ type λ[α] = Emm[C, α] })#λ] {
def point[A](a: => A): Emm[C, A] = new Emm(C.point(a))
def ap[A, B](fa: => Emm[C, A])(f: => Emm[C, A => B]): Emm[C, B] = new Emm(C.ap(fa.run)(f.run))
}
}
// > defined trait EmmLowPriorityImplicits1
trait EmmLowPriorityImplicits2 extends EmmLowPriorityImplicits1 {
import CoPConst._
implicit def monadInstance[C <: CoPConst : CApplicative : CJoin]: Monad[({ type λ[α] = Emm[C, α] })#λ] = new Monad[({ type λ[α] = Emm[C, α] })#λ] {
def point[A](a: => A): Emm[C, A] = new Emm(implicitly[CApplicative[C]].point(a))
def bind[A, B](fa: Emm[C, A])(f: A => Emm[C, B]): Emm[C, B] = fa flatMap f
}
}
// > defined trait EmmLowPriorityImplicits2
object Emm extends EmmLowPriorityImplicits2 {
import CoPConst._
implicit def traverseInstance[C <: CoPConst](implicit C: CTraverse[C]): Traverse[({ type λ[α] = Emm[C, α] })#λ] = new Traverse[({ type λ[α] = Emm[C, α] })#λ] {
def traverseImpl[G[_]: Applicative, A, B](fa: Emm[C, A])(f: A => G[B]): G[Emm[C, B]] =
Applicative[G].map(C.traverse(fa.run)(f)) { new Emm(_) }
}
implicit class LiftSyntax[F[_], A](val fa: F[A]) extends AnyVal {
def liftM[C <: CoPConst](implicit L: Lifter[F, C]): Emm[C, A] = new Emm(L(fa))
}
}
// > warning: there were two feature warnings; re-run with -feature for details
// > defined object Emm
// > warning: previously defined class Emm is not a companion to object Emm.
// > Companions must be defined together; you may wish to use :paste mode for this.
////////////////////////////////////////////////////////////////////
object CCTest {
import CoPConst._
def foo[C <: CoPConst](implicit C: Contains[List, C]): Unit = ()
foo[List |: CCNil]
foo[Option |: List |: CCNil]
}
// > defined object CCTest
import Emm._
// > import Emm._
val fa: Option[Int] = Some(42)
// > fa: Option[Int] = Some(42)
fa.liftM[Option |: CCNil]
// > res0: Emm[|:[Option,CCNil],Int] = Emm(Some(42))
fa.liftM[Option |: List |: CCNil]
// > res1: Emm[|:[Option,|:[List,CCNil]],Int] = Emm(Some(List(42)))
type C = List |: Option |: CCNil
// > defined type alias C
val e = fa.liftM[C]
// > e: Emm[C,Int] = Emm(List(Some(42)))
e map (2 *)
// > warning: there was one feature warning; re-run with -feature for details
// > res2: Emm[C,Int] = Emm(List(Some(84)))
for {
x <- Option(42).liftM[C] // can't use Some without ascribing Option
y <- List(12, 22).liftM[C]
} yield x + y
// > res3: Emm[C,Int] = Emm(List(Some(54), Some(64)))
import scalaz.effect.IO
// > import scalaz.effect.IO
// note how the IO action is the outermost effect
type CIO = IO |: List |: CCNil
// > defined type alias CIO
val e = for {
v <- List(1, 2, 3, 4).liftM[CIO]
_ <- (IO { println(v) }).liftM[CIO]
} yield ()
// > e: Emm[CIO,Unit] = Emm(scalaz.effect.IOFunctions$$anon$6@4e9c23b7)
e.run.unsafePerformIO()
// > 1
// > 2
// > 3
// > 4
// > res4: |:[List,CCNil]#Point[Unit] = List((), (), (), ())
/*
The following DOESN'T compile! Nor should it. What we're literally asking
the runtime to do below is "flip" a list which exists inside of IO to the out
side. Put differently, we're asking the interpreter to defeat the safety
guarantees of IO, and as such, we shouldn't expect this to compile.
*****************************
type CIO = List |: IO |: CCNil
val e = for {
v <- List(1, 2, 3, 4).liftM[CIO]
_ <- (IO { println(v) }).liftM[CIO]
} yield ()
e.run map { _.unsafePerformIO() }
*/
def sum[M[_]: Applicative](m1: M[Int], m2: M[Int]): M[Int] = (m1 |@| m2) { _ + _ }
// > warning: there was one feature warning; re-run with -feature for details
// > sum: [M[_]](m1: M[Int], m2: M[Int])(implicit evidence$1: scalaz.Applicative[M])M[Int]
// this fails because of SI-2712, but that's ok (can be easily fixed with Unapply)
sum(Option(24).liftM[List |: Option |: CCNil], List(42).liftM[List |: Option |: CCNil])
// > <console>:33: error: no type parameters for method sum: (m1: M[Int], m2: M[Int])(implicit evidence$1: scalaz.Applicative[M])M[Int] exist so that it can be applied to arguments (Emm[|:[List,|:[Option,CCNil]],Int], Emm[|:[List,|:[Option,CCNil]],Int])
// > --- because ---
// > argument expression's type is not compatible with formal parameter type;
// > found : Emm[|:[List,|:[Option,CCNil]],Int]
// > required: ?M[Int]
// > sum(Option(24).liftM[List |: Option |: CCNil], List(42).liftM[List |: Option |: CCNil])
// > ^
// > <console>:33: error: type mismatch;
// > found : Emm[|:[List,|:[Option,CCNil]],Int]
// > required: M[Int]
// > sum(Option(24).liftM[List |: Option |: CCNil], List(42).liftM[List |: Option |: CCNil])
// > ^
// > <console>:33: error: type mismatch;
// > found : Emm[|:[List,|:[Option,CCNil]],Int]
// > required: M[Int]
// > sum(Option(24).liftM[List |: Option |: CCNil], List(42).liftM[List |: Option |: CCNil])
// > ^
// > <console>:33: error: could not find implicit value for evidence parameter of type scalaz.Applicative[M]
// > sum(Option(24).liftM[List |: Option |: CCNil], List(42).liftM[List |: Option |: CCNil])
// > ^
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment