elm: give it a try
Eugene Zharkov, JUNO
the best of functional programming
in your browser
So Elm…
readItem :: String -> Maybe Decl
readItem x | ParseOk y <- myParseDecl x = Just $ unGADT y
readItem x -- newtype
| Just x <- stripPrefix "newtype " x
, ParseOk (DataDecl a _ c d e f g) <- fmap unGADT $ myParseDecl $ "data " ++ x
= Just $ DataDecl a NewType c d e f g
readItem x -- constructors
| ParseOk (GDataDecl _ _ _ _ _ _ [GadtDecl s name _ ty] _) <- myParseDecl $ "data
Data where " ++ x
, let f (TyBang _ (TyParen x@TyApp{})) = x
f (TyBang _ x) = x
f x = x
parserC warning = f [] ""
where
f com url = do
x <- await
whenJust x $ (i,s) -> case () of
_ | Just s <- strStripPrefix "-- | " s -> f [s] url
| Just s <- strStripPrefix "--" s -> f (if null com then [] else
strTrimStart s : com) url
| Just s <- strStripPrefix "@url " s -> f com (strUnpack s)
| strNull $ strTrimStart s -> f [] ""
| otherwise -> do
case parseLine $ fixLine $ strUnpack s of
Left y -> lift $ warning $ show i ++ ":" ++ y
-- only check Nothing as some items (e.g. "instance () :>
Foo a")
-- don't roundtrip but do come out equivalent
It was Haskell
Real Elm
results : Signal.Mailbox (Result String (List String))
results =
Signal.mailbox (Err "A valid US zip code is 5 numbers.")
port requests : Signal (Task x ())
port requests =
Signal.map lookupZipCode query.signal
|> Signal.map (task -> Task.toResult task `andThen` Signal.send results.address)
lookupZipCode : String -> Task String (List String)
lookupZipCode query =
let toUrl =
if String.length query == 5 && String.all Char.isDigit query
then succeed ("http://api.zippopotam.us/us/" ++ query)
else fail "Give me a valid US zip code!"
in
toUrl `andThen` (mapError (always "Not found :(") << Http.get places)
cene : (Int,Int) -> (Int,Int) -> Element
scene (x,y) (w,h) =
let
(dx,dy) =
(toFloat x - toFloat w / 2, toFloat h / 2 - toFloat y)
in
collage w h
[ ngon 3 100
|> filled blue
|> rotate (atan2 dy dx)
, ngon 6 30
|> filled orange
|> move (dx, dy)
Installation
npm install -g elm
elm-compiler
compiler, yep
elm-make
build tools
compile to JS or HTML
build dependencies
elm-reactor
dev tools
hot swap
time travel debugging
elm-repl
ets you interact with values and functions directly.
elm-package
package manager
JS > Elm | diff
string "functional" "functional"
multiline non exists """functional programming"""
string
char non exists 'f'
bool true/false True/False
> Literals
{ width: 300, height: 400} { width = 300, height = 400}
worker.id = 12 { worker | id = 12 }
> Objects aka Records
function(x,y) { return x + y; } x y -> x + y
Math.max(3, 4) max 3 4
Math.min(1, Math.pow(2, 4)) min 1 (2^4)
numbers.map(Math.sqrt) List.map sqrt numbers
points.map(function(p) { return p.x }) List.map .x points
> Functions
JS > Elm | diff
'NCC-' + '1701' "NCC-" ++ "1701"
'NCC-'.length String.length "NCC-"
'NCC-'.toLowerCase() String.toLower "NCC-"
'NCC-' + 1701 "NCC-" ++ toString 1701
> Work with strings
2 > 1 ? 'Lee' : 'Chan' if 3 > 2 then "Lee" else "Chan"
var x = 42; let x = 42
return 42 Everything is an expression, no need for return
> Control Flow
Core
> 9 / 2
4.5 : Float
> 9 // 2
4 : Int
>
> Values
> isNegative n = n < 0
<function>
> isNegative 4
False
> isNegative -7
True
> isNegative (-3 * -4)
False
> Functions
Data Structures
> Lists
> names = [ "Alice", "Bob", "Chuck" ]
["Alice","Bob","Chuck"]
> List.isEmpty names
False
> List.length names
3
> List.reverse names
["Chuck","Bob","Alice"]
> numbers = [1,4,3,2]
[1,4,3,2]
> List.sort numbers
[1,2,3,4]
> double n = n * 2
<function>
> List.map double numbers
[2,8,6,4]
List != Object

elm != OOP
List = Module
List module
module List
( isEmpty, length, reverse, member
, head, tail, filter, take, drop
, repeat, (::), append, concat, intersperse
, partition, unzip
, map, map2, map3, map4, map5
, filterMap, concatMap, indexedMap
, ……………
{-| Determine if a list is empty.
isEmpty [] == True
-}
isEmpty : List a -> Bool
isEmpty xs =
case xs of
[] ->
True
_ ->
False
{-| Keep only elements that satisfy the
predicate.
filter isEven [1..6] == [2,4,6]
-}
filter : (a -> Bool) -> List a -> List a
filter pred xs =
let
conditionalCons x xs' =
if pred x then
x :: xs'
else
xs'
in
foldr conditionalCons [] xs
Data Structures
> Tuples
> import String
> goodName name = 
| if String.length name <= 20 then 
| (True, "name accepted!") 
| else 
| (False, "name was too long; please limit it to 20
characters")
> goodName "Tom"
(True, "name accepted!")
Data Structures
> Records
> point = { x = 3, y = 4 }
{ x = 3, y = 4 }
> point.x
3
> bill = { name = "Gates", age = 57 }
{ age = 57, name = "Gates" }
> bill.name
“Gates"
> .name bill
"Gates"
> List.map .name [bill,bill,bill]
["Gates","Gates","Gates"]
Data Structures
> Records
> under70 {age} = age < 70
<function>
> under70 bill
True
> under70 { species = "Triceratops", age = 68000000 }
False
> { bill | name = "Nye" }
{ age = 57, name = "Nye" }
> { bill | age = 22 }
{ age = 22, name = "Gates" }
JS Object vs Elm Record
- You cannot ask for a field that does not exist.
- No field will ever be undefined or null.
- You cannot create recursive records with a this or self keyword.
Contracts
import String
fortyTwo : Int
fortyTwo = 42
drinks : List String
drinks = [ "Hennessy", "JimBeam", "Jack Daniels" ]
book : { title: String, author: String, pages: Int }
book =
{ title = "Robert", author = "Martin", pages = 434 }
longestName : List String -> Int
longestName drinks =
List.maximum (List.map String.length drinks)
isLong : { record | pages : Int } -> Bool
isLong book =
book.pages > 400
All of these types can be inferred, so you
can leave off the type annotations and Elm
can still check that data is flowing around in
a way that works. This means you can just
not write these contracts and still get all the
benefits!
Enumerations
type Visibility = All | Active | Completed
toString : Visibility -> String
toString visibility =
case visibility of
All ->
"All"
Active ->
"Active"
Completed ->
"Completed"
-- toString All == "All"
-- toString Active == "Active"
-- toString Completed == "Completed"
State Machines
type User = Anonymous | LoggedIn String
userPhoto : User -> String
userPhoto user =
case user of
Anonymous ->
"anon.png"
LoggedIn name ->
"users/" ++ name ++ "/photo.png"
Architecture
Model
View
Action
MUV
-- MODEL
type alias Model = { ... }
-- UPDATE
type Action = Reset | ...
update : Action -> Model -> Model
update action model =
case action of
Reset -> ...
...
-- VIEW
view : Model -> Html
view =
...
Example
type alias Model =
{ topic : String
, gifUrl : String
}
init : String -> (Model, Effects Action)
init topic =
( Model topic "assets/waiting.gif"
, ge
update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
RequestMore ->
(model, getRandomGif model.topic)
NewGif maybeUrl ->
( Model model.topic (Maybe.withDefault model.gifUrl maybeUrl)
, Effects.none
)
Model
Update
Example
getRandomGif : String -> Effects Action
getRandomGif topic =
Http.get decodeUrl (randomUrl topic)
|> Task.toMaybe
|> Task.map NewGif
|> Effects.task
randomUrl : String -> String
randomUrl topic =
Http.url "http://api.giphy.com/v1/gifs/random"
[ "api_key" => "dc6zaTOxFJmzC"
, "tag" => topic
]
decodeUrl : Json.Decoder String
decodeUrl =
Json.at ["data", "image_url"] Json.string
Effects
Functional stuff, I got it
App flow
Signals
Signals
module Signal
( Signal
, merge, mergeMany
, map, map2, map3, map4, map5
, constant
, dropRepeats, filter, filterMap, sampleOn
, foldp
, Mailbox, Address, Message
, mailbox, send, message, forwardTo
) where
Tasks
elm-http — talk to servers
elm-history — navigate browser history
elm-storage — save info in the users browser
elm-storage
import Native.Storage
getItemAsJson : String -> Task String Value
getItemAsJson = Native.Storage.getItemAsJson
-- Do better error detection
getItem : String -> Decoder value -> Task String value
getItem key decoder =
let decode value = case decodeValue decoder value of
Ok v -> succeed v
Err err -> fail "Failed"
in
getItemAsJson key `andThen` decode
Native.Storage aka Storage.js
/*!
localForage -- Offline Storage, Improved
Version 1.2.2
https://mozilla.github.io/localForage
(c) 2013-2015 Mozilla, Apache License 2.0
*/
(function() {
var define, requireModule, require, requirejs;
(function() {
var registry = {}, seen = {};
define = function(name, deps, callback) {
registry[name] = { deps: deps, callback: callback };
};
requirejs = require = requireModule = function(name) {
requirejs._eak_seen = registry;
if (seen[name]) { return seen[name]; }
seen[name] = {};
Mailbox
type alias Mailbox a =
{ signal : Signal a
, address : Address a
}
send : Address a -> a -> Task x ()
import Graphics.Element exposing (Element, show)
import Task exposing (Task, andThen)
import TaskTutorial exposing (getCurrentTime, print)
main : Signal Element
main =
Signal.map show contentMailbox.signal
contentMailbox : Signal.Mailbox String
contentMailbox =
Signal.mailbox ""
port updateContent : Task x ()
port updateContent =
Signal.send contentMailbox.address "hello!"
Init with empty value
send a message
signature
Ports - Communication between Elm and
pure JS
Ports | JS > Elm
port addUser : Signal (String, UserRecord)
> Elm
> JS
myapp.ports.addUser.send([
"Tom",
{ age: 32, job: "lumberjack" }
]);
myapp.ports.addUser.send([
"Sue",
{ age: 37, job: "accountant" }
]);
Ports | Elm > JS
port requestUser : Signal String
port requestUser =
signalOfUsersWeWantMoreInfoOn
> Elm
> JS
myapp.ports.requestUser.subscribe(databaseLookup);
function databaseLookup(user) {
var userInfo = database.lookup(user);
myapp.ports.addUser.send(user, userInfo);
}
Elm: give it a try
HTML generation
main : Signal Element
main =
Signal.map2 renderStamps Window.dimensions clickLocations
clickLocations : Signal (List (Int,Int))
clickLocations =
Signal.foldp (::) [] (Signal.sampleOn Mouse.clicks Mouse.position)
renderStamps : (Int,Int) -> List (Int,Int) -> Element
renderStamps (w,h) locs =
let pentagon (x,y) =
ngon 5 20
|> filled (hsla (toFloat x) 0.9 0.6 0.7)
|> move (toFloat x - toFloat w / 2, toFloat h / 2 - toFloat y)
|> rotate (toFloat x)
in
layers
[ collage w h (List.map pentagon locs)
, show "Click to stamp a pentagon."
]
elm-make Stamper.elm --output=Main.html
Nice architecture
Good documentation
Source code with comments
Great developer toolset
Own packages / manager
Overview from JS perspective
Elm: give it a try
Complex syntax
Learning curve
Team integration cost?
Overview from JS perspective
That's all folks
eu.zharkov@gmail.com
@2j2e

More Related Content

PDF
Cycle.js: Functional and Reactive
PPTX
Fact, Fiction, and FP
PPTX
PPTX
Ricky Bobby's World
PPTX
Super Advanced Python –act1
DOCX
Opp compile
PDF
Building Real Time Systems on MongoDB Using the Oplog at Stripe
PPTX
Academy PRO: ES2015
Cycle.js: Functional and Reactive
Fact, Fiction, and FP
Ricky Bobby's World
Super Advanced Python –act1
Opp compile
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Academy PRO: ES2015

What's hot (19)

PDF
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
PDF
Composition in JavaScript
PDF
Mongoskin - Guilin
PDF
스위프트를 여행하는 히치하이커를 위한 스타일 안내
PDF
Simulator customizing & testing for Xcode 9
PDF
Rust ⇋ JavaScript
PDF
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
PDF
Building Real Time Systems on MongoDB Using the Oplog at Stripe
PDF
Building Real Time Systems on MongoDB Using the Oplog at Stripe
PDF
Functional programming using underscorejs
PDF
Javascript
PPTX
Everyday's JS
PDF
JavaScript ES6
PPTX
Introduzione a C#
PDF
Minimizing Decision Fatigue to Improve Team Productivity
PPTX
JavaScript Objects and OOP Programming with JavaScript
ODP
ES6 PPT FOR 2016
PDF
Writing Clean Code in Swift
PDF
RxSwift 시작하기
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
Composition in JavaScript
Mongoskin - Guilin
스위프트를 여행하는 히치하이커를 위한 스타일 안내
Simulator customizing & testing for Xcode 9
Rust ⇋ JavaScript
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Functional programming using underscorejs
Javascript
Everyday's JS
JavaScript ES6
Introduzione a C#
Minimizing Decision Fatigue to Improve Team Productivity
JavaScript Objects and OOP Programming with JavaScript
ES6 PPT FOR 2016
Writing Clean Code in Swift
RxSwift 시작하기
Ad

Viewers also liked (6)

PPTX
Emotional manifestation in children 2
PPTX
Elm: Make Yourself A Happy Front-end Web Developer
PPTX
E言語スタック
Emotional manifestation in children 2
Elm: Make Yourself A Happy Front-end Web Developer
E言語スタック
Ad

Similar to Elm: give it a try (20)

PDF
Damn Fine CoffeeScript
PDF
Refactoring to Macros with Clojure
PDF
PureScript & Pux
PDF
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
PDF
Functional Programming with Groovy
KEY
Desarrollando aplicaciones web en minutos
PDF
Swift 함수 커링 사용하기
PDF
Ruby Language - A quick tour
PDF
Introduction to Scala
PDF
7 Habits For a More Functional Swift
PDF
Pooya Khaloo Presentation on IWMC 2015
PDF
A swift introduction to Swift
PDF
How te bring common UI patterns to ADF
KEY
Introduction to Groovy
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
An introduction to property-based testing
KEY
関数潮流(Function Tendency)
Damn Fine CoffeeScript
Refactoring to Macros with Clojure
PureScript & Pux
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Functional Programming with Groovy
Desarrollando aplicaciones web en minutos
Swift 함수 커링 사용하기
Ruby Language - A quick tour
Introduction to Scala
7 Habits For a More Functional Swift
Pooya Khaloo Presentation on IWMC 2015
A swift introduction to Swift
How te bring common UI patterns to ADF
Introduction to Groovy
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
An introduction to property-based testing
関数潮流(Function Tendency)

More from Eugene Zharkov (20)

PDF
Monorepo: React + React Native. React Alicante
PDF
Monorepo: React Web & React Native
PDF
Create React Native App vs Expo vs Manually
PDF
Build automation with Fastlane
PDF
GraphQL and/or REST
PDF
React Native Animation
PDF
React Native: Hurdle Race
PDF
Burn your grass with react native
PDF
Фронтенд сказки
PDF
How to be a good frontend developer
PDF
Что там в summary
PDF
Switch to React.js from AngularJS developer
PDF
AngularJS: Good parts
PDF
Mobile applications in a new way with React Native
PDF
Angular 2: Всех переиграл
PDF
Angular 2.0: Brighter future?
PDF
Как объяснить на платьях процесс разработки?
PDF
Angular.JS: Do it right
PDF
SignalR: Add real-time to your applications
PDF
Roslyn compiler as a service
Monorepo: React + React Native. React Alicante
Monorepo: React Web & React Native
Create React Native App vs Expo vs Manually
Build automation with Fastlane
GraphQL and/or REST
React Native Animation
React Native: Hurdle Race
Burn your grass with react native
Фронтенд сказки
How to be a good frontend developer
Что там в summary
Switch to React.js from AngularJS developer
AngularJS: Good parts
Mobile applications in a new way with React Native
Angular 2: Всех переиграл
Angular 2.0: Brighter future?
Как объяснить на платьях процесс разработки?
Angular.JS: Do it right
SignalR: Add real-time to your applications
Roslyn compiler as a service

Recently uploaded (20)

PDF
Developing a website for English-speaking practice to English as a foreign la...
PDF
Hindi spoken digit analysis for native and non-native speakers
PDF
Unlock new opportunities with location data.pdf
PPTX
The various Industrial Revolutions .pptx
PDF
DP Operators-handbook-extract for the Mautical Institute
PDF
Assigned Numbers - 2025 - Bluetooth® Document
PPTX
Tartificialntelligence_presentation.pptx
PDF
Zenith AI: Advanced Artificial Intelligence
PPTX
Group 1 Presentation -Planning and Decision Making .pptx
PDF
TrustArc Webinar - Click, Consent, Trust: Winning the Privacy Game
PPTX
MicrosoftCybserSecurityReferenceArchitecture-April-2025.pptx
PPT
Module 1.ppt Iot fundamentals and Architecture
PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PDF
Enhancing emotion recognition model for a student engagement use case through...
PDF
A comparative study of natural language inference in Swahili using monolingua...
PPTX
Final SEM Unit 1 for mit wpu at pune .pptx
PDF
sustainability-14-14877-v2.pddhzftheheeeee
PDF
Hybrid horned lizard optimization algorithm-aquila optimizer for DC motor
PDF
DASA ADMISSION 2024_FirstRound_FirstRank_LastRank.pdf
PDF
ENT215_Completing-a-large-scale-migration-and-modernization-with-AWS.pdf
Developing a website for English-speaking practice to English as a foreign la...
Hindi spoken digit analysis for native and non-native speakers
Unlock new opportunities with location data.pdf
The various Industrial Revolutions .pptx
DP Operators-handbook-extract for the Mautical Institute
Assigned Numbers - 2025 - Bluetooth® Document
Tartificialntelligence_presentation.pptx
Zenith AI: Advanced Artificial Intelligence
Group 1 Presentation -Planning and Decision Making .pptx
TrustArc Webinar - Click, Consent, Trust: Winning the Privacy Game
MicrosoftCybserSecurityReferenceArchitecture-April-2025.pptx
Module 1.ppt Iot fundamentals and Architecture
Univ-Connecticut-ChatGPT-Presentaion.pdf
Enhancing emotion recognition model for a student engagement use case through...
A comparative study of natural language inference in Swahili using monolingua...
Final SEM Unit 1 for mit wpu at pune .pptx
sustainability-14-14877-v2.pddhzftheheeeee
Hybrid horned lizard optimization algorithm-aquila optimizer for DC motor
DASA ADMISSION 2024_FirstRound_FirstRank_LastRank.pdf
ENT215_Completing-a-large-scale-migration-and-modernization-with-AWS.pdf

Elm: give it a try

  • 1. elm: give it a try Eugene Zharkov, JUNO
  • 2. the best of functional programming in your browser
  • 3. So Elm… readItem :: String -> Maybe Decl readItem x | ParseOk y <- myParseDecl x = Just $ unGADT y readItem x -- newtype | Just x <- stripPrefix "newtype " x , ParseOk (DataDecl a _ c d e f g) <- fmap unGADT $ myParseDecl $ "data " ++ x = Just $ DataDecl a NewType c d e f g readItem x -- constructors | ParseOk (GDataDecl _ _ _ _ _ _ [GadtDecl s name _ ty] _) <- myParseDecl $ "data Data where " ++ x , let f (TyBang _ (TyParen x@TyApp{})) = x f (TyBang _ x) = x f x = x parserC warning = f [] "" where f com url = do x <- await whenJust x $ (i,s) -> case () of _ | Just s <- strStripPrefix "-- | " s -> f [s] url | Just s <- strStripPrefix "--" s -> f (if null com then [] else strTrimStart s : com) url | Just s <- strStripPrefix "@url " s -> f com (strUnpack s) | strNull $ strTrimStart s -> f [] "" | otherwise -> do case parseLine $ fixLine $ strUnpack s of Left y -> lift $ warning $ show i ++ ":" ++ y -- only check Nothing as some items (e.g. "instance () :> Foo a") -- don't roundtrip but do come out equivalent
  • 5. Real Elm results : Signal.Mailbox (Result String (List String)) results = Signal.mailbox (Err "A valid US zip code is 5 numbers.") port requests : Signal (Task x ()) port requests = Signal.map lookupZipCode query.signal |> Signal.map (task -> Task.toResult task `andThen` Signal.send results.address) lookupZipCode : String -> Task String (List String) lookupZipCode query = let toUrl = if String.length query == 5 && String.all Char.isDigit query then succeed ("http://api.zippopotam.us/us/" ++ query) else fail "Give me a valid US zip code!" in toUrl `andThen` (mapError (always "Not found :(") << Http.get places) cene : (Int,Int) -> (Int,Int) -> Element scene (x,y) (w,h) = let (dx,dy) = (toFloat x - toFloat w / 2, toFloat h / 2 - toFloat y) in collage w h [ ngon 3 100 |> filled blue |> rotate (atan2 dy dx) , ngon 6 30 |> filled orange |> move (dx, dy)
  • 6. Installation npm install -g elm elm-compiler compiler, yep elm-make build tools compile to JS or HTML build dependencies elm-reactor dev tools hot swap time travel debugging elm-repl ets you interact with values and functions directly. elm-package package manager
  • 7. JS > Elm | diff string "functional" "functional" multiline non exists """functional programming""" string char non exists 'f' bool true/false True/False > Literals { width: 300, height: 400} { width = 300, height = 400} worker.id = 12 { worker | id = 12 } > Objects aka Records function(x,y) { return x + y; } x y -> x + y Math.max(3, 4) max 3 4 Math.min(1, Math.pow(2, 4)) min 1 (2^4) numbers.map(Math.sqrt) List.map sqrt numbers points.map(function(p) { return p.x }) List.map .x points > Functions
  • 8. JS > Elm | diff 'NCC-' + '1701' "NCC-" ++ "1701" 'NCC-'.length String.length "NCC-" 'NCC-'.toLowerCase() String.toLower "NCC-" 'NCC-' + 1701 "NCC-" ++ toString 1701 > Work with strings 2 > 1 ? 'Lee' : 'Chan' if 3 > 2 then "Lee" else "Chan" var x = 42; let x = 42 return 42 Everything is an expression, no need for return > Control Flow
  • 9. Core > 9 / 2 4.5 : Float > 9 // 2 4 : Int > > Values > isNegative n = n < 0 <function> > isNegative 4 False > isNegative -7 True > isNegative (-3 * -4) False > Functions
  • 10. Data Structures > Lists > names = [ "Alice", "Bob", "Chuck" ] ["Alice","Bob","Chuck"] > List.isEmpty names False > List.length names 3 > List.reverse names ["Chuck","Bob","Alice"] > numbers = [1,4,3,2] [1,4,3,2] > List.sort numbers [1,2,3,4] > double n = n * 2 <function> > List.map double numbers [2,8,6,4]
  • 11. List != Object
 elm != OOP List = Module
  • 12. List module module List ( isEmpty, length, reverse, member , head, tail, filter, take, drop , repeat, (::), append, concat, intersperse , partition, unzip , map, map2, map3, map4, map5 , filterMap, concatMap, indexedMap , …………… {-| Determine if a list is empty. isEmpty [] == True -} isEmpty : List a -> Bool isEmpty xs = case xs of [] -> True _ -> False {-| Keep only elements that satisfy the predicate. filter isEven [1..6] == [2,4,6] -} filter : (a -> Bool) -> List a -> List a filter pred xs = let conditionalCons x xs' = if pred x then x :: xs' else xs' in foldr conditionalCons [] xs
  • 13. Data Structures > Tuples > import String > goodName name = | if String.length name <= 20 then | (True, "name accepted!") | else | (False, "name was too long; please limit it to 20 characters") > goodName "Tom" (True, "name accepted!")
  • 14. Data Structures > Records > point = { x = 3, y = 4 } { x = 3, y = 4 } > point.x 3 > bill = { name = "Gates", age = 57 } { age = 57, name = "Gates" } > bill.name “Gates" > .name bill "Gates" > List.map .name [bill,bill,bill] ["Gates","Gates","Gates"]
  • 15. Data Structures > Records > under70 {age} = age < 70 <function> > under70 bill True > under70 { species = "Triceratops", age = 68000000 } False > { bill | name = "Nye" } { age = 57, name = "Nye" } > { bill | age = 22 } { age = 22, name = "Gates" }
  • 16. JS Object vs Elm Record - You cannot ask for a field that does not exist. - No field will ever be undefined or null. - You cannot create recursive records with a this or self keyword.
  • 17. Contracts import String fortyTwo : Int fortyTwo = 42 drinks : List String drinks = [ "Hennessy", "JimBeam", "Jack Daniels" ] book : { title: String, author: String, pages: Int } book = { title = "Robert", author = "Martin", pages = 434 } longestName : List String -> Int longestName drinks = List.maximum (List.map String.length drinks) isLong : { record | pages : Int } -> Bool isLong book = book.pages > 400
  • 18. All of these types can be inferred, so you can leave off the type annotations and Elm can still check that data is flowing around in a way that works. This means you can just not write these contracts and still get all the benefits!
  • 19. Enumerations type Visibility = All | Active | Completed toString : Visibility -> String toString visibility = case visibility of All -> "All" Active -> "Active" Completed -> "Completed" -- toString All == "All" -- toString Active == "Active" -- toString Completed == "Completed"
  • 20. State Machines type User = Anonymous | LoggedIn String userPhoto : User -> String userPhoto user = case user of Anonymous -> "anon.png" LoggedIn name -> "users/" ++ name ++ "/photo.png"
  • 22. MUV -- MODEL type alias Model = { ... } -- UPDATE type Action = Reset | ... update : Action -> Model -> Model update action model = case action of Reset -> ... ... -- VIEW view : Model -> Html view = ...
  • 23. Example type alias Model = { topic : String , gifUrl : String } init : String -> (Model, Effects Action) init topic = ( Model topic "assets/waiting.gif" , ge update : Action -> Model -> (Model, Effects Action) update action model = case action of RequestMore -> (model, getRandomGif model.topic) NewGif maybeUrl -> ( Model model.topic (Maybe.withDefault model.gifUrl maybeUrl) , Effects.none ) Model Update
  • 24. Example getRandomGif : String -> Effects Action getRandomGif topic = Http.get decodeUrl (randomUrl topic) |> Task.toMaybe |> Task.map NewGif |> Effects.task randomUrl : String -> String randomUrl topic = Http.url "http://api.giphy.com/v1/gifs/random" [ "api_key" => "dc6zaTOxFJmzC" , "tag" => topic ] decodeUrl : Json.Decoder String decodeUrl = Json.at ["data", "image_url"] Json.string Effects
  • 28. Signals module Signal ( Signal , merge, mergeMany , map, map2, map3, map4, map5 , constant , dropRepeats, filter, filterMap, sampleOn , foldp , Mailbox, Address, Message , mailbox, send, message, forwardTo ) where
  • 29. Tasks elm-http — talk to servers elm-history — navigate browser history elm-storage — save info in the users browser
  • 30. elm-storage import Native.Storage getItemAsJson : String -> Task String Value getItemAsJson = Native.Storage.getItemAsJson -- Do better error detection getItem : String -> Decoder value -> Task String value getItem key decoder = let decode value = case decodeValue decoder value of Ok v -> succeed v Err err -> fail "Failed" in getItemAsJson key `andThen` decode
  • 31. Native.Storage aka Storage.js /*! localForage -- Offline Storage, Improved Version 1.2.2 https://mozilla.github.io/localForage (c) 2013-2015 Mozilla, Apache License 2.0 */ (function() { var define, requireModule, require, requirejs; (function() { var registry = {}, seen = {}; define = function(name, deps, callback) { registry[name] = { deps: deps, callback: callback }; }; requirejs = require = requireModule = function(name) { requirejs._eak_seen = registry; if (seen[name]) { return seen[name]; } seen[name] = {};
  • 32. Mailbox type alias Mailbox a = { signal : Signal a , address : Address a } send : Address a -> a -> Task x () import Graphics.Element exposing (Element, show) import Task exposing (Task, andThen) import TaskTutorial exposing (getCurrentTime, print) main : Signal Element main = Signal.map show contentMailbox.signal contentMailbox : Signal.Mailbox String contentMailbox = Signal.mailbox "" port updateContent : Task x () port updateContent = Signal.send contentMailbox.address "hello!" Init with empty value send a message signature
  • 33. Ports - Communication between Elm and pure JS
  • 34. Ports | JS > Elm port addUser : Signal (String, UserRecord) > Elm > JS myapp.ports.addUser.send([ "Tom", { age: 32, job: "lumberjack" } ]); myapp.ports.addUser.send([ "Sue", { age: 37, job: "accountant" } ]);
  • 35. Ports | Elm > JS port requestUser : Signal String port requestUser = signalOfUsersWeWantMoreInfoOn > Elm > JS myapp.ports.requestUser.subscribe(databaseLookup); function databaseLookup(user) { var userInfo = database.lookup(user); myapp.ports.addUser.send(user, userInfo); }
  • 37. HTML generation main : Signal Element main = Signal.map2 renderStamps Window.dimensions clickLocations clickLocations : Signal (List (Int,Int)) clickLocations = Signal.foldp (::) [] (Signal.sampleOn Mouse.clicks Mouse.position) renderStamps : (Int,Int) -> List (Int,Int) -> Element renderStamps (w,h) locs = let pentagon (x,y) = ngon 5 20 |> filled (hsla (toFloat x) 0.9 0.6 0.7) |> move (toFloat x - toFloat w / 2, toFloat h / 2 - toFloat y) |> rotate (toFloat x) in layers [ collage w h (List.map pentagon locs) , show "Click to stamp a pentagon." ] elm-make Stamper.elm --output=Main.html
  • 38. Nice architecture Good documentation Source code with comments Great developer toolset Own packages / manager Overview from JS perspective
  • 40. Complex syntax Learning curve Team integration cost? Overview from JS perspective