Created
October 20, 2019 04:35
-
-
Save sshark/cdcb61f48c705f536a4c700756f819ff to your computer and use it in GitHub Desktop.
An example of using Simulacrum and how NOT to include a certain typeclass in the function type declaration
This file contains hidden or 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
| /* | |
| * This example demonstrates how NOT to include Monad[F[_]] in the function `foo` | |
| * type declaration but yet it is implicitly part of the for-comprehension. In | |
| * additional, it shows how Simulacrum reduces the amount of boilerplate required. | |
| */ | |
| package org.teckhooi | |
| import simulacrum._ | |
| import scala.language.implicitConversions | |
| @typeclass trait Sync[F[_]] extends Monad[F] { | |
| def delay[A](a: => A): F[A] | |
| } | |
| @typeclass trait Monad[F[_]] { | |
| @op(">>=") def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] | |
| @op("<*>") def map[A, B](fa: F[A])(f: A => B): F[B] | |
| def pure[A](a: A): F[A] | |
| } | |
| object Effect { | |
| case class IO[A](run: () => A) { | |
| def flatMap[B](f: A => IO[B]): IO[B] = f(run()) | |
| def map[B](f: A => B): IO[B] = IO(() => f(run())) | |
| } | |
| object IO { | |
| implicit object IOMonad extends Monad[IO] { | |
| override def flatMap[A, B](fa: IO[A])(f: A => IO[B]): IO[B] = fa.flatMap(f) | |
| override def map[A, B](fa: IO[A])(f: A => B): IO[B] = fa.map(f) | |
| override def pure[A](a: A): IO[A] = IO(() => a) | |
| } | |
| implicit object IOSync extends Sync[IO] { | |
| override def delay[A](a: => A): IO[A] = IO(() => a) | |
| override def flatMap[A, B](fa: IO[A])(f: A => IO[B]): IO[B] = fa.flatMap(f) | |
| override def map[A, B](fa: IO[A])(f: A => B): IO[B] = fa.map(f) | |
| override def pure[A](a: A): IO[A] = IO(() => a) | |
| } | |
| } | |
| } | |
| object Syntax { | |
| implicit class MonadSyntax[F[_], A](fa: F[A]) { | |
| def map[B](f: A => B)(implicit F: Monad[F]): F[B] = F.map(fa)(f) | |
| def flatMap[B](afb: A => F[B])(implicit F: Monad[F]): F[B] = F.flatMap(fa)(afb) | |
| } | |
| } | |
| object CustomSyncImplExample { | |
| import Effect._ | |
| import Syntax._ | |
| def main(args: Array[String]): Unit = { | |
| /* | |
| * Monad[F] must be included in `foo` i.e. `def foo[F[_]: Sync: Monad]: F[Int]`, if | |
| * Sync did not extend from Monad[F] | |
| */ | |
| def foo[F[_]: Sync]: F[Int] = | |
| for { | |
| s <- Sync[F].delay("abc") | |
| len <- Sync[F].delay(s.length) | |
| } yield len | |
| println(foo[IO].run()) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment