Skip to content

Instantly share code, notes, and snippets.

@lombardo-chcg
Last active November 10, 2021 01:59
Show Gist options
  • Select an option

  • Save lombardo-chcg/21c275eb48647be97ee2a0c7685e34da to your computer and use it in GitHub Desktop.

Select an option

Save lombardo-chcg/21c275eb48647be97ee2a0c7685e34da to your computer and use it in GitHub Desktop.
functor hierarchy
import scala.language.higherKinds
object Example {
// demo version of the Option type for example use
sealed trait Maybe[+A]
case class Just[+A](value: A) extends Maybe[A]
case object Niente extends Maybe[Nothing]
trait Functor[F[A]] {
// fmap :: (a -> b) -> f a -> f b
def fmap[A, B](f: A => B)(that: F[A]): F[B]
// (<$) :: a -> f b -> f a
def `<$`[A, B](a: A)(that: F[B]): F[A]
}
trait Applicative[F[A]] extends Functor[F] {
// pure :: a -> f a
def pure[A](a: A): F[A]
// (<*>) :: f (a -> b) -> f a -> f b
def <*>[A, B](f: F[A => B])(that: F[A]): F[B]
}
trait Monad[F[A]] extends Applicative[F] {
// (>>=) :: m a -> (a -> m b) -> m b
def >>=[A, B](a: F[A])(func: A => F[B]): F[B]
}
// Monad instance for our custom Maybe type
implicit val maybeInstance: Monad[Maybe] = new Monad[Maybe] {
// Functor methods
def fmap[A, B](f: A => B)(that: Maybe[A]): Maybe[B] =
that match {
case Just(value) => Just(f(value))
case Niente => Niente
}
def `<$`[A, B](a: A)(that: Maybe[B]): Maybe[A] =
that match {
case Just(_) => Just(a)
case Niente => Niente
}
// Applicative methods
def <*>[A, B](f: Maybe[A => B])(that: Maybe[A]): Maybe[B] =
(f, that) match {
case (Just(func), Just(value)) => Just(func(value))
case _ => Niente
}
def pure[A](a: A): Maybe[A] = `return`(a)
// Monad methods
def >>=[A, B](a: Maybe[A])(func: A => Maybe[B]): Maybe[B] =
a match {
case Just(value) => func(value)
case Niente => Niente
}
def `return`[A](a: A): Maybe[A] = Just(a)
}
// extension api for functor methods
implicit class FunctorSyntax[F[_]: Functor, A](v: F[A]) {
val instance: Functor[F] = implicitly[Functor[F]]
def fmap[B](f: A => B): F[B] = instance.fmap(f)(v)
// needed for for-comp syntax
def map[B](f: A => B): F[B] = fmap(f)
def `<$>`[B](f: A => B): F[B] = instance.fmap(f)(v)
def `<$`[B](b: B): F[B] = instance.`<$`(b)(v)
}
// extension api for functor methods
implicit class ApplicativeSyntax[F[_]: Applicative, A, B](func: F[A => B]) {
val instance: Applicative[F] = implicitly[Applicative[F]]
def <*>(that: F[A]): F[B] = instance.<*>(func)(that)
}
// extension api for functor methods
implicit class MonadSyntax[F[_]: Monad, A, B](a: F[A]) {
val instance: Monad[F] = implicitly[Monad[F]]
def >>=(func: A => F[B]): F[B] = instance.>>=(a)(func)
// needed for for-comp syntax
def flatMap(func: A => F[B]): F[B] = >>=(func)
}
def main(args: Array[String]): Unit = {
val ex: Maybe[Int] = Just(10)
val res1 = maybeInstance.fmap[Int, Int](_ * 2)(ex)
println(res1)
println(maybeInstance.`<$`("TEST")(Just(10)))
println(ex.fmap(_ * 10))
println(ex.`<$`("TESTING"))
val times: Int => Int => Int = { a =>
{ b =>
a * b
}
}
val moreTimes: Int => Int => Int => Int = { a =>
{ b =>
{ c =>
a * b * c
}
}
}
val appRes: Maybe[Int] = maybeInstance.<*>(ex.fmap(times))(Just(2))
println(appRes)
val xxx = ex.fmap(times) <*> Just(5)
println(xxx)
val two: Maybe[Int] = Just(2)
val abc = (two `<$>` moreTimes) <*> Just(5) <*> Just(6)
println(abc)
val squareMaybeInt: Int => Maybe[Int] = { v =>
Just(v * v)
}
val m = two >>= squareMaybeInt >>= squareMaybeInt >>= squareMaybeInt
println(m)
val comped = for {
v <- two
a <- squareMaybeInt(v)
b <- squareMaybeInt(a)
c <- squareMaybeInt(b)
} yield c
println(comped)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment