Skip to content

Instantly share code, notes, and snippets.

@ioleo
Last active February 12, 2018 20:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ioleo/59d453e11ed06a3ad07a208003118cc4 to your computer and use it in GitHub Desktop.
Save ioleo/59d453e11ed06a3ad07a208003118cc4 to your computer and use it in GitHub Desktop.
Example of generic tagless algebra with Cats and Freestyle. Additional type args beyond F[_] in algebras are not currently supported. To work around we use path dependent types.
import cats.data.State
import freestyle.tagless._
/* domain objects */
sealed trait Acme
object Acme {
case object AcmeUK extends Acme
case object AcmeUS extends Acme
}
case class CommonId(id: String)
trait Item[A <: Acme] {
val id: CommonId
}
case class AcmeUKItem(id: CommonId) extends Item[Acme.AcmeUK.type]
case class AcmeUSItem(id: CommonId) extends Item[Acme.AcmeUS.type]
/* state */
trait MapState[K, V] {
type Type = Map[K, V]
type TypeA[A] = State[Type, A]
def apply(state: Map[K, V]): Type = state
def apply(state: (K, V)*): Type = state.toMap
def empty: Type = Map.empty[K, V]
}
trait CommonState[A <: Acme] extends MapState[CommonId, Item[A]]
object AcmeUKState extends CommonState[Acme.AcmeUK.type]
object AcmeUSState extends CommonState[Acme.AcmeUS.type]
/* freestyle tagless */
trait AcmeStorage[A <: Acme] {
@tagless trait Algebra[M[_]] {
def put(item: Item[A]): M[Unit]
def get(id: CommonId): M[Option[Item[A]]]
}
}
object GenericTaglessAcme extends App {
object AcmeUKStorage extends AcmeStorage[Acme.AcmeUK.type]
implicit val acmeUKStorageHandler = new AcmeUKStorage.Algebra[AcmeUKState.TypeA] {
def put(item: Item[Acme.AcmeUK.type]): AcmeUKState.TypeA[Unit] = State.modify(_.updated(item.id, item))
def get(id: CommonId): AcmeUKState.TypeA[Option[Item[Acme.AcmeUK.type]]] = State.inspect(_.get(id))
}
val ukOps = AcmeUKStorage.Algebra[AcmeUKState.TypeA]
val inputState = AcmeUKState.empty
val (id1, id2, id3) = (CommonId("foo"), CommonId("bar"), CommonId("baz"))
val (ukItem1, ukItem2, ukItem3) = (AcmeUKItem(id1), AcmeUKItem(id2), AcmeUKItem(id3))
val program = for {
_ <- ukOps.put(ukItem1)
_ <- ukOps.put(ukItem2)
_ <- ukOps.put(ukItem3)
mid <- ukOps.get(id2)
} yield mid
val (outputState, result) = program.run(inputState).value
val assertion1 = outputState == AcmeUKState(id1 -> ukItem1, id2 -> ukItem2, id3 -> ukItem3)
val assertion2 = result == Some(ukItem2)
println("%s << outputState == AcmeUKState(id1 -> ukItem1, id2 -> ukItem2, id3 -> ukItem3)".format(assertion1))
// true << outputState == AcmeUKState(id1 -> ukItem1, id2 -> ukItem2, id3 -> ukItem3)
// true << result == Some(ukItem2)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment