Skip to content

Instantly share code, notes, and snippets.

@Krever
Created February 29, 2020 20:09
Show Gist options
  • Save Krever/2b14e36c362d8f21e2c9caeb7ceb810c to your computer and use it in GitHub Desktop.
Save Krever/2b14e36c362d8f21e2c9caeb7ceb810c to your computer and use it in GitHub Desktop.
Higher kinded data for CRUD
sealed trait RelationUpdate[+New, +Id]
object RelationUpdate {
type Aux[E <: EntityBase] = RelationUpdate[E#New, E#Id]
case class CreateAndAdd[New](items: List[New]) extends RelationUpdate[New, Nothing]
case class Delete[Id](items: List[Id]) extends RelationUpdate[Nothing, Id]
case class Add[Id](items: List[Id]) extends RelationUpdate[Nothing, Id]
}
object H {
case class UIO[T](x: () => T) // ZIO
type Ident[A] = A
type Const[A] = { type Out[T] = A }
type Update[T <: EntityBase] = List[RelationUpdate.Aux[T]]
type New[T <: EntityBase] = List[T#New]
type Relation[T <: EntityBase] = UIO[List[T#Existing]]
}
trait EntityBase {
type Id
type Raw[Idx, FieldT[_], CollT[_ <: Entity]]
type New
type Existing
type Update
}
trait Entity extends EntityBase {
type New = Raw[Unit, H.Ident, H.New]
type Existing = Raw[Id, H.Ident, H.Relation]
type Update = Raw[Id, Option, H.Update]
}
object Book extends Entity {
type Id = String // newtype in real world
case class Raw[Idx, FieldT[_], CollT[_ <: Entity]](
id: Idx,
title: FieldT[String],
authors: CollT[Author.type]
)
def _new(title: String, authors: List[Author.New]): Book.New = Book.Raw[Unit, H.Ident, H.New](
(),
title,
authors
)
def _existing(id: Id, title: String, authors: H.UIO[List[Author.Existing]]): Book.Existing =
Book.Raw[Id, H.Ident, H.Relation](id, title, authors)
def _update(id: Id, title: Option[String], authors: List[RelationUpdate.Aux[Author.type]]): Book.Update =
Book.Raw[Id, Option, H.Update](id, title, authors)
}
object Author extends Entity {
type Id = String // NewType in real world
case class Raw[Idx, FieldT[_], CollT[_ <: Entity]](
id: Idx,
name: FieldT[String]
)
def _new(name: String): Author.New = Author.Raw[Unit, H.Ident, H.New]((), name)
def _existing(id: Id, name: String): Author.Existing = Author.Raw[Id, H.Ident, H.Relation](id, name)
}
object Example {
val newBook: Book.New = Book._new(
"Title 1",
List(
Author._new(
"Author 1"
)
)
)
val existingBook: Book.Existing = Book._existing(
"b1",
"Title 1",
H.UIO(
() =>
List(
Author._existing(
"a1",
"Name 1"
)
)
)
)
val updateBook: Book.Update = Book._update(
"b1",
Some("Title 2"),
List[RelationUpdate.Aux[Author.type]](
RelationUpdate.Add(List("a3")),
RelationUpdate.CreateAndAdd(List(Author._new("Author 4"))),
RelationUpdate.Delete(List("a1"))
)
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment