Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

What is Elm and Why Should I care?

3.825 visualizaciones

Publicado el

Dive into some of the clever ideas of the Elm programming language and how they can be applied to current JavaScript development.

Presented at Pandamonium 2016, an Instructure Engineering Conference.

Alternate format:

Publicado en: Tecnología
  • Sé el primero en comentar

What is Elm and Why Should I care?

  1. 1. WHAT IS ELM AND WHY SHOULD I CARE? twitter & github: @tgroshon
  3. 3. GOALS 1. Apply Elm's good ideas to our daily work 2. Promote Elm as a development tool AGENDA 1. Introduction to Elm 2. Details of How it Works 1. Type Analysis Advantages 2. Signals -> Values over Time 3. Abstracting Away Side-Effects 3. Sum-up FORMAT Some persuasion followed by an Immediate Action (IA)
  4. 4. INTRO: SYNTAX Haskell-like syntax Statically Typed Declarative Compile-to-JavaScript import Graphics.Element exposing (..) import Window main : Signal Element main = resizeablePaint Window.dimensions resizeablePaint : (Int,Int) -> Element resizeablePaint (w,h) = fittedImage w h "/imgs/paint.jpg" -- Types can be ommitted/inferred in most cases but are nice for Doc
  5. 5. INTRO: TOOL-CHAIN Written in Haskell elm-make # Compiles Elm to JS elm-package # Package Manager elm-repl # A Read-Eval-Print-Loop elm-reactor # Quick-start Watcher/Builder elm # Wrapper around all the tools OTHER TOOLS Webpack Loader: elm-make JS Bindings: webpack-loader
  6. 6. THE TYPE ANALYSIS ADVANTAGE THIS JUST IN: DYNAMIC TYPES ARE OUT, STATIC TYPES ARE IN! Advantages of Elm's Type System Catch Simple Errors Banish Nulls Enforce Semantic Versioning IA: Flow
  7. 7. THE SIMPLE ERRORS Goodbye spelling mistakes and refactoring lapses:
  8. 8. BANISH NULL I call it my billion-dollar mistake. It was the invention of the null reference in 1965. - Tony Hoare (Builder of Algol W) No nulls in Elm, Just Maybes: type Maybe a = Just a | Nothing Allows the compiler to enforce "null-checks" when needed.
  9. 9. ENFORCING SEMANTIC VERSIONING elm-package will bump versions for you, automatically enforcing these rules: PATCH - the API is the same, no risk of breaking code MINOR - values have been added, existing values are unchanged MAJOR - existing values have been changed or removed Even Rust-lang is talking about adding similar checks: comparison/2377
  10. 10. IA: FLOW /* @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 Babel will strip it out for you:
  11. 11. ONE BEEF WITH FLOW In JavaScript, null implicitly converts to all the primitive types; it is also a valid inhabitant of any object type. In contrast, Flow considers null to be a distinct value that is not part of any other type. Let's be Honest: In JavaScript, all types are nullable! Requires programmer discipline to be honest about which vars are not Maybe (fewer than we think) Not a great decision for a gradually-typed system over a language with nullable types RECOMMENDATION As a habit in flow, mark things as Maybe
  12. 12. SIGNALS -> VALUES OVER TIME Discrete values over time Signal Graphs IA: RxJS, Most.js, Flyd
  13. 13. DISCRETE VALUES OVER TIME type Signal a A value that changes over time. Every signal is updated at discrete moments in response to events in the world. So like an EventEmitter? Almost, and not quite. The Reactive in Elm Functional-Reactive Programming
  14. 14. SIGNAL CHARACTERISTICS Synchronous: FIFO Infinite & Static: You cannot delete, create, or unsubscribe from signals at runtime.
  15. 15. SIGNAL GRAPH Your program ends up being a graph of signals. Think of pipes (streams): (a) sources, (b) branches, and (c) sinks.
  16. 16. SIGNAL GRAPH (CONT'D) An Application's signal graph can have multiple sources, branches, and sinks. Sources: position : Signal (Int, Int) -- Mouse clicks : Signal () -- dimensions : Signal (Int, Int) -- Window Branches (Transforms): map : (a -> b) -> Signal a -> Signal b filter : (a -> Bool) -> a -> Signal a -> Signal a merge : Signal a -> Signal a -> Signal a foldp : (a -> s -> s) -> s -> Signal a -> Signal s Sinks: main : Signal Element -- element to be rendered to screen port someJavaScriptFn : Signal String -- Send strings to JavaScript
  17. 17. BUT WHY SIGNALS? 1. Naturally handles asynchrony 2. Naturally model data over time while avoiding shared mutable state
  18. 18. IA: FRP STREAMS IN JS Contenders: RxJS (Biggest) Bacon.js (Slowest) Kefir.js (Middle) Most.js (Fastest) Flyd (Smallest) I'll discuss RxJS, Most, and Flyd
  19. 19. IA: RXJS RxJS implements Observables /* 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) }) Most popular but also the largest file size (around 200kb minified) Can choose asynchrony between setImmediate, setTimeout, requestAnimationFrame
  20. 20. IA: MOST.JS Monadic Streams 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) }) Super fast and around 50kb minified, uncompressed Consuming functions (reduce, forEach, observe) return Promises.
  21. 21. IA: FLYD Pronounced flu (it's Dutch) var x = // Create stream with initial value var y = 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) Very Simple. Modeled after Elm Signals. Mostly just gives low-level composition constructs e.g. combine, merge, transduce
  22. 22. ABSTRACTING AWAY SIDE-EFFECTS Tasks Effects IA: Promises
  23. 23. TASKS type Task x a Represents asynchronous effects that return value of type a but may fail with value of type x. Sound like Promises? They are even chainable with andThen: logCurrentTime : Task x () logCurrentTime = getCurrentTimeTask `andThen` consoleLogTask
  24. 24. EFFECTS type Effects a The Effects type is essentially a data structure holding a bunch of independent tasks that will get run at some later point. Example making an HTTP request for jsonData: getData : String -> Effects Action getData url = Http.get decodeJsonData url |> Task.toMaybe |> NewGif |> Effects.task
  25. 25. EXPLAINED type Action = NewGif (Maybe String) | ... -- some other action getData : String -> Effects Action getData url = Http.get decodeJsonData url |> Task.toMaybe |> NewGif |> Effects.task Task.toMaybe drops the error message and returns a Maybe String type. NewGif wraps the Maybe String in a NewGif action. Effects.task turn this task into an Effects type.
  26. 26. TASK, MAYBE, EFFECTS OH MY! Effects wrap Tasks that are guaranteed to "not fail" ... meaning they have an error handler. Task.toMaybe and Task.toResult are convenience functions to move errors into the success handler by returning a new task. 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))
  27. 27. WHY ARE EFFECTS USEFUL? Thoughts?
  28. 28. WHY ARE EFFECTS USEFUL? My theory: to make the start-app package better! If you can collect the tasks for all your components that will be rendered, you can run them in a batch but only if they never fail.
  29. 29. IA: PROMISES var p = new Promise(function(resolve, reject) { // something async happens }); p.then(successHandlerFn, failureHandlerFn); What can we learn from Elm? Don't reject promises needlessly var p = new Promise(function (resolve, reject) { doAsync(function (err, result) { if (err) { return resolve({status: 'failed', data: err}) } resolve({status: 'success', data: result}) }) })
  30. 30. SUM-UP Try out Elm Or: Use flow for static type analysis Use a JavaScript FRP Library Use Promises that "never fail" More realistically, use some combination of the above ;)