Skip to content

Instantly share code, notes, and snippets.

@sshark
Created October 20, 2019 04:35
Show Gist options
  • Select an option

  • Save sshark/cdcb61f48c705f536a4c700756f819ff to your computer and use it in GitHub Desktop.

Select an option

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 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