The document discusses reinventing the free monad to model effects in different ways, including with pure effects, effects with returns, and simpler effects using bind. It also covers composing effects, type-safe mocking, runtime optimization, aspect-oriented programming, and hacking the free monad to add capabilities like parallelism, failure handling, and nondeterminism. However, it notes that excessively hacking the free monad can lead to poor composability and performance due to deeply nested types, and can blur the distinction between effects and machinery.
2. OUTLINE
> Free What?
> Live Free...
> ...Or Die Hard
> Exploration of Solutions
> Hacks for Free
> Reconstructing Free
> Fixing Free
> Closing Thoughts
3. FREE WHAT?
REINVENTING FREE: PURE EFFECTS
data ConsoleIO
= WriteLine String ConsoleIO
| ReadLine (String -> ConsoleIO)
| End
sealed trait ConsoleIO
final case class WriteLine(line: String, then: ConsoleIO) extends ConsoleIO
final case class ReadLine(process: String => ConsoleIO) extends ConsoleIO
final case object End extends ConsoleIO
4. 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]
5. 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)
6. 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]
7. 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)
8. 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]
9. 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
10. 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]
11. 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]
12. 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
13. 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]
15. 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 ()
16. 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]
17. 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]
19. LIVE FREE...
TYPE-SAFE MOCKING
mockSpec :: MockSpec ConsoleIO
mockSpec = 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 ()
20. LIVE FREE...
RUNTIME OPTIMIZATION (PURESCRIPT)
data Parser a
= ParseChar (Char -> a)
| ParseString (String -> a)
| Fail String
optimize :: FreeAp Parser ~> FreeAp Parser
optimize = ...
21. 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, ?] = ???
22. 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 Unit
program = ...
program' :: Free (Coproduct ConsoleIO FileIO) Unit
program' = foldFree weaveLogging program
24. ...OR DIE HARD
THE FREE MONAD IS ONLY A FREE MONAD
> Parallelism
> Failure
> Alternatives
> Nondeterminism
> ...And all other abstractions with more structure than a
monad.
25. ...OR DIE HARD
USE CASE #1: CONCURRENCY
loadModel = do
token <- authenticate
sequential $
Model <$> parallel (get "/products/popular/" token)
<*> parallel (get "/categories/all" token)
26. ...OR DIE HARD
USE 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
27. ...OR DIE HARD
USE CASE #3: FAILURE
handleError (readConfig specifiedDir) (const $ readConfig defaultDir)
handleError(readConfig(specifiedDir),
Function.const(readConfig(defaultDir)))
28. HACKS FOR FREE
HACKING PARALLELISM: TYPE & EFFECT
type SeqPar f = Free (FreeAp f)
liftFA :: forall f. f ~> SeqPar f
liftFA 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))
29. HACKS FOR FREE
HACKING PARALLELISM: LIFTING PAR & SEQ
liftSeq :: forall f a. Free f a -> SeqPar f a
liftSeq = foldFree liftFA
liftPar :: forall f a. FreeAp f a -> SeqPar f a
liftPar = 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)
30. HACKS FOR FREE
HACKING PARALLELISM: OPTIMIZATION
type ParInterpreter f g = FreeAp f ~> g
type ParOptimizer f g = ParInterpreter f (SeqPar g)
optimize :: forall f g a. (FreeAp f ~> SeqPar g) -> SeqPar f a -> SeqPar g a
optimize = foldFree
type ParInterpreter[F[_], G[_]] = FreeAp[F, ?] ~> G
type 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)
31. HACKS FOR FREE
HACKING 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]
33. HACKS FOR FREE
HACKING 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)
34. HACKS FOR FREE
HACKING 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]
35. HACKS FOR FREE
THE PROBLEMS WITH HACKING
> Composes poorly, and at great cost to performance, usability
data 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.
36. HACKS FOR FREE
WISH LIST
> Improve performance
> Improve usability
> Cleanly separate effects and machinery
39. RECONSTRUCTING FREE
TYPE 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)
43. RECONSTRUCTING FREE
PARALLELISM
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
}
44. RECONSTRUCTING FREE
FAILURE
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]
47. RECONSTRUCTING FREE
THE 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
48. RECONSTRUCTING FREE
WISH LIST
> Fine-grained features — pay for what you use
> Compositional features
> Compositional interpreters
49. FIXING FREE
DETOUR: RECURSIVE EXPR
data Expr = Lit Int | Add Expr Expr
sealed trait Expr
final case class Lit(value: Int) extends Expr
final case class Add(left: Expr, right: Expr) extends Expr
50. FIXING FREE
DETOUR: 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]
51. FIXING FREE
DETOUR: 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]
52. FIXING FREE
DETOUR: 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]
53. FIXING FREE
DETOUR: FIXED LIST — EMPTY, CONS, UNCONS
empty :: List a
empty = Fixed Empty
cons :: a -> List a -> List a
cons a as = Fixed (Cons a as)
uncons :: List a -> Maybe (Tuple a List a)
uncons (Fixed Empty) = Nothing
uncons (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))
}
}
54. FIXING FREE
DETOUR: FIXED LIST — COMPOSABLE TERMS (PURESCRIPT)
data Empty z a = Empty
data 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
55. FIXING FREE
DETOUR: 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]
56. FIXING FREE
DETOUR: FIXED LIST — COMPOSABLE TERMS (PURESCRIPT)
empty :: forall a. List a
empty = Fixed (CLeft Empty)
cons :: forall a. a -> List a -> List a
cons a as = Fixed <<< CRight <<< CLeft $ Cons a as
concat :: forall a. List a -> List a -> List a
concat 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))
57. FIXING FREE
DETOUR: 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)))
}
}
}
58. FIXING FREE
BASIC TERM DEFINITION
A program that halts, runs
forever, or produces an A
^
|
|
t z e a
T[Z[_], E[_], A]
^ ^ ^
| | |
| | |
Self | |
Effect |
Return
Kind: (* -> *) -> (* -> *) -> * -> *
59. FIXING FREE
EXTENDED TERM DEFINITION
A program that halts, errors
with an E, runs forever, or
produces an A
^
|
|
t e z e a
T[E, Z[_], E[_], A]
^ ^ ^ ^
| | | |
| | | |
| Self | |
| Effect |
| Return
Error
Kind: * -> (* -> *) -> (* -> *) -> * -> *
62. FIXING FREE
SEQUENCE
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]
}
63. FIXING FREE
PARALLEL
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
}
64. FIXING FREE
FAILURE
data Failure e z f a = Failure e
data 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])
65. FIXING FREE
ORDERED 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])
66. FIXING FREE
TERM 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]
67. FIXING FREE
FIXING
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]
)
68. FIXING FREE
THE PROBLEMS WITH FIXING
> Straining language capabilities
> "Benign" boilerplate
> But...type-level or macro machinery to the rescue?
69. FIXING FREE
WISH LIST
> Language-level support for positively & negatively
constrained, heterogeneous sets (unions)
> Recursion as a computational feature
> i.e. A NEW PROGRAMMING LANGUAGE
70. CLOSING THOUGHTS
WHERE 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
71. CLOSING THOUGHTS
WHERE 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
72. THANK YOUFOLLOW ME ON TWITTER AT @JDEGOES
READ MY BLOG ON HTTP://DEGOES.NET