Last active
September 29, 2024 10:01
-
-
Save caeus/c9f6d549694cf6aac495f5dd4089f8a0 to your computer and use it in GitHub Desktop.
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
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