Post-Free: Life After Free Monads
-
Upload
john-de-goes -
Category
Technology
-
view
1.482 -
download
0
Transcript of Post-Free: Life After Free Monads
POST-FREE: LIFE AFTER
FREE MONADSJOHN A. DE GOES — @JDEGOES
OUTLINE> Free What?> Live Free...
> ...Or Die Hard> Exploration of Solutions
> Hacks for Free> Reconstructing Free
> Fixing Free> Closing Thoughts
FREE WHAT?REINVENTING FREE: PURE EFFECTS
data ConsoleIO = WriteLine String ConsoleIO | ReadLine (String -> ConsoleIO) | End
sealed trait ConsoleIOfinal case class WriteLine(line: String, then: ConsoleIO) extends ConsoleIOfinal case class ReadLine(process: String => ConsoleIO) extends ConsoleIOfinal case object End extends ConsoleIO
FREE WHAT?REINVENTING FREE: PURE EFFECTS W/RETURNS
data ConsoleIO a = WriteLine String (ConsoleIO a) | ReadLine (String -> ConsoleIO a) | EndWith a
sealed trait ConsoleIO[A]final case class WriteLine[A](line: String, then: ConsoleIO[A]) extends ConsoleIO[A]final case class ReadLine[A](process: String => ConsoleIO[A]) extends ConsoleIO[A]final case class EndWith[A](value: A) extends ConsoleIO[A]
FREE WHAT?REINVENTING FREE: PURE EFFECTS W/MAP (PURESCRIPT)
data ConsoleIO a = WriteLine String (ConsoleIO a) | ReadLine (String -> ConsoleIO a) | EndWith a | Map (forall z. (forall a0. ConsoleIO a0 -> (a0 -> a) -> z) -> z)
FREE WHAT?REINVENTING FREE: PURE EFFECTS W/MAP (SCALA)
sealed trait ConsoleIO[A] { def map[B](f: A => B): Console[B] = Map(this, f)}final case class WriteLine[A](line: String, then: ConsoleIO[A]) extends ConsoleIO[A]final case class ReadLine[A](process: String => ConsoleIO[A]) extends ConsoleIO[A]final case class EndWith[A](value: A) extends ConsoleIO[A]final case class Map[A0, A](v: ConsoleIO[A0], f: A0 => A) extends ConsoleIO[A]
FREE WHAT?REINVENTING FREE: SIMPLER EFFECTS W/BIND (PURESCRIPT)
data ConsoleIO a = WriteLine String a | ReadLine (String -> a) | Pure a | Chain (forall z. (forall a0. Console a0 -> (a0 -> ConsoleIO a) -> z) -> z)
FREE WHAT?REINVENTING FREE: SIMPLER EFFECTS W/BIND (SCALA)
sealed trait ConsoleIO[A] { def map[B](f: A => B): ConsoleIO[B] = flatMap(a => Pure[B](f(a))) def flatMap[B](f: A => ConsoleIO[B]): ConsoleIO[B] = Chain(this, f)}final case class WriteLine(line: String) extends ConsoleIO[Unit]final case class ReadLine() extends ConsoleIO[String]final case class Pure[A](value: A) extends ConsoleIO[A]final case class Chain[A0, A](v: ConsoleIO[A0], f: A0 => ConsoleIO[A]) extends ConsoleIO[A]
FREE WHAT?REINVENTING FREE: SIMPLER EFFECTS W/BIND (PURESCRIPT)
data ConsoleIOF a = WriteLine String a | ReadLine (String -> a)
data Free f a = Pure a | Effect (f a) | Chain (forall z. ( forall a0. Free f a0 -> (a0 -> Free f a) -> z) -> z)
type ConsoleIO = Free ConsoleIOF
FREE WHAT?REINVENTING FREE: SIMPLER EFFECTS W/BIND (SCALA)
sealed trait ConsoleIOF[A]final case class WriteLine(line: String) extends ConsoleIOF[Unit]final case class ReadLine() extends ConsoleIOF[String]
sealed trait Free[F[_], A]final case class Pure[F[_], A](value: A) extends Free[F, A]final case class Effect[F[_], A](effect: F[A]) extends Free[F, A]final case class Chain[F[_], A0, A](v: Free[A0], f: A0 => Free[A]) extends Free[F, A]
type ConsoleIO[A] = Free[ConsoleIOF, A]
FREE WHAT?CLASSIC DEFINITION OF FREE
data Free f a = Point a | Join (f (Free f a))
sealed trait Free[F[_], A]final case class Point[F[_], A](value: A) extends Free[F, A]final case class Join[F[_], A](value: F[Free[F, A]]) extends Free[F, A]
FREE WHAT?INTERPRETATION OF FREE
Free f a Free[F, A] ^ ^ ^ | \ \------- The value produced by the program | \ | \ The effects of the program |A program that halts, runs forever, or produces an A
FREE WHAT?EXAMPLE: EFFECTS
data ConsoleIO a = ReadLine (String -> a) | WriteLine a String
sealed trait ConsoleIO[A]final case class ReadLine[A](next: String => A) extends ConsoleIO[A]final case class WriteLine[A](next: A, line: String) extends ConsoleIO[A]
FREE WHAT?EXAMPLE: HELPERS
readLine :: Free ConsoleIO StringreadLine = liftF (ReadLine id)
writeLine :: String -> Free ConsoleIO UnitwriteLine = liftF <<< WriteLine unit
def readLine: Free[ConsoleIO, String] = Free.liftF(ReadLine[String](identity))def writeLine(line: String): Free[ConsoleIO, Unit] = Free.liftF(WriteLine[Unit]((), line))
FREE WHAT?EXAMPLE: PROGRAM
program = do writeLine "What is your name?" n <- readLine writeLine ("Hello, " <> n <> "!") return unit
def program: Free[ConsoleIO, Unit] = for { _ <- writeLine("What is your name?") n <- readLine _ <- writeLine("Hello, " + n + "!") } yield ()
FREE WHAT?COMPOSITION: FILEIO
data FileIO a = ReadFile (Bytes -> a) String | WriteFile a String Bytes
sealed trait FileIO[A]final case class ReadFile[A](next: Bytes => A, name: String) extends FileIO[A]final case class WriteFile[A](next: A, name: String, file: Bytes) extends FileIO[A]
FREE WHAT?COMPOSITION: COPRODUCT
data Coproduct f g a = Left (f a) | Right (g a)
sealed trait Coproduct[F[_], G[_], A]final case class Left[F[_], G[_], A](value: F[A]) extends Coproduct[F, G, A]final case class Right[F[_], G[_], A](value: G[A]) extends Coproduct[F, G, A]
FREE WHAT?COMPOSITION: PROGRAM
type Program = Free (Coproduct FileIO ConsoleIO)
type Program[A] = Free[Coproduct[FileIO, ConsoleIO, ?], A]
LIVE FREE...TYPE-SAFE MOCKING
mockSpec :: MockSpec ConsoleIOmockSpec = do expectWrite _WriteLine (assertEquals "What is your name?") expectRead _ReadLine "World" expectWrite _WriteLine (assertEquals "Hello, World!")
def mockSpec: MockSpec[ConsoleIO] = for { _ <- expectWrite(_WriteLine, assertEquals("What is your name?")) _ <- expectRead(_ReadLine, "World") _ <- expectWrite(_WriteLine, assertEquals("Hello World")) } yield ()
LIVE FREE...RUNTIME OPTIMIZATION (PURESCRIPT)
data Parser a = ParseChar (Char -> a) | ParseString (String -> a) | Fail String
optimize :: FreeAp Parser ~> FreeAp Parseroptimize = ...
LIVE FREE...RUNTIME OPTIMIZATION (SCALA)
sealed trait Parser[A]final case class ParseChar[A](next: Char => A) extends Parser[A]final case class ParseString[A](next: String => A) extends Parser[A]final case class Fail[A](error: String) extends Parser[A]
def optimize: FreeAp[Parser, ?] ~> FreeAp[Parser, ?] = ???
LIVE FREE...ASPECT-ORIENTED PROGRAMMING (PURESCRIPT)
log line = liftF (Left (WriteLine unit line))
weaveLogging :: FileIO ~> Free (Coproduct ConsoleIO FileIO)weaveLogging (ReadFile next name) = do log $ "Reading file: " <> name bytes <- liftF (Right (ReadFile id name)) log $ "File contents: " <> show bytes return (next bytes) weaveLogging (WriteFile next name bytes) = ...
program :: Free FileIO Unitprogram = ...
program' :: Free (Coproduct ConsoleIO FileIO) Unitprogram' = foldFree weaveLogging program
LIVE FREE...ASPECT-ORIENTED PROGRAMMING (SCALA)
def weaveLogging: FileIO ~> Free[Coproduct[ConsoleIO, FileIO, ?], ?] = new NaturalTransformation[FileIO, Coproduct[ConsoleIO, FileIO, ?]] { def log(line: String) = Free.liftF(Left[ConsoleIO, FileIO, Unit](WriteLine(unit, line)))
def apply[A](fa: FileIO[A]): Coproduct[ConsoleIO, FileIO, A] = fa match { case (ReadFile(next, name)) => for { _ <- log("Reading file: " + name) bytes <- liftF(Right[ConsoleIO, FileIO, Bytes](ReadFile(id, name)) _ <- log("File contents: " + bytes) } yield next(bytes)
case (WriteFile(next, name, bytes)) => ??? } }
...OR DIE HARDTHE FREE MONAD IS ONLY A FREE MONAD
> Parallelism> Failure
> Alternatives> Nondeterminism
> ...And all other abstractions with more structure than a monad.
...OR DIE HARDUSE CASE #1: CONCURRENCY
loadModel = do token <- authenticate sequential $ Model <$> parallel (get "/products/popular/" token) <*> parallel (get "/categories/all" token)
...OR DIE HARDUSE CASE #2: NONDETERMINISM
data UI a = Click (ClickEvent -> a) | KeyPress (KeyEvent -> a) | GetClass (String -> a) | SetClass String a | ...
doubleClick btn = guard ((\e1 e2 -> (e2.ts - e1.ts) < 200) <$> click btn <*> click btn)
toggleOnDoubleClick btn = doubleClick btn *> toggleClass "toggled" btn
toggleFirst = foldl (\e m -> m <|> toggleOnDoubleClick e) empty buttons
...OR DIE HARDUSE CASE #3: FAILURE
handleError (readConfig specifiedDir) (const $ readConfig defaultDir)
handleError(readConfig(specifiedDir), Function.const(readConfig(defaultDir)))
HACKS FOR FREEHACKING PARALLELISM: TYPE & EFFECT
type SeqPar f = Free (FreeAp f)
liftFA :: forall f. f ~> SeqPar fliftFA fa = liftF (liftFreeAp fa)
type SeqPar[F[_], A] = Free[FreeAp[F, ?], A]
def liftFA[F[_], A](fa: F[A]): SeqPar[F, A] = Free.liftF[FreeAp[F, ?], A](FreeAp.lift(fa))
HACKS FOR FREEHACKING PARALLELISM: LIFTING PAR & SEQ
liftSeq :: forall f a. Free f a -> SeqPar f aliftSeq = foldFree liftFA
liftPar :: forall f a. FreeAp f a -> SeqPar f aliftPar = liftF
def liftSeq[F[_], A](freefa: Free[F, A]): SeqPar[F, A] = { implicit val m: Monad[SeqPar[F, ?]] = Free.freeMonad[FreeAp[F, ?]]
freefa.foldMap[SeqPar[F, ?]](new NaturalTransformation[F, SeqPar[F, ?]] { def apply[A](fa: F[A]): SeqPar[F, A] = liftFA(fa) })}
def liftPar[F[_], A](freeap: FreeAp[F, A]): SeqPar[F, A] = Free.liftF[FreeAp[F, ?], A](freeap)
HACKS FOR FREEHACKING PARALLELISM: OPTIMIZATION
type ParInterpreter f g = FreeAp f ~> gtype ParOptimizer f g = ParInterpreter f (SeqPar g)
optimize :: forall f g a. (FreeAp f ~> SeqPar g) -> SeqPar f a -> SeqPar g aoptimize = foldFree
type ParInterpreter[F[_], G[_]] = FreeAp[F, ?] ~> Gtype ParOptimizer[F[_], G[_]] = ParInterpreter[F, SeqPar[G, ?]]
def optimize[F[_], G[_], A](nt: FreeAp[F, ?] ~> SeqPar[G, ?], p: SeqPar[F, A]): SeqPar[G, A] = { implicit val m: Monad[SeqPar[G, ?]] = Free.freeMonad[FreeAp[G, ?]]
p.foldMap[SeqPar[G, ?]](nt)}
def parOptimize[F[_], G[_], A](nt: FreeAp[F, ?] ~> FreeAp[G, ?], p: SeqPar[F, A]): SeqPar[G, A] = optimize(new NaturalTransformation[FreeAp[F, ?], SeqPar[G, ?]] { def apply[A](freeap: FreeAp[F, A]): SeqPar[G, A] = liftPar(nt(freeap)) }, p)
HACKS FOR FREEHACKING NONDETERMINISM: TYPE
data FreeAlt e f a = FreeAlt (Free (Alt e f) a)
data Alt e f a = Failure e | FirstSuccess (FreeAlt e f a) (FreeAlt e f a) | Effect (f a)
final case class FreeAlt[E, F[_], A](run: Free[Alt[E, F, ?], A])
sealed trait Alt[E, F[_], A]final case class Failure[E, F[_], A](error: E) extends Alt[E, F, A]final case class FirstSuccess[E, F[_], A](first: FreeAlt[E, F, A], second: FreeAlt[E, F, A], merge: (E, E) => E) extends Alt[E, F, A]final case class Effect[E, F[_], A](effect: F[A]) extends Alt[E, F, A]
HACKS FOR FREEHACKING NONDETERMINISM: INSTANCES
implicit def FreeAltMonadPlus[E: Monoid, F[_]]: MonadPlus[FreeAlt[E, F, ?]] = new MonadPlus[FreeAlt[E, F, ?]] { implicit val m: Monad[Free[Alt[E, F, ?], ?]] = Free.freeMonad[Alt[E, F, ?]]
def point[A](a: => A): FreeAlt[E, F, A] = FreeAlt[E, F, A](m.point(a))
def bind[A, B](fa: FreeAlt[E, F, A])(f: A => FreeAlt[E, F, B]): FreeAlt[E, F, B] = FreeAlt[E, F, B](m.bind(fa.run)(f.map(_.run)))
def plus[A](a: FreeAlt[E, F, A], b: => FreeAlt[E, F, A]): FreeAlt[E, F, A] = FreeAlt(Free.liftF[Alt[E, F, ?], A]( FirstSuccess[E, F, A](a, b, Monoid[E].append(_, _))))
def empty[A]: FreeAlt[E, F, A] = FreeAlt(Free.liftF[Alt[E, F, ?], A](Failure[E, F, A](Monoid[E].zero)))}
HACKS FOR FREEHACKING NONDETERMINISM: IMPROVING COMPOSABILITY
(PURESCRIPT)
data FreeAlt e f a = Free (Alt FreeAlt e f) a
data Alt t e f a = Failure e | FirstSuccess (t e f a) (t e f a) | Effect (f a)
HACKS FOR FREEHACKING NONDETERMINISM: IMPROVING COMPOSABILITY (SCALA)
final case class FreeAlt[E, F[_], A](run: Free[Alt[FreeAlt, E, F, ?], A])
sealed trait Alt[T[_, _[_], _], E, F[_], A]final case class Failure[E, F[_], A](error: E) extends Alt[E, F, A]final case class FirstSuccess[E, F[_], A](first: T[E, F, A], second: T[E, F, A], merge: (E, E) => E) extends Alt[E, F, A]final case class Effect[E, F[_], A](effect: F[A]) extends Alt[E, F, A]
HACKS FOR FREETHE PROBLEMS WITH HACKING
> Composes poorly, and at great cost to performance, usabilitydata MyFree f a
= MyFree (Free (
Alt MyFree (
Parallel MyFree (
Race MyFree f))) a)
final case class MyFree[F[_], A](
run: Free[Alt[MyFree, Parallel[MyFree, Race[MyFree, F, ?], ?], ?], A])> Blurs machinery & effects
> Where is effect? It's nested inside n-levels of machinery.
HACKS FOR FREEWISH LIST
> Improve performance> Improve usability
> Cleanly separate effects and machinery
RECONSTRUCTING FREETYPE DEFINITION (PURESCRIPT)
data FreeStar e f a ^ ^ ^ | | | Error| Return Value | Effect
RECONSTRUCTING FREETYPE DEFINITION (SCALA)
sealed trait FreeStar[E, F[_], A] ^ ^ ^ | | | Error | Return Value | Effect
RECONSTRUCTING FREETYPE DEFINITION (PURESCRIPT)
data FreeStar e f a = Pure a | Effect (f a) | Sequence (forall z. (forall a0. FreeStar e f a0 -> (a0 -> FreeStar e f a) -> z) -> z) | Parallel (forall z. (forall l r. FreeStar e f l -> FreeStar e f r -> (l -> r -> a) -> z) -> z) | Failure e | Recover (FreeStar e f a) (e -> FreeStar e f a) | FirstSuccess (FreeStar e f a) (FreeStar e f a)
RECONSTRUCTING FREEPURITY
final case class Pure[E, F[_], A](a: A) extends FreeStar[E, F, A]
RECONSTRUCTING FREEEFFECTS
final case class Effect[E, F[_], A](fa: F[A]) extends FreeStar[E, F, A]
RECONSTRUCTING FREESEQUENCING
sealed trait Sequence[E, F[_], A] extends FreeStar[E, F, A] { type A0
def a: FreeStar[E, F, A0] def f: A0 => FreeStar[E, F, A]}
RECONSTRUCTING FREEPARALLELISM
sealed trait Parallel[E, F[_], A] extends FreeStar[E, F, A] { type B type C
def left: FreeStar[E, F, B]
def right: FreeStar[E, F, C]
def join: (B, C) => A}
RECONSTRUCTING FREEFAILURE
final case class Failure[E, F[_], A](error: E) extends FreeStar[E, F, A]
final case class Recover[E, F[_], A]( value: FreeStar[E, F, A], f: E => FreeStar[E, F, A]) extends FreeStar[E, F, A]
RECONSTRUCTING FREENONDETERMINISM
final case class FirstSuccess[E, F[_], A]( first: FreeStar[E, F, A], second: FreeStar[E, F, A]) extends FreeStar[E, F, A]
RECONSTRUCTING FREEINSTANCES
implicit def FreeStarMonadPlus[E: Monoid, F[_]] = new MonadPlus[FreeStar[E, F, ?]] with MonadError[FreeStar[E, F, ?], E] { def point[A](a: => A): FreeStar[E, F, A] = Pure[E, F, A](a)
def bind[A, B](fa: FreeStar[E, F, A])(f: A => FreeStar[E, F, B]): FreeStar[E, F, B] = Sequence[A, E, F, B](fa, f)
def plus[A](a: FreeStar[E, F, A], b: => FreeStar[E, F, A]): FreeStar[E, F, A] = FirstSuccess(a, b)
def empty[A]: FreeStar[E, F, A] = Failure[E, F, A](Monoid[E].zero)
def raiseError[A](e: E): FreeStar[E, F, A] = Failure(e)
def handleError[A](fa: FreeStar[E, F, A])(f: E => FreeStar[E, F, A]): FreeStar[E, F, A] = Recover(fa, f) }
RECONSTRUCTING FREETHE PROBLEMS WITH RECONSTRUCTION
> Whole program has access to all features> Constraints (on features) liberate (interpreters)> Liberties (on features) constrain (interpreters)
> Cannot express one feature in terms of others> Must interpret all features at once
RECONSTRUCTING FREEWISH LIST
> Fine-grained features — pay for what you use> Compositional features
> Compositional interpreters
FIXING FREEDETOUR: RECURSIVE EXPR
data Expr = Lit Int | Add Expr Expr
sealed trait Exprfinal case class Lit(value: Int) extends Exprfinal case class Add(left: Expr, right: Expr) extends Expr
FIXING FREEDETOUR: FIXED EXPR
data Expr a = Lit Int | Add a a
data Fixed f = Fixed (f (Fixed f))
type RecursiveExpr = Fixed Expr
sealed trait Expr[A]final case class Lit[A](value: Int) extends Expr[A]final case class Add[A](left: A, right: A) extends Expr[A]
final case class Fixed[F[_]](unfix: F[Fixed[F]])
type RecursiveExpr = Fixed[Expr]
FIXING FREEDETOUR: RECURSIVE LIST
data List a = Empty | Cons a (List a)
sealed trait List[A]final case class Empty[A]() extends List[A]final case class Cons[A](head: A, tail: List[A]) extends List[A]
FIXING FREEDETOUR: FIXED LIST
data ListF z a = Empty | Cons a (z a)
data Fixed t a = Fixed (t (Fixed t) a)
type List = Fixed LiftF
sealed trait ListF[Z[_], A]final case class Empty[Z[_], A]() extends ListF[Z, A]final case class Cons[Z[_], A](head: A, tail: Z[A]) extends ListF[Z, A]
final case class Fixed[T[_[_], _], A](unfix: T[Fixed[T, ?], A])
type List[A] = Fixed[ListF, A]
FIXING FREEDETOUR: FIXED LIST — EMPTY, CONS, UNCONS
empty :: List aempty = Fixed Empty
cons :: a -> List a -> List acons a as = Fixed (Cons a as)
uncons :: List a -> Maybe (Tuple a List a)uncons (Fixed Empty) = Nothinguncons (Fixed (Cons a as)) = Just (Tuple a as)
def empty[A]: List[A] = Fixed[ListF, A](Empty[List, A](): ListF[List, A])
def cons[A](a: A, as: List[A]): List[A] = Fixed[ListF, A](Cons[List, A](a, as): ListF[List, A])
def uncons[A](as: List[A]): Option[(A, List[A])] = as match { case Fixed(l) => (l : ListF[List, A]) match { case x : Empty[List, A] => None case x : Cons[List, A] => Some((x.head, x.tail)) }}
FIXING FREEDETOUR: FIXED LIST — COMPOSABLE TERMS (PURESCRIPT)
data Empty z a = Emptydata Cons z a = Cons a (z a)data Concat z a = Concat (z a) (z a)
data Coproduct t1 t2 z a = CLeft (t1 z a) | CRight (t2 z a)
data Fixed t a = Fixed (t (Fixed t) a)
type ConsOrCat = Coproduct Cons Concat
type EmptyOrConsOrCat = Coproduct Empty ConsOrCat
type List a = Fixed EmptyOrConsOrCat a
FIXING FREEDETOUR: FIXED LIST — COMPOSABLE TERMS (SCALA)
final case class Empty[Z[_], A]()final case class Cons[Z[_], A](head: A, tail: Z[A])final case class Concat[Z[_], A](first: Z[A], last: Z[A])
sealed trait Coproduct[T1[_[_], _], T2[_[_], _], Z[_], A]case class CLeft[T1[_[_], _], T2[_[_], _], Z[_], A](value: T1[Z, A]) extends Coproduct[T1, T2, Z, A]case class CRight[T1[_[_], _], T2[_[_], _], Z[_], A](value: T2[Z, A]) extends Coproduct[T1, T2, Z, A]
final case class Fixed[T[_[_], _], A](unfix: T[Fixed[T, ?], A])
type ConsOrCat[Z[_], A] = Coproduct[Cons, Concat, Z, A]
type EmptyOrConsOrCat[Z[_], A] = Coproduct[Empty, ConsOrCat, Z, A]
type List[A] = Fixed[EmptyOrConsOrCat, A]
FIXING FREEDETOUR: FIXED LIST — COMPOSABLE TERMS (PURESCRIPT)
empty :: forall a. List aempty = Fixed (CLeft Empty)
cons :: forall a. a -> List a -> List acons a as = Fixed <<< CRight <<< CLeft $ Cons a as
concat :: forall a. List a -> List a -> List aconcat a1 a2 = Fixed <<< CRight <<< CRight $ Concat a1 a2
uncons :: forall a. List a -> Maybe (Tuple a (List a))uncons (Fixed f) = case f of CLeft Empty -> Nothing CRight (CLeft (Cons a as)) -> Just (Tuple a as) CRight (CRight (Concat a1 a2)) -> case uncons a1 of Nothing -> Nothing Just (Tuple a as) -> Just (Tuple a (concat as a2))
FIXING FREEDETOUR: FIXED LIST — COMPOSABLE TERMS (SCALA)
def empty[A]: List[A] = Fixed[EmptyOrConsOrCat, A]( CLeft[Empty, ConsOrCat, List, A](Empty[List, A]()))def cons[A](a: A, as: List[A]): List[A] = Fixed[EmptyOrConsOrCat, A]( CRight[Empty, ConsOrCat, List, A]( CLeft[Cons, Concat, List, A](Cons[List, A](a, as))))def concat[A](first: List[A], last: List[A]): List[A] = Fixed[EmptyOrConsOrCat, A]( CRight[Empty, ConsOrCat, List, A]( CRight[Cons, Concat, List, A](Concat[List, A](first, last))))def uncons[A](as: List[A]): Option[(A, List[A])] = as.unfix match { case _ : CLeft[Empty, ConsOrCat, List, A] => None case v : CRight[Empty, ConsOrCat, List, A] => v.value match { case v : CLeft[Cons, Concat, List, A] => Some((v.value.head, v.value.tail)) case v : CRight[Cons, Concat, List, A] => uncons(v.value.first) match { case None => uncons(v.value.last) case Some((a, as)) => Some((a, concat(as, v.value.last))) } }}
FIXING FREEBASIC TERM DEFINITION
A program that halts, runs forever, or produces an A ^ | | t z e a T[Z[_], E[_], A] ^ ^ ^ | | | | | | Self | | Effect | Return
Kind: (* -> *) -> (* -> *) -> * -> *
FIXING FREEEXTENDED TERM DEFINITION
A program that halts, errorswith an E, runs forever, or produces an A ^ | | t e z e a T[E, Z[_], E[_], A] ^ ^ ^ ^ | | | | | | | | | Self | | | Effect | | Return Error
Kind: * -> (* -> *) -> (* -> *) -> * -> *
FIXING FREEPURITY
data Pure e z f a = Pure a
final case class Pure[E, Z[_], F[_], A](a: A)
FIXING FREEEFFECTS
data Effect e z f a = Effect (f a)
final case class Effect[E, Z[_], F[_], A](fa: F[A])
FIXING FREESEQUENCE
data Sequence e z f a = Sequence (forall x. (forall a0. z a0 -> (a0 -> z a) -> x) -> x)
trait Sequence[E, Z[_], F[_], A] { type A0
def a: Z[A0] def f: A0 => Z[A]}
FIXING FREEPARALLEL
data Parallel e z f a = Parallel ( forall w. ( forall l r. z l -> z r -> (l -> r -> a) -> w) -> w)
trait Parallel[E, Z[_], F[_], A] { type B type C
def left: Z[B]
def right: Z[C]
def join: (B, C) => A}
FIXING FREEFAILURE
data Failure e z f a = Failure edata Recover e z f a = Recover (z a) (e -> z a)
case class Failure[E, Z[_], F[_], A](error: E)case class Recover[E, Z[_], F[_], A](value: Z[A], f: E => Z[A])
FIXING FREEORDERED ALTERNATE
data FirstSuccess e z f a = FirstSuccess (z a) (z a)
case class FirstSuccess[E, Z[_], F[_], A](first: Z[A], second: Z[A])
FIXING FREETERM COMPOSITION
data EitherF t1 t2 e z f a = LeftF (t1 e z f a) | RightF (t1 e z f a)
sealed trait EitherF[T1[_, _[_], _[_], _], T2[_, _[_], _[_], _], E, Z[_], F[_], A]final case class LeftF[T1[_, _[_], _[_], _], T2[_, _[_], _[_], _], E, Z[_], F[_], A]( value: T1[E, Z, F, A]) extends EitherF[T1, T2, E, Z, F, A]final case class RightF[T1[_, _[_], _[_], _], T2[_, _[_], _[_], _], E, Z[_], F[_], A]( value: T2[E, Z, F, A]) extends EitherF[T1, T2, E, Z, F, A]
FIXING FREEFIXING
data Fixed t e f a = Fixed (t e (Fixed t e f) f a)
final case class Fixed[T[_, _[_], _[_], _], E, F[_], A]( unfix: T[E, Fixed[T, E, F, ?], F, A])
FIXING FREETHE PROBLEMS WITH FIXING
> Straining language capabilities> "Benign" boilerplate
> But...type-level or macro machinery to the rescue?
FIXING FREEWISH LIST
> Language-level support for positively & negatively constrained, heterogeneous sets (unions)> Recursion as a computational feature
> i.e. A NEW PROGRAMMING LANGUAGE
CLOSING THOUGHTSWHERE WE ARE TODAY
> "Post-free" is about reifying the structure of computation> By constraining the structure of subprograms, we
liberate interpretation of them> We can do "post-free" now using a variety of
techniques
CLOSING THOUGHTSWHERE WE COULD GO TOMORROW
> Post-free points to a world where programs are defined by:> (a) Defining computational features in terms of others> (b) Defining program effects in terms of others (onion
architecture)> (c) Type-safe weaving, introspection, mocking, optimization, etc.
> (d) Purely denotational semantics for programs
THANK YOUFOLLOW ME ON TWITTER AT @JDEGOESREAD MY BLOG ON HTTP://DEGOES.NET