Last active
September 25, 2019 00:40
-
-
Save mpilquist/c44b59483a1478d8d9a0637d6777db67 to your computer and use it in GitHub Desktop.
Example encoding of Functor / Applicative / Monad using dotty 0.15
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
/* Example of encoding Functor/Applicative/Monad from cats with Dotty 0.15 features. | |
* Derived in part from Cats -- see https://github.com/typelevel/cats/blob/master/COPYING for full license & copyright. | |
*/ | |
package structures | |
import scala.annotation._ | |
trait Functor[F[_]] { | |
def (fa: F[A]) map[A, B](f: A => B): F[B] | |
def (fa: F[A]) as[A, B](b: B): F[B] = | |
fa.map(_ => b) | |
def (fa: F[A]) void[A]: F[Unit] = | |
fa.as(()) | |
} | |
trait Semigroupal[F[_]] { | |
def (fa: F[A]) product[A, B](fb: F[B]): F[(A, B)] | |
} | |
trait Apply[F[_]] extends Functor[F] with Semigroupal[F] { | |
@alpha("ap") def (ff: F[A => B]) <*>[A, B](fa: F[A]): F[B] | |
def (fa: F[A]) map2[A, B, Z](fb: F[B])(f: (A, B) => Z): F[Z] = | |
fa.product(fb).map(f.tupled) | |
override def (fa: F[A]) product[A, B](fb: F[B]): F[(A, B)] = | |
fa.map(a => (b: B) => (a, b)) <*> fb | |
@alpha("productL") def (fa: F[A]) <*[A, B] (fb: F[B]): F[A] = | |
fa.map2(fb)((a, _) => a) | |
@alpha("productR") def (fa: F[A]) *>[A, B] (fb: F[B]): F[B] = | |
fa.map2(fb)((_, b) => b) | |
} | |
trait Applicative[F[_]] extends Apply[F] { | |
def (a: A) pure[A]: F[A] | |
} | |
trait FlatMap[F[_]] extends Apply[F] { | |
def (fa: F[A]) flatMap[A, B](f: A => F[B]): F[B] | |
def (ffa: F[F[A]]) flatten[A]: F[A] = ffa.flatMap(identity) | |
override def (ff: F[A => B]) <*>[A, B](fa: F[A]): F[B] = | |
ff.flatMap(f => fa.map(f)) | |
} | |
trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { | |
override def (fa: F[A]) map[A, B](f: A => B): F[B] = fa.flatMap(f andThen pure) | |
} | |
implied ListMonad for Monad[List] { | |
def (a: A) pure[A]: List[A] = List(a) | |
def (fa: List[A]) flatMap[A, B](f: A => List[B]): List[B] = | |
fa.flatMap(f) | |
} | |
implied OptionMonad for Monad[Option] { | |
def (a: A) pure[A]: Option[A] = Some(a) | |
def (fa: Option[A]) flatMap[A, B](f: A => Option[B]): Option[B] = | |
fa.flatMap(f) | |
} | |
/* Example usage: | |
scala> import implied structures._ | |
scala> Some(1) <* Some(2) | |
val res0: Option[Int] = Some(1) | |
scala> List(1, 2) *> List(3, 4) | |
val res1: List[Int] = List(3, 4, 3, 4) | |
scala> val x: Option[Int] = 4.pure | |
val x: Option[Int] = Some(4) | |
scala> val y: List[Int] = 4.pure | |
val y: List[Int] = List(4) | |
scala> 4.pure | |
1 |4.pure | |
|^ | |
|Found: Int(4) | |
|Required: ?{ pure: ? } | |
|Note that implicit extension methods cannot be applied because they are ambiguous; | |
|both object ListMonad in package structures and object OptionMonad in package structures provide an extension method `pure` on Int(4) | |
scala> val zeroes = List(1, 2, 3).as(0) | |
val zeroes: List[Int] = List(0, 0, 0) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How would you do
x = pure 4 :: Monad m => m Int
?