Skip to content

Instantly share code, notes, and snippets.

@p-pavel
Created August 15, 2023 23:19
Show Gist options
  • Save p-pavel/7801c83f5c03b66ec7266f1673b7bb0d to your computer and use it in GitHub Desktop.
Save p-pavel/7801c83f5c03b66ec7266f1673b7bb0d to your computer and use it in GitHub Desktop.
import scala.annotation.targetName
import cats.*
import cats.implicits.*
import cats.data.{StateT, State}
trait CRUD:
type Obj
type Identity
type Criteria
val All: Criteria
type F[_]
extension (c: Criteria) def select: F[Seq[Obj]]
extension (id: Identity)
def delete: F[Unit]
def lookup: F[Option[Obj]]
extension (o: Obj)
def id: Identity
def store: F[Unit]
@targetName("deleteObject")
def delete: F[Unit] = o.id.delete
object CRUD:
type Aux[G[_], O] = CRUD {type F[A] = G[A]; type Obj = O}
trait StateM[S, F[_]]:
def get: F[S]
def put(s: S): F[Unit]
def modify(f: S => S)(using Monad[F]) = get.map(f).flatMap(put)
trait Lens[T, A]:
def get(t: T): A
def update(a: A): T => T
given tailLens[A, T <: Tuple, B](using l: Lens[T, B]): Lens[A *: T, B] with
type TT = A *: T
def get(t: TT): B = l.get(t.tail)
def update(a: B) = { case h *: tail =>
h *: l.update(a)(tail)
}
given headLens[A, T <: Tuple]: Lens[A *: T, A] with
def get(t: A *: T): A = t.head
def update(a: A) = l => a *: l.tail
given lensStateM[T, A, F[_]: Monad](using
st: StateM[T, F],
l: Lens[T, A]
): StateM[A, F] with
def get: F[A] = st.get.map(l.get)
def put(a: A): F[Unit] = st.get.map(l.update(a)).flatMap(st.put)
given stateMFromStateT[F[_]: Applicative, S]: StateM[S, StateT[F, S, *]] with
def get = StateT.get
def put(s: S) = StateT.set(s)
trait Identified[A]:
type Identity
extension (a: A) def id: Identity
given idenifiedCrud[A](using
c: CRUD { type Obj = A }
): (Identified[A] { type Identity = c.Identity }) =
new Identified[A]:
type Identity = c.Identity
extension (a: A) def id: Identity = c.id(a)
type SimpleCRUD[O, Id] = CRUD {
type Obj = O; type Identity = Id; type Criteria = O => Boolean
}
given crudOnState[G[_]:Monad, S](using
ident: Identified[S],
st: StateM[Map[ident.Identity, S], G]
): (SimpleCRUD[S, ident.Identity] { type F[A] = G[A] }) =
new CRUD:
type Identity = ident.Identity
type Obj = S
type Criteria = S => Boolean
type F[A] = G[A]
val All = _ => true
extension (c: Criteria) def select: F[Seq[Obj]] = ???
extension (id: Identity)
def delete: F[Unit] = st.modify(_ - id)
def lookup: F[Option[Obj]] = st.get.map(_.get(id))
extension (o: Obj)
def id: Identity = ident.id(o)
def store: F[Unit] = st.modify(_ + (o.id -> o))
end new
case class Person(id: Long, name: String, age: Int)
given Identified[Person] with
type Identity = Long
extension (p: Person) def id = p.id
def createPerson[F[_]] (using CRUD.Aux[F, Person]) =
Person(123, "Sdaf", 12).store
@main
def prt = println(createPerson[State[(Map[Long, Person], Int),*]].run((Map.empty,0)).value)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment