what is elm and why should i care?
TRANSCRIPT
WHATISELMANDWHY
SHOULDICARE?twitter&github:@tgroshon
http://tgroshon.github.io/elm-pandamonium-pres
GOALS
1. ApplyElm'sgoodideastoourdailywork
2. PromoteElmasadevelopmenttool
AGENDA
1. IntroductiontoElm
2. DetailsofHowitWorks
1. TypeAnalysisAdvantages
2. Signals->ValuesoverTime
3. AbstractingAwaySide-Effects
3. Sum-up
FORMAT
SomepersuasionfollowedbyanImmediateAction(IA)
INTRO:SYNTAXHaskell-likesyntax
StaticallyTyped
Declarative
Compile-to-JavaScript
import Graphics.Element exposing (..)import Window
main : Signal Elementmain = Signal.map resizeablePaint Window.dimensions
resizeablePaint : (Int,Int) -> ElementresizeablePaint (w,h) = fittedImage w h "/imgs/paint.jpg"
-- Types can be ommitted/inferred in most cases but are nice for Doc
INTRO:TOOL-CHAINWritteninHaskell
elm-make # Compiles Elm to JSelm-package # Package Managerelm-repl # A Read-Eval-Print-Loopelm-reactor # Quick-start Watcher/Builderelm # Wrapper around all the tools
OTHERTOOLS
WebpackLoader:
elm-makeJSBindings:
https://github.com/rtfeldman/elm-
webpack-loader
https://github.com/rtfeldman/node-elm-compiler
THETYPEANALYSIS
ADVANTAGETHISJUSTIN:DYNAMICTYPESAREOUT,
STATICTYPESAREIN!
AdvantagesofElm'sTypeSystem
CatchSimpleErrors
BanishNulls
EnforceSemanticVersioning
IA:Flow
BANISHNULLIcallitmybillion-dollarmistake.Itwasthe
inventionofthenullreferencein1965.-
TonyHoare(BuilderofAlgolW)
NonullsinElm,JustMaybes:
type Maybe a = Just a | Nothing
Allowsthecompilertoenforce"null-checks"whenneeded.
ENFORCINGSEMANTIC
VERSIONINGelm-packagewillbumpversionsforyou,automatically
enforcingtheserules:
PATCH-theAPIisthesame,noriskofbreakingcode
MINOR-valueshavebeenadded,existingvaluesare
unchanged
MAJOR-existingvalueshavebeenchangedorremoved
EvenRust-langistalkingaboutaddingsimilarchecks:
https://users.rust-lang.org/t/signature-based-api-
comparison/2377
IA:FLOWflowtype.org
/* @flow */function foo(x) { return x * 10;}foo('Hello, world!'); // Error: This type is incompatible with ...: string
/* @flow */function foo(x: string, y: number): string { return x.length * y;}foo('Hello', 42); // Error: number ... type is incompatible with ... string
Babelwillstripitoutforyou:
http://babeljs.io/docs/plugins/transform-flow-strip-types/
ONEBEEFWITHFLOWInJavaScript,nullimplicitlyconvertstoall
theprimitivetypes;itisalsoavalid
inhabitantofanyobjecttype.
Incontrast,Flowconsidersnulltobea
distinctvaluethatisnotpartofanyother
type.
Let'sbeHonest:InJavaScript,alltypesarenullable!
Requiresprogrammerdisciplinetobehonestaboutwhich
varsarenotMaybe(fewerthanwethink)
Notagreatdecisionforagradually-typedsystemovera
languagewithnullabletypes
RECOMMENDATION
Asahabitinflow,markthingsasMaybe
DISCRETEVALUES
OVERTIMEtype Signal a
Avaluethatchangesovertime.Every
signalisupdatedatdiscretemomentsin
responsetoeventsintheworld.
SolikeanEventEmitter?Almost,andnotquite.
TheReactiveinElmFunctional-ReactiveProgramming
SIGNAL
CHARACTERISTICSSynchronous:FIFO
Infinite&Static:Youcannotdelete,create,orunsubscribe
fromsignalsatruntime.
SIGNALGRAPHYourprogramendsupbeingagraphofsignals.
Thinkofpipes(streams):(a)sources,(b)branches,and(c)
sinks.
SIGNALGRAPH
(CONT'D)AnApplication'ssignalgraphcanhavemultiplesources,
branches,andsinks.
Sources:
position : Signal (Int, Int) -- Mouseclicks : Signal () --
dimensions : Signal (Int, Int) -- Window
Branches(Transforms):
map : (a -> b) -> Signal a -> Signal bfilter : (a -> Bool) -> a -> Signal a -> Signal amerge : Signal a -> Signal a -> Signal afoldp : (a -> s -> s) -> s -> Signal a -> Signal s
Sinks:
main : Signal Element -- element to be rendered to screenport someJavaScriptFn : Signal String -- Send strings to JavaScript
BUTWHYSIGNALS?1. Naturallyhandlesasynchrony
2. Naturallymodeldataovertimewhileavoidingshared
mutablestate
IA:FRPSTREAMSINJSContenders:
RxJS(Biggest)
Bacon.js(Slowest)
Kefir.js(Middle)
Most.js(Fastest)
Flyd(Smallest)
I'lldiscussRxJS,Most,andFlyd
IA:RXJSRxJSimplementsObservables
/* Get stock data somehow */var source = getAsyncStockData() // Source is an observable
source .filter(function (quote) { // Branches return quote.price > 30 }) .map(function (quote) { return quote.price }) .subscribe(function (price) { // Sink console.log('Prices higher ' + 'than $30: $' + price) })
Mostpopularbutalsothelargestfilesize(around200kb
minified)
CanchooseasynchronybetweensetImmediate,
setTimeout,requestAnimationFrame
IA:MOST.JSMonadicStreams
most.from([1, 2, 3, 4]) .delay(1000) .filter(function(i) { return i % 2 === 0 }) .reduce(function(result, y) { return result + y }, 0) .then(function(result) { console.log(result) })
Superfastandaround50kbminified,uncompressed
Consumingfunctions(reduce,forEach,observe)
returnPromises.
IA:FLYDPronouncedflu(it'sDutch)
var x = flyd.stream(10) // Create stream with initial valuevar y = flyd.stream(20)
var sum = flyd.combine(function(x, y) { return x() + y() // Calling stream without argument returns last value}, [x, y])
flyd.on(function(s) { console.log(s)}, sum)
VerySimple.ModeledafterElmSignals.
Mostlyjustgiveslow-levelcompositionconstructse.g.
combine,merge,transduce
TASKStype Task x a
Representsasynchronouseffectsthat
returnvalueoftypeabutmayfailwith
valueoftypex.
SoundlikePromises?Theyareevenchainablewith
andThen:
logCurrentTime : Task x ()logCurrentTime = getCurrentTimeTask `andThen` consoleLogTask
EFFECTStype Effects a
TheEffectstypeisessentiallyadata
structureholdingabunchofindependent
tasksthatwillgetrunatsomelaterpoint.
ExamplemakinganHTTPrequestforjsonData:
getData : String -> Effects ActiongetData url = Http.get decodeJsonData url |> Task.toMaybe |> Task.map NewGif |> Effects.task
EXPLAINEDtype Action = NewGif (Maybe String) | ... -- some other action
getData : String -> Effects ActiongetData url = Http.get decodeJsonData url |> Task.toMaybe |> Task.map NewGif |> Effects.task
Task.toMaybedropstheerrormessageandreturnsa
Maybe Stringtype.
Task.map NewGifwrapstheMaybe Stringina
NewGifaction.
Effects.taskturnthistaskintoanEffectstype.
TASK,MAYBE,EFFECTS
OHMY!EffectswrapTasksthatareguaranteedto"notfail"...
meaningtheyhaveanerrorhandler.
Task.toMaybeandTask.toResultareconvenience
functionstomoveerrorsintothesuccesshandlerby
returninganewtask.
toMaybe : Task x a -> Task y (Maybe a)toMaybe task = map Just task `onError` (\_ -> succeed Nothing)
toResult : Task x a -> Task y (Result x a)toResult task = map Ok task `onError` (\msg -> succeed (Err msg))
WHYAREEFFECTS
USEFUL?Mytheory:tomakethestart-apppackagebetter!
Ifyoucancollectthetasksforallyourcomponentsthatwill
berendered,youcanruntheminabatchbutonlyifthey
neverfail.
IA:PROMISESvar p = new Promise(function(resolve, reject) { // something async happens});
p.then(successHandlerFn, failureHandlerFn);
WhatcanwelearnfromElm?
Don'trejectpromisesneedlessly
var p = new Promise(function (resolve, reject) { doAsync(function (err, result) { if (err) { return resolve({status: 'failed', data: err}) } resolve({status: 'success', data: result}) })})