Skip to content

Instantly share code, notes, and snippets.

@mpilquist
Last active September 25, 2019 00:40
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mpilquist/c44b59483a1478d8d9a0637d6777db67 to your computer and use it in GitHub Desktop.
Save mpilquist/c44b59483a1478d8d9a0637d6777db67 to your computer and use it in GitHub Desktop.
Example encoding of Functor / Applicative / Monad using dotty 0.15
/* 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)
*/
@friedbrice
Copy link

How would you do x = pure 4 :: Monad m => m Int?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment