Skip to content

Instantly share code, notes, and snippets.

@caeus
Last active September 29, 2024 10:01
Show Gist options
  • Save caeus/c9f6d549694cf6aac495f5dd4089f8a0 to your computer and use it in GitHub Desktop.
Save caeus/c9f6d549694cf6aac495f5dd4089f8a0 to your computer and use it in GitHub Desktop.
final class Scope[F[+_]] {
def ^[T](value: F[T]): Value[F, T, this.type] = new Value(value)
}
final class Ref[+T, Tag] {
def !(using ctx: Ctx[Tag]): T = ctx(this)
}
final class Value[F[+_], +T, Tag](val value: F[T]) extends AnyVal {
def flatMap[U](fn: Ref[T, Tag] => Expr[F, U, Tag]): Expr[F, U, Tag] = {
val ref = new Ref[T, Tag]
fn(ref).and(ref, value)
}
def map[U](fn: Ref[T, Tag] => Ctx[Tag] ?=> U): Expr[F, U, Tag] = {
val ref = new Ref[T, Tag]
val fun: Ctx[Tag] ?=> U = fn(ref)
Fun[F, U, Tag](fun).and(ref, value)
}
}
final class Ctx[Tag](private val values: Map[Ref[?, Tag], Any]) {
def apply[T](ref: Ref[T, Tag]): T = values(ref).asInstanceOf[T]
}
object Ctx {
def empty[Tag] = new Ctx[Tag](Map())
}
sealed trait Expr[F[_], T, Tag] {
final def and[U](ref: Ref[U, Tag], value: F[U]): Expr[F, T, Tag] =
new Param(ref, value, this)
}
final class Fun[F[_], T, Tag](val fun: Ctx[Tag] ?=> T) extends Expr[F, T, Tag]
final class Param[F[_], P, T, Tag](
val ref: Ref[P, Tag],
val value: F[P],
val in: Expr[F, T, Tag]
) extends Expr[F, T, Tag]
trait Gather[F[+_]] {
def fold[T, Tag](
expr: Expr[F, T, Tag],
values: Map[Ref[?, Tag], Any] = Map[Ref[?, Tag], Any]()
): F[T]
def flatMap[T](fn: (s: Scope[F]) => Expr[F, T, s.type]): F[T] = {
val scope = new Scope[F]
fold(fn(scope))
}
def map[T](fn: (s: Scope[F]) => Ctx[s.type] ?=> T): F[T] = {
val scope = new Scope[F]
fold(Fun(fn(scope)))
}
}
object Gather {
def apply[F[+_]](using Gather[F]): Gather[F] = summon
given option: Gather[Option] = new Gather[Option] {
def fold[T, Tag](
expr: Expr[Option, T, Tag],
values: Map[Ref[?, Tag], Any] = Map[Ref[?, Tag], Any]()
): Option[T] = expr match {
case fun: Fun[Option, T, Tag] => Some(fun.fun(using new Ctx(values)))
case param: Param[Option, ?, T, Tag] =>
param.value.flatMap { value =>
fold(param.in, values + (param.ref -> value))
}
}
}
given list: Gather[List] = new Gather[List] {
def fold[T, Tag](
expr: Expr[List, T, Tag],
values: Map[Ref[?, Tag], Any] = Map[Ref[?, Tag], Any]()
): List[T] = expr match {
case fun: Fun[List, T, Tag] => List(fun.fun(using new Ctx(values)))
case param: Param[List, ?, T, Tag] =>
param.value.flatMap { value =>
fold(param.in, values + (param.ref -> value))
}
}
}
}
final case class User(name: String, age: Int)
def typeoption = {
for {
s <- Gather[Option]
name <- s ^ Some("Alejandro")
age <- s ^ Option.empty[Nothing]
} yield User(name.!, age.!)
}
def typelist = {
for {
s <- Gather[List]
name <- s ^ List("Alejandro", "Camilo")
age <- s ^ List(25, 29, 34)
} yield User(name.!, age.!)
}
println(typeoption)
println(typelist)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment