applying functional reactive programming to write … · applying functional reactive programming...
TRANSCRIPT
APPLYINGFUNCTIONALREACTIVEPROGRAMMINGTOWRITEDYNAMICSERVER
BASEDWEB-APPLICATIONS-
ConnectingSinglePageApplicationstotheBackendwithoutMessandMayhem
-
HackITZ,November2016
Dr.ThomasSchankVersion1.0.0
Motivation1-Cider-CIperiodicallypullpage(!)andreplace
Knownaspjaxpjax=pushState+ajax
"pushState"⇒pulling
pjaxworksbygrabbinghtmlfromyourserverviaajaxandreplacingthecontentofacontaineron
yourpagewiththeajax'dhtml
pjaxpro/conssimple,verylittleJavaScript
extendsserversiderendering
notsuitableforcomplexfrontend
frequentpullingcausesloadandtraffic
timelags
Goals1
maintainstateontheserver,
andpushitfromtheretothebrowserondemand
Motivation2-OO/MVCStyleinPivotal
⇒Reactwonthelpyouifyoumessupmanagingstate
DataBindingsandConnectingComponents
DataBindingsandConnectingComponents
DataBindingsandConnectingComponents
DataBindingsandConnectingComponents
Goal2
ensureconsistencyofstateeverywherebackend,frontend,components,…
Motivation3-Segregation
Thereisanunnaturalandunnecessarydividebetweenback-andfrontend.ThegapinMadekv3isnowbiggerasithaseverbeenbefore.
Thereisanunnaturalandunnecessarydividebetweenback-andfrontend.ThegapinMadekv3isnowbiggerasithaseverbeenbefore.
RailsandotherMVCFrameworks:conventionoverconfiguration
breaksdownwhenthingsgetcomplex
Thereisanunnaturalandunnecessarydividebetweenback-andfrontend.ThegapinMadekv3isnowbiggerasithaseverbeenbefore.
RailsandotherMVCFrameworks:conventionoverconfiguration
breaksdownwhenthingsgetcomplex
"Backend":LotsofRails+ARhackstowritecomplexSQLqueries.
Middle-Layer:lotsofcode(presentersetc.)ifyouwantrealseparation(asinMVC)
Frontend:(C)JSXsomeReactgoodness
XML/HTMLhardtoread
Templates
React = require('react')ReactDOM = require('react-dom')RailsForm = require('../lib/forms/rails-form.cjsx')
module.exports = React.createClass displayName: 'HeaderButton'
_onClick: (event) -> event.preventDefault() if @props.onAction @props.onAction(@props.asyncAction) return false
render: ({authToken, href, method, icon, title, name} = @props) -> method = 'post' if not method icon = 'icon-' + icon onClick = if @props.onAction and @props.asyncAction then @_onClick else null <RailsForm className='button_to' name='' method={method} action={href} authToken={authToken}> <button className="button" type="submit" title={title} onClick={onClick}> <i className={icon}></i> </button> {@props.children} </RailsForm>
Frontend:(C)JSX
TheDOM"isatree"!Encodeitessuchandyougetfullcode-andcomposability.
someReactgoodness
XML/HTMLhardtoread
Templates
React = require('react')ReactDOM = require('react-dom')RailsForm = require('../lib/forms/rails-form.cjsx')
module.exports = React.createClass displayName: 'HeaderButton'
_onClick: (event) -> event.preventDefault() if @props.onAction @props.onAction(@props.asyncAction) return false
render: ({authToken, href, method, icon, title, name} = @props) -> method = 'post' if not method icon = 'icon-' + icon onClick = if @props.onAction and @props.asyncAction then @_onClick else null <RailsForm className='button_to' name='' method={method} action={href} authToken={authToken}> <button className="button" type="submit" title={title} onClick={onClick}> <i className={icon}></i> </button> {@props.children} </RailsForm>
Goal3
uniformityclosethegapbetweenbackendandfrontend
CoreProblem-Remedy-Roadmap
TheCoreProblemmanagingstateovertimeandindifferentplaces
ComingUpcompletelydifferentapproach
effective
veryefficient
impossibletocreatebugslikeseen
awesomedeveloperexperience
Roadmap1. basics
2. singlepageapplications
3. SPA+backend
4. consequencestodevelopers&architecture
OnefurtherGoalInterestYOUforthestuff.
Catchattachedtoaparticulartechnology:
Clojure+ClojureScript
FunctionalReactiveProgramming
Elliott,Conal;Hudak,Paul(1997),"FunctionalReactiveAnimation"
Thecombinationoffunctionalprogramming,andreactiveprogramming.
Elliott,Conal;Hudak,Paul(1997),"FunctionalReactiveAnimation"
What?
Thecombinationoffunctionalprogramming,andreactiveprogramming.
Thebasicideabehindreactiveprogrammingisthattherearecertaindatatypesthatrepresentavalue"overtime".Computationsthatinvolvethesechanging-over-timevalueswillthemselveshave
valuesthatchangeovertime.
SpreadsheetProgramming
StoringStatewithAtoms
increaseanddecreaseexample: (def ^:dynamic db {:a 0 :b 0})
(doseq [_ (range 0 1000)] (def db {:a (inc (:a db)) :b (dec (:b db))}))
; (= db {:a 1000 :b -1000})
1234567
increaseanddecreaseexample:
withthreads
ooooops
(def ^:dynamic db {:a 0 :b 0})
(doseq [_ (range 0 1000)] (def db {:a (inc (:a db)) :b (dec (:b db))}))
; (= db {:a 1000 :b -1000})
1234567
(def ^:dynamic db {:a 0 :b 0})
(doseq [_ (range 0 1000)] (future (def db {:a (inc (:a db)) :b (dec (:b db))})))
; .e.g. (= db {:a 598, :b -666})
12345678
increaseanddecreaseexample:
withthreads
ooooopsTheabsenceofthreadsdoesnotsolvethestateproblem,itmakesitevenharder.
(def ^:dynamic db {:a 0 :b 0})
(doseq [_ (range 0 1000)] (def db {:a (inc (:a db)) :b (dec (:b db))}))
; (= db {:a 1000 :b -1000})
1234567
(def ^:dynamic db {:a 0 :b 0})
(doseq [_ (range 0 1000)] (future (def db {:a (inc (:a db)) :b (dec (:b db))})))
; .e.g. (= db {:a 598, :b -666})
12345678
TheAtom (def db (atom {:a 0 :b 0})) (doseq [_ (range 0 1000)] (future (swap! db (fn [current] {:a (inc (:a current)) :b (dec (:b current))}))))
; (= @db {:a 1000, :b -1000})
123456789
10
Atomsprovideawaytomanageshared,synchronous,independentstate.TheintendeduseofatomistoholdoneofClojure’simmutabledata
structures.
TheAtomcont.
Interestingnolockingtransactions,MVCC
Extremelyuseful
supportvalidators⇒schema
watchable
ReactiveProgramming-Atoms-Reagent
AtomsandReactiveProgrammingimagine:atom≅cellofaspreadsheet
(def a1 (atom 5)) (def a2 (atom 7)) (def a3 (reaction (+ @a1 @a2)))
123
AtomsandReactiveProgrammingimagine:atom≅cellofaspreadsheet
a3isanatomvalueofa1ora2change⇒a3willgetupdated[^lazy][^reaction]evaluationcanbeeagerorlazy,dependingontheenvironmentandlibrary
(def a1 (atom 5)) (def a2 (atom 7)) (def a3 (reaction (+ @a1 @a2)))
123
-
"MinimalisticReactforClojureScript"see
"ManagingstateinReagent"
Reagent
SinglePageApplications
Weconsiderawebappwithoutdataexchangetoorfromtheserverfornow.
Single-PageApplications(SPAs)areWebappsthatloadasingleHTMLpageanddynamicallyupdatethatpageastheuserinteractswiththeapp...withoutconstantpagereloads.(ASP.NET)
re-frameisapatternforwritingSPAsinClojureScript,usingReagent.
re-frame
"It'sMVC,Jim,butnotasweknowit".(re-frame)
re-frameisapatternforwritingSPAsinClojureScript,usingReagent.
Honestly,itisnotMVC!
Youwillfindthepatternsusedinre-frameinothertalks,libraries,frameworks.Itisallovertheplace.
Youshouldreadthere-framedocumentation.Itiswittyinallofitsmeanings.
re-frame
"It'sMVC,Jim,butnotasweknowit".(re-frame)
re-framekeyfeaturesOneandonlyonedatabase!
DataflowsfromthedatabasetotheDOM.
Eventscausethedatabasetochange.
EventsnevermanipulatetheDOMdirectly.
noshortcuts!
OneDatabase?Technically,youcanhavemultipledatabases.
Theirsignalgraphmustformanacyclicdirectedgraph.
Makesureyoudon'tshortcut.TheDomisalways"thesink".
Donotcomplicatethingsunnecessarily⇒justuseonedatabase!
OneDatabase?Technically,youcanhavemultipledatabases.
Theirsignalgraphmustformanacyclicdirectedgraph.
Makesureyoudon'tshortcut.TheDomisalways"thesink".
Donotcomplicatethingsunnecessarily⇒justuseonedatabase!Theserulesarelessoutofatechnicalnecessity.Theyhelpustoavoidunnecessarycomplexityandfocusonthefeatures.
DefineaSchemaandValidate! (ns cider-ci.repository.fetch-and-update.db-schema (:require [schema.core :as schema])) (def schema {:last_fetched_at (schema/maybe org.joda.time.DateTime) :last_error (schema/maybe String) :last_error_at (schema/maybe org.joda.time.DateTime) :updated_at org.joda.time.DateTime :state (schema/enum "error" "fetching" "initializing" "ok" "waiting")
:pending? Boolean })
123456789
10
DefineaSchemaandValidate!
e.g.useitlikethis
worksuniformlyinClojureandClojureScript.
(ns cider-ci.repository.fetch-and-update.db-schema (:require [schema.core :as schema])) (def schema {:last_fetched_at (schema/maybe org.joda.time.DateTime) :last_error (schema/maybe String) :last_error_at (schema/maybe org.joda.time.DateTime) :updated_at org.joda.time.DateTime :state (schema/enum "error" "fetching" "initializing" "ok" "waiting")
:pending? Boolean })
123456789
10
(def fetch-and-update-db (atom {:last_fetched_at nil :last_error nil :last_error_at nil :updated_at (time/now) :pending? true :state "initializing" } :validator #(schema/validate schema %)))
12345678
plumatic/schema
SPAsinteractingwiththeServerAtleastapartoftheroutesareSPAs.
Updatedstateontheserverneedstobereflectedontheclient.
⇒extendingthere-frameconcept
Butthisisnot"webscale"
No,butonhowmanycoresdoesyourapplicationrunrightnow?
PushforthechannelServer→ClientprobablyWebSocketspushdiffs!
Wepushthestateinthespiritofreactiveprogramming.
nothingelse,noRPCstyle,nothing!
⇒WebSocketsvia seemtoworkratherwellptaoussanis/sente
RESTfortheChannelClient→ServerWecanusethesameroutesfortheAPIandthefrontend!
willsaveyouatonofwork
enablesacertainkindofarchitecture
WedoNotSyncTwo(n-)waysyncingdataisveryhard/expensivetoimplement.Sowe
justdonotdoit.
Benefits-DeveloperExperience-Architecture
♻UniformProgrammingClojure+ClojureScript
Ourprimarygoalistoimplementfeatures;nottowriteHTML,React,SQL,ActiveRecord,…
Traditionalframeworksfosterseparationbytechnologyandoverheadincommunication.
samecodeontheserverandclient(almost)
itworks,actuallyitisawesome!
e.g.durationparserandvalidatorinCider-CI
UniformStructuresItisalljustdata-treesmostly.
DOM ,
SQL
Lisp'shomoiconicityreallyflieshere!
weavejester/hiccup reagent
kk/honeysql
ClojureScriptProgrammingExperience
sourcemaps
interactiveprogrammingwith
Demo?
ItisreallybetterthanwritingpureJavaScript!
bhauman/lein-figwheel
ArchitectureMostapplicationhaveascrumdriven(i.e.nonatall)architecture.
Onthenextlevelthearchitecturesaremostlytechnologydriven.
Weshouldincludethefeaturesanddemandsinourarchitectures.
FeatureDrivenArchitecture
Cider-CIArchitectureRefactoring
⇒cheaperandeasierchangesandrefactorings
FinalWords
Whyshouldyoucare?JavaScripthastomanygotchasasageneralplatformontheserver.
Ruby(onRails),PHP,Python,…,willneverruninthebrowserforreal.Theyhavealsotoomanyweaknessesontheserver.
Whyshouldyoucare?JavaScripthastomanygotchasasageneralplatformontheserver.
Ruby(onRails),PHP,Python,…,willneverruninthebrowserforreal.Theyhavealsotoomanyweaknessesontheserver.
Imho,wehavetomoveandthefuturewillbeeither:
TheenterprisestackAngular2/3+servertechnologyX,orthedistinguishablealternativeClojure+ClojureScript.
Whatdoyouchoose?
THEENDThankyou!