Skip to content

Instantly share code, notes, and snippets.

@lachezar
Created April 23, 2025 10:41
Show Gist options
  • Save lachezar/064fc720b28f235550dcf24b4af97c8b to your computer and use it in GitHub Desktop.
Save lachezar/064fc720b28f235550dcf24b4af97c8b to your computer and use it in GitHub Desktop.
Monads with for-comprehensions
enum MyOption[+A]:
case MyNone
case MySome(a: A)
// in case we don't want to have extension for some reason
// type M = [X] =>> MyOption[X]
// def flatMap[B](f: A => M[B]): M[B] = summon[MyMonad[M]].flatMap(this)(f)
// def map[B](f: A => B)(using myMonad: MyMonad[M]): M[B] =
// myMonad.flatMap(this)(a => myMonad.pure(f(a)))
import MyOption.*
trait MyMonad[M[A]] {
def flatMap[A, B](m: M[A])(f: A => M[B]): M[B]
def pure[A](a: A): M[A]
// define map in terms of flatMap and pure
// def map[A, B](m: M[A])(f: A => B): M[B] = flatMap(m)(a => pure(f(a)))
}
given moMonad: MyMonad[MyOption] = new MyMonad[MyOption] {
override def flatMap[A, B](m: MyOption[A])(f: A => MyOption[B]): MyOption[B] =
m match
case MyNone => MyNone
case MySome(a) => f(a)
override def pure[A](a: A): MyOption[A] = MySome(a)
}
extension [A, M[+A]: MyMonad](m: M[A])
def flatMap[B](f: A => M[B]): M[B] = summon[MyMonad[M]].flatMap(m)(f)
def map[B](f: A => B)(using myMonad: MyMonad[M]): M[B] =
myMonad.flatMap(m)(a => myMonad.pure(f(a)))
def getName: MyOption[String] = MySome("Tom")
def getPass: MyOption[String] = MySome("secret")
def getAge: MyOption[Int] = MySome(42)
val res: MyOption[(String, String, Int)] = for {
name <- getName
pass <- getPass
age <- getAge
} yield (name, pass, age)
@main
def main(args: String*): Unit = {
println(res)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment