Last active
October 11, 2018 22:00
-
-
Save dreverri/a92ec395a93770d9e68da2968359ea34 to your computer and use it in GitHub Desktop.
Experimenting with a different Typeclass encoding in Scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object fp { | |
case class derived[a](a: a) { | |
def map[b](f: a => b): derived[b] = derived(f(a)) | |
} | |
trait function { } | |
object function { | |
trait mappend[a] extends function { def apply(a1: a, a2: => a): a } | |
trait mempty[a] extends function { def apply: a } | |
trait imap[f[_]] extends function { def apply[a, b](fa: f[a])(f: a => b)(g: b => a): f[b] } | |
trait cmap[f[_]] extends function { def apply[a, b](fa: f[a])(f: b => a): f[b] } | |
trait map[f[_]] extends function { def apply[a, b](fa: f[a])(f: a => b): f[b] } | |
trait ap[f[_]] extends function { def apply[a, b](fa: f[a])(ff: f[a => b]): f[b] } | |
trait pure[f[_]] extends function { def apply[a](a: a): f[a] } | |
trait product[f[_]] extends function { def apply[a, b](fa: f[a])(fb: f[b]): f[(a, b)] } | |
} | |
case class typeclass[a](a: a) | |
object typeclass { | |
import function._ | |
type semigroup[a] = typeclass[mappend[a]] | |
type monoid[a] = typeclass[(semigroup[a], mempty[a])] | |
type invariant[f[_]] = typeclass[imap[f]] | |
type contravariant[f[_]] = typeclass[(invariant[f], cmap[f])] | |
type functor[f[_]] = typeclass[(invariant[f], map[f])] | |
type apply[f[_]] = typeclass[(functor[f], ap[f])] | |
type applicative[f[_]] = typeclass[(apply[f], pure[f])] | |
} | |
object ops { | |
import typeclass._ | |
implicit class functorOps[f[_]](val functor: functor[f]) extends AnyVal { | |
def map = functor.a._2 | |
} | |
implicit class applyOps[f[_]](val apply: apply[f]) extends AnyVal { | |
def functor = apply.a._1 | |
def ap = apply.a._2 | |
} | |
implicit class applicativeOps[f[_]](val applicative: applicative[f]) extends AnyVal { | |
def apply = applicative.a._1 | |
def ap = apply.ap | |
def pure = applicative.a._2 | |
} | |
} | |
object syntax { | |
import typeclass._ | |
import ops._ | |
implicit class functorSyntax[f[_], a](val fa: f[a]) extends AnyVal { | |
def map[b](f: a => b)(implicit functor: functor[f]) = functor.map(fa)(f) | |
} | |
implicit class applySyntax[f[_], a](val fa: f[a]) extends AnyVal { | |
def ap[b](ff: f[a => b])(implicit apply: apply[f]) = apply.ap(fa)(ff) | |
} | |
} | |
trait derivations { | |
implicit def deriveFunction[f <: function](implicit ev: f): derived[f] = derived(ev) | |
implicit def deriveTypeclass[a](implicit ev: derived[a]): derived[typeclass[a]] = ev.map(typeclass(_)) | |
implicit def underiveTypeclass[a](implicit ev: derived[typeclass[a]]): typeclass[a] = ev.a | |
implicit def derivePair[a, b](implicit ev1: derived[a], ev2: derived[b]): derived[(a, b)] = derived((ev1.a, ev2.a)) | |
import function._ | |
// imap can be derived from map or cmap | |
implicit def `map => imap`[f[_]](implicit map: map[f]): imap[f] = new imap[f] { | |
def apply[a, b](fa: f[a])(f: a => b)(g: b => a): f[b] = map(fa)(f) | |
} | |
implicit def `cmap => imap`[f[_]](implicit cmap: cmap[f]): imap[f] = new imap[f] { | |
def apply[a, b](fa: f[a])(f: a => b)(g: b => a): f[b] = cmap(fa)(g) | |
} | |
// map can be derived from ap and pure | |
implicit def `(ap, pure) => map`[f[_]](implicit ap: ap[f], pure: pure[f]): map[f] = new map[f] { | |
def apply[a, b](fa: f[a])(f: a => b): f[b] = ap(fa)(pure(f)) | |
} | |
// ap can be derived from product and map | |
implicit def `(product, map) => ap`[f[_]](implicit product: product[f], map: map[f]): ap[f] = new ap[f] { | |
def apply[a, b](fa: f[a])(ff: f[a => b]): f[b] = map(product(ff)(fa))({ case (f, a) => f(a) }) | |
} | |
} | |
trait h0 extends derivations { | |
import typeclass._ | |
import ops._ | |
implicit def `apply => functor`[f[_]](implicit apply: apply[f]): functor[f] = apply.functor | |
implicit def `applicative => apply`[f[_]](implicit applicative: applicative[f]): apply[f] = applicative.apply | |
} | |
object h extends h0 | |
} | |
object main { | |
object list { | |
import fp.function._ | |
// specifying map, ap, and pure will fail compilation since map can be derived from ap and pure. | |
// implicit def map: map[List] = new map[List] { | |
// def apply[a, b](fa: List[a])(f: a => b): List[b] = fa.map(f) | |
// } | |
implicit def ap: ap[List] = new ap[List] { | |
def apply[a, b](fa: List[a])(ff: List[a => b]): List[b] = ff.flatMap { f => fa.map { a => f(a) } } | |
} | |
implicit def pure: pure[List] = new pure[List] { | |
def apply[a](a: a): List[a] = List(a) | |
} | |
} | |
object program { | |
import fp.typeclass._ | |
import fp.syntax._ | |
def addOne[f[_]: functor](fa: f[Int]): f[Int] = { | |
fa.map(i => i + 1) | |
} | |
} | |
def main(args: Array[String]): Unit = { | |
import list._ | |
import fp.h._ | |
println(program.addOne[List](List(1,2,3))) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment