frp in clojurescript with javelinalan/index.cgi/doc/tip/... · frp might be good for modeling i/o...

37
fresh fresh diet diet THE THE Fresh, Gourmet, Delivered Daily Fresh, Gourmet, Delivered Daily FRP in ClojureScript with Javelin FRP in ClojureScript with Javelin Alan Dipert Alan Dipert @alandipert @alandipert

Upload: others

Post on 12-Aug-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

freshfreshdietdietTHETHE

Fresh, Gourmet, Delivered DailyFresh, Gourmet, Delivered Daily

FRP in ClojureScript with JavelinFRP in ClojureScript with JavelinAlan DipertAlan Dipert@alandipert@alandipert

Page 2: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not
Page 3: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

nginx

syslog-ng

jetty

postgresredis

Page 4: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not
Page 5: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

fn

fn

val val val

Page 6: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

(+ 1 (* 2 3))

+

1

*

2 3

Page 7: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Applicative EvaluationApplicative Evaluation

● Evaluate argumentsEvaluate arguments● Syntax specifies orderSyntax specifies order

● Apply arguments to functionsApply arguments to functions● No notion of timeNo notion of time

● Almost: in Clojure, CL args eval left to rightAlmost: in Clojure, CL args eval left to right

Page 8: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

(+ 1 (* 2 ?))

+

1

*

2 ?

Page 9: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

(+ 1 (* 2 ?))

+

1 2 ?

Page 10: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

(+ 1 (* 2 ?))

1 2 ?

Page 11: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

1 2 ?

ThreadThread

Page 12: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Child ThreadChild Thread

Child ThreadChild Thread

Parent ThreadParent Thread

Page 13: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

+ time+ time==

Page 14: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

+ time+ time==FunctionalFunctionalReactive Reactive ProgrammingProgramming

Page 15: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Reactive EvaluationReactive Evaluation

+

1

*

2 ?

Page 16: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Reactive EvaluationReactive Evaluation

+

1

*

2 4

do something with result

Page 17: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Reactive EvaluationReactive Evaluation

● Maintain dependency order of processesMaintain dependency order of processes● Evaluation triggered by availability of Evaluation triggered by availability of

argumentsarguments● Instead of by invocation of parentInstead of by invocation of parent

● Programs look/feel applicativePrograms look/feel applicative● We don't need threads (necessarily)We don't need threads (necessarily)

Page 18: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Browsers: bells, whistles, gotchasBrowsers: bells, whistles, gotchas

● JavaScript event loopJavaScript event loop● Callbacks everywhereCallbacks everywhere

● DOM (Document Object Model)DOM (Document Object Model)● DOM elements contribute to application state, but DOM elements contribute to application state, but

are poor variables: they're mutable globalsare poor variables: they're mutable globals

see hlisp! https://github.com/tailrecursion/hlisp-starter

Page 19: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

FRP: Objects in PlayFRP: Objects in Play

● Event StreamsEvent Streams● Sources of zero or more values over timeSources of zero or more values over time● Incoming events trigger activity in whatever is Incoming events trigger activity in whatever is

listeninglistening

● Behaviors (a.k.a. Signals)Behaviors (a.k.a. Signals)● Always-valued boxes, Always-valued boxes, refref-like-like● Value can be derived from a function applied to 1 or Value can be derived from a function applied to 1 or

more “constituent” objectsmore “constituent” objects● Application of this function is triggered by activity in Application of this function is triggered by activity in

constituent objectsconstituent objects

Page 20: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

FRP: Fundamental OperationsFRP: Fundamental Operations

● Event StreamsEvent Streams● filterfilter, , mapmap, , mergemerge etc. etc.● startsWithstartsWith: return a new Behavior backed by the : return a new Behavior backed by the

stream, provided an initial valuestream, provided an initial value

● BehaviorsBehaviors● liftlift: return a new Behavior provided 1 or more : return a new Behavior provided 1 or more

other objects and a function other objects and a function ● changeschanges: return a new Event Stream carrying a : return a new Event Stream carrying a

Behavior's value over timeBehavior's value over time

Page 21: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

FRP with Flapjax: ExampleFRP with Flapjax: Example

HTML<body onload="demo.start()" <h3>Flapjax Demo</h3> <input type="text" id="n1" value="0"/> <input type="text" id="n2" value="0"/> <span id="sum">0</span></body>

Browser

Page 22: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Browser

ClojureScript(defn extractFloatE [id] (F/mapE parse-float (F/extractValueE id)))

(defn start [] (let [n1 (extractFloatE "n1") n2 (extractFloatE "n2") sum (F/liftB + n1 n2)] (F/insertValueB sum "sum" "innerHTML")))

Page 23: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Browser

ClojureScript(defn extractFloatE [id] (F/mapE parse-float (F/extractValueE id)))

(defn start [] (let [n1 (extractFloatE "n1") n2 (extractFloatE "n2") sum (F/liftB + n1 n2)] (F/insertValueB sum "sum" "innerHTML")))

Page 24: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Browser

M-x ceremony-mode(defn extractFloatE [id] (F/mapE parse-float (F/extractValueE id)))

(defn start [] (let [n1 (extractFloatE "n1") n2 (extractFloatE "n2") sum (F/liftB + n1 n2)] (F/insertValueB sum "sum" "innerHTML")))

Page 25: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Browser

ClojureScript(defn extractFloatE [id] (F/mapE parse-float (F/extractValueE id)))

(defn start [] (let [n1 (extractFloatE "n1") n2 (extractFloatE "n2") sum (F/liftB + n1 n2)] (F/insertValueB sum "sum" "innerHTML")))

Page 26: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Browser

ClojureScript(defn extractFloatE [id] (F/mapE parse-float (F/extractValueE id)))

(defn start [] (let [n1 (extractFloatE "n1") n2 (extractFloatE "n2") sum (+ n1 n2)] (F/insertValueB sum "sum" "innerHTML")))

Page 27: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

omg

Page 28: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

SpreadsheetsSpreadsheets

● Input cellsInput cells● user inserts values, evaluation propagates when user inserts values, evaluation propagates when

new values are enterednew values are entered

● Formula cellsFormula cells● user defines, evaluated when constituent cells user defines, evaluated when constituent cells

change valuechange value

● If spreadsheets are so awesome, why do we If spreadsheets are so awesome, why do we care about FRP?care about FRP?

Page 29: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Continuous vs. Discrete Continuous vs. Discrete PropagationPropagation

● Spreadsheet propagation is Spreadsheet propagation is continuouscontinuous● Evaluation only happens if new values are Evaluation only happens if new values are

introduced into the systemintroduced into the system● It's not possible to trigger evaluation without It's not possible to trigger evaluation without

providing a new valueproviding a new value

● FRP evaluation is continuous and/or FRP evaluation is continuous and/or discretediscrete● liftlift can take Event Stream arguments can take Event Stream arguments● Event Streams trigger evaluation in dependents Event Streams trigger evaluation in dependents

when any value is received, regardless of noveltywhen any value is received, regardless of novelty

Page 30: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

<opinion><opinion>● FRP might be good for modeling I/O flows that FRP might be good for modeling I/O flows that

are for side effects only (e.g. Rx Observables)are for side effects only (e.g. Rx Observables)● FRP not awesome in ClojureScriptFRP not awesome in ClojureScript

● Application state spreads across the graph as Application state spreads across the graph as intermediate, disparate Behaviorsintermediate, disparate Behaviors

● Requires special control structures (Requires special control structures (switchEswitchE))● Implying Event Streams Implying Event Streams of Event Streams of Event Streams of Event Streams …of Event Streams … ● Hard to debug without static type systemHard to debug without static type system

● Overlap between Behavior, Event Stream APIsOverlap between Behavior, Event Stream APIs● No integration point with Clojure state modelNo integration point with Clojure state model

</opinion></opinion>

Page 31: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

JavelinJavelin

Abstract spreadsheet library for reactive Abstract spreadsheet library for reactive programming with values in ClojureScript.programming with values in ClojureScript.

Proudly delivered as a single macro, Proudly delivered as a single macro, cellcell, that , that you write regular ClojureScript inside of.you write regular ClojureScript inside of.

Page 32: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

ClojureScript(defn start [] (let [a (cell 0) b (cell (inc a)) c (cell (+ 123 a b))] (cell (.log js/console c)) (swap! a inc) (js/alert @b)))

input cellformula cellformula cellanon. formula cellmutationdereference

Page 33: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

ClojureScript(let [a (cell 0) b (cell (inc a)) c (cell (+ 123 a b))] (cell (.log js/console c)) (swap! a inc))

a

b

c

(.log js/console c)

(swap! a inc)

Javelin guarantees Javelin guarantees that cell that cell cc sees only sees only consistentconsistent values of values of aa and and bb. This makes . This makes Javelin “glitch-free”Javelin “glitch-free”

Page 34: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Javelin's OpinionsJavelin's Opinions

● At any point in time, a web application is in At any point in time, a web application is in exactly one stateexactly one state● ...and that state is stored as a value in an input cell ...and that state is stored as a value in an input cell

we call the “stem cell”we call the “stem cell”● The stem cell is the root node of the dependency The stem cell is the root node of the dependency

graph representing the application's behaviorgraph representing the application's behavior

● Everything the user sees or can do is governed Everything the user sees or can do is governed by data in the stem cellby data in the stem cell● ...or derived formula cells...or derived formula cells

Page 35: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

““Real” Javelin AppsReal” Javelin Apps

● We use 2 other things to build our applications:We use 2 other things to build our applications:● hlisp: compiles HTML to ClojureScripthlisp: compiles HTML to ClojureScript● wigwam: server, client RPC machinerywigwam: server, client RPC machinery

● Available at Available at https://github.com/tailrecursionhttps://github.com/tailrecursion

Page 36: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

Javelin/hlisp/wigwam architectureJavelin/hlisp/wigwam architecture

wigwamstem cell

cellDOM(hlisp)

clientclient serverserver

● DOM elements bound to cells (hlisp) DOM elements bound to cells (hlisp) ● RPC always return stem cell (wigwam)RPC always return stem cell (wigwam)

Page 37: FRP in ClojureScript with Javelinalan/index.cgi/doc/tip/... ·  FRP might be good for modeling I/O flows that are for side effects only (e.g. Rx Observables) FRP not

freshfreshdietdietTHETHE

Fresh, Gourmet, Delivered DailyFresh, Gourmet, Delivered Daily

Thank you!Thank you!