A sense of place

Intro

Secondary requirements of functional programs

Something I worked on lately

 

Something I worked on lately

What goes into running benchmarks

Functions

type Setup[I] = I
type RunBenchmarks[I, R] = I => R
type Sink[R, O] = R => O
type Cleanup[I, C] = I => C

Composition

def setupRunSinkAndCleanup[F[_]: MonadBracket, I, O](
  setup: F[I],
  runBenchmarks: I => F[R],
  sink: R => F[O],
  cleanup: I => F[Unit]
): F[O] =
  for {
    start <- setup
    out <- (for {
      results <- runBenchmarks
      output <- sink(results)
    } yield output).guarantee(cleanup(start))
  } yield out

Logging

Log what you're doing

When will then be now?

You choose

Logging

final case class Log[L, A](
  debug: L => A,
  info: L => A,
  error: L => A
)

def unlevelledLog[L, A](f: L => A): Log[L, A] = 
  Log(f, f, f)
implicit val logProfunctor: Profunctor[Log] = 
  new Profunctor[Log] {
  def dimap[A, B, C, D]
    (l: Log[A, B])(pre: C => A)(post: B => D)
  : Log[C, D] = Log(
    (c: C) => post(l.debug(pre(c))),
    (c: C) => post(l.info(pre(c))),
    (c: C) => post(l.error(pre(c)))
  )
} 
implicit val logCategory: Category[Log] = 
  new Category[Log] {
    def id[A]: Log[A, A] =
      Log(a => a, a => a, a => a)

    def compose[A, B, C]
      (f: Log[B, C], g: Log[A, B])
    : Log[A, C] = Log(
        (a: A) => f.debug(g.debug(a)),
        (a: A) => f.info(g.info(a)),
        (a: A) => f.error(g.error(a))
    )
  }
// exercise!
implicit val logMonad[L]: Monad[Log[L, ?]] = ???
val levelledLog = Log[String, String](
  debug = msg => s"Debug: $msg",
  info = msg => s"Info: $msg",
  error = msg => s"Error: $msg"
)
val consoleLog: Log[String, IO[Unit]] =
  levelledLog.rmap(console.putStrLn)

Places

def logWithPlace(place: String, msg: String): String =
  s"[$place]: $msg"

def consoleLoggerWithPlace: Log[(String, String), IO[Unit]] =
  consoleLog.lmap { (logWithPlace _).tupled }

Argument passing

final class ReaderT[F[_], E, A](run: E => F[A])
def flatten[F[_], E, A]
  (r: ReaderT[F, E, ReaderT[F, E, A]])
  (implicit F: Monad[F]): ReaderT[F, E, A] = {
  ReaderT((e: E) => r.run(e).flatMap(_.run(e)))
}
def local[F[_], E, NE, A]
  (f: NE => E)
  (rt: ReaderT[F, E, A]): ReaderT[F, NE, A] =
    ReaderT(rt.run.compose(f))
def ask[F[_]: Applicative, E]: ReaderT[F, E, E] =
  ReaderT((e: E) => e.pure[F])
def setupRunSinkAndCleanup[
  F[_]: MonadBracket: MonadReader[?[_], String],
  E,
  I,
  O
](
  setup: F[I],
  runBenchmarks: I => F[R],
  sink: R => F[O],
  cleanup: I => F[Unit]
): F[O] =
  for {
    start <- setup.local("Setup->" + _)
    out <- (for {
      results <- runBenchmarks.local("Run Benchmarks->" + _)
      output <- sink(results).local("Output->" + _)
    }).guarantee(cleanup(start).local("Cleanup->" + _)
  } yield out

Logging with place

val consoleLogWithPlace: Log[(String, String), IO[Unit]]

// case class ReaderT[F[_], E, A](run: E => F[A])

val goal: Log[String, ReaderT[IO, String, Unit]] = ???





   String => ReaderT[IO, String, Unit]





      String => String => IO[Unit]





     ((String, String)) => IO[Unit]

def convertLogFunction[F[_]: Monad, A, E, B](
  f: ((E, A)) => F[B]
): A => ReaderT[F, E, B] =
  a => ReaderT[F, E, B](e => f((e, a)))

val readerLog: Log[String, ReaderT[IO, String, Unit]] =
  Log(
    debug = convertLogFunction(consoleLogWithPlace.debug),
    info = convertLogFunction(consoleLogWithPlace.info),
    error = convertLogFunction(consoleLogWithPlace.error)
  )

Who passes the function to the function composer?

No

final class LoggerAndScope[L, A](
  logger: Log[L, A], scope: String
)
trait HasLogger[S, L, A] {
  def getLogger(s: S[L, A]): Log[L, A]
  def modifyLogger(f: Log[L, A] => Log[L, A]): S[L, A] => S[L, A]
}

trait HasScope[S] {
  def getScope(s: S): String
  def modifyScope(f: String => String): S => S
}
def setupRunSinkAndCleanup[
  F[_]: MonadBracket: MonadReader[?[_], Env],
  Env
  I,
  O
](
  setup: F[I],
  runBenchmarks: I => F[R],
  sink: R => F[O],
  cleanup: I => F[Unit]
)(
  implicit S: HasScope[S]
): F[O] =
  for {
    start <- setup.local(_.modifyScope("Setup->" + _))
    out <- (for {
      results <- runBenchmarks(start)
        .local(S.modifyScope("Run Benchmarks->" + _))
      output <- sink(results)
        .local(S.modifyScope("Output->" + _))
    }).guarantee(cleanup(start)
        .local(S.modifyScope("Cleanup->" + _))
  } yield out

Errors and crashes

Performance

Addendum

Questions?

deck

By edmundnoble

deck

  • 122