Skip to content

Instantly share code, notes, and snippets.

@lachezar
Last active February 12, 2024 15:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lachezar/f1aebd7f8913256b8ee01965e8a35c29 to your computer and use it in GitHub Desktop.
Save lachezar/f1aebd7f8913256b8ee01965e8a35c29 to your computer and use it in GitHub Desktop.
Scala 3 Monad type class for Option and Either
trait Monad[M[_]] {
def pure[A](a: A): M[A]
def flatMap[A, B](m: M[A])(f: A => M[B]): M[B]
}
enum MyOption[+T]:
case None
case Some(value: T)
given Monad[MyOption] with
override def pure[A](a: A): MyOption[A] = MyOption.Some(a)
override def flatMap[A, B](m: MyOption[A])(f: A => MyOption[B]): MyOption[B] =
m match {
case MyOption.None => MyOption.None
case MyOption.Some(x) => f(x)
}
val x: MyOption[Int] =
summon[Monad[MyOption]].flatMap(MyOption.Some(42))(x => MyOption.Some(x + 1))
val y: MyOption[Int] = MyOption.None
println(x)
println(y)
enum MyEither[+L, +R]:
case Left(value: L)
case Right(value: R)
class EitherMonad[T] extends Monad[[E] =>> MyEither[T, E]] {
override def pure[A](a: A): MyEither[T, A] = MyEither.Right(a)
override def flatMap[A, B](
m: MyEither[T, A]
)(f: A => MyEither[T, B]): MyEither[T, B] = m match {
case MyEither.Left(e) => MyEither.Left(e)
case MyEither.Right(x) => f(x)
}
}
given [T]: EitherMonad[T] = new EitherMonad[T]
extension [L, A](either: MyEither[L, A])
def flatMap[B]: (f: A => MyEither[L, B]) => MyEither[L, B] =
summon[EitherMonad[L]].flatMap(either)
val z: MyEither[String, Int] =
summon[EitherMonad[String]].pure(42).flatMap(x => MyEither.Right(x + 1))
println(z)
// Scala 3 type lambda inspiration from https://blog.rockthejvm.com/scala-3-type-lambdas/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment