Skip to content

Instantly share code, notes, and snippets.

@dreverri
Last active October 11, 2018 22:00
Show Gist options
  • Save dreverri/a92ec395a93770d9e68da2968359ea34 to your computer and use it in GitHub Desktop.
Save dreverri/a92ec395a93770d9e68da2968359ea34 to your computer and use it in GitHub Desktop.
Experimenting with a different Typeclass encoding in Scala
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