Skip to content

Instantly share code, notes, and snippets.

@wemrysi
Last active December 14, 2015 17:48
Show Gist options
  • Save wemrysi/6ba61fefe8e83e04161b to your computer and use it in GitHub Desktop.
Save wemrysi/6ba61fefe8e83e04161b to your computer and use it in GitHub Desktop.
Auditing Ideas
type Timestamp = Long
type Nanos = Long
sealed trait Clock[A]
final case class CurrentMillis extends Clock[Timestamp]
final case class CurrentNanos extends Clock[Nanos]
final case class EntryId(val asString: String) extends AnyVal
sealed trait Audit[A]
final case class StartEntry(startTs: Timestamp, startNanos: Long, log: Json)
extends Audit[EntryId]
final case class EndEntry(id: EntryId, endNanos: Long)
extends Audit[Unit]
// Another possibility
final case class AuditLogEntry(user: User, ts: Timestamp, operation: Json)
sealed trait AuditLog[A]
final case class AddEntry(operation: Json) extends AuditLog[AuditLogEntry]
type ClockF[A] = Coyoneda[Clock, A]
type AuditF[A] = Coyoneda[Audit, A]
def currentMillis[S[_]: Functor](implicit S: ClockF :<: S): Free[S, Timestamp]
def currentNanos[S[_]: Functor](implicit S: ClockF :<: S): Free[S, Nanos]
def startEntry[S[_]: Functor](startTs: Timestamp, startNanos: Long, log: Json)(implicit S: AuditF :<: S): Free[S, EntryId]
def endEntry[S[_]: Functor](entryId: EntryId, endNanos: Long)(implicit S: AuditF :<: S): Free[S, Unit]
def log[S[_]: Functor](log: Json, op: Free[S, A])(implicit S0: AuditF :<: S, S1: ClockF :<: S): Free[S, A] =
for {
ts <- currentMillis
ns <- currentNanos
id <- startEntry(ts, ns, log)
a <- op
ne <- currentNanos
_ <- endEntry(id, ne)
} yield a
// or, if you want to log a particular api
sealed trait Api[A]
final case class StrOp extends Api[String]
final case class IntOp extends Api[Int]
type ApiF[A] = Coyoneda[Api, A]
type Eff0[A] = Coproduct[AuditF, ClockF, A]
type Eff[A] = Coproduct[ApiF, Eff0, A]
val logApi: Api ~> Free[Eff, ?] =
new (Api ~> Free[Eff, ?]) {
def apply[A](api: Api[A]) = api match {
case StrOp =>
log(JString("StrOp!"), <inject StrOp into Eff, lift into Free>)
case IntOp =>
log(JString("IntOp!"), <inject IntOp into Eff, lift into Free)
}
}
val someApiProgram: Free[ApiF, A] = ???
val loggedApiProgram = someApiProgram flatMapSuspension logApi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment