functional web development using elm
TRANSCRIPT
Functional Web Programming using Elm
Spencer Schneidenbach @schneidenbach
schneids.net
Common element?
JavaScript
this
https://www.christosmonogios.com/2016/08/15/The-
This-Keyword-In-JavaScript-Must-Know-JavaScript-Features-Every-Programmer-Should-Learn-To-
Master-The-Language/
http://odetocode.com/blogs/scott/archive/
2016/09/01/the-troubles-with-
javascript-arrow-functions.aspx
https://derickbailey.com/
2016/08/22/undefined-is-not-
a-function/
http://www.bitnative.co
m/2016/08/18/react-binding-patterns-this-
keyword/
http://www.javascripthive.info/javascript/context-this/
var customer = repo.getCustomer(id); var orders = customer.getOrders();
var customer = repo.getCustomer(id); var orders = customer.getHorDoeuvres();
var customer = repo.getCustomer(id); var orders = customer.getOrderes();
JavaScript Isn’t Bad.
JavaScript Is Weird.
Elm “just works.”
Not like a Mac “just works”
That’s pretty vague
So let’s dig in
Four things
One: handling null
null
–Tony Hoare
“I call it my billion-dollar mistake. It was the invention of the null reference in 1965.”
null/undefined
What about in Elm?
type alias Order = { name: String, date: Date, amount: Float }
type alias Order = { name: String, date: Date, amount: Float }
myOrder: Order
myOrder : Order myOrder = { --no name date = today, amount = 123.45 }
myOrder : Order myOrder = { name = null, date = today, amount = 123.45 }
So where is null?
Easy: there IS no null
Easy: there IS no null
(loud applause and cheering)
type Maybe a = Just a | Nothing
getAmount : Maybe Int -> String getAmount maybeInt = case maybeInt of Just i -> "You have: " ++ toString i Nothing -> "You don't have any!"
type Maybe a = Just a | Nothing
type ContactType = Email String | Phone Int | Twitter String | Address MailingAddress
type ContactType = Email String | Phone Int | Twitter String | Address MailingAddress
contactPerson : ContactType -> String contactPerson contact = case contact of Email email -> email Phone phoneNumber -> "Phone: " ++ toString phoneNumber Twitter twitterHandle -> "Twitter: " ++ twitterHandle
type ContactType = Email String | Phone Int | Twitter String | Address MailingAddress
contactPerson : ContactType -> String contactPerson contact = case contact of Email email -> email Phone phoneNumber -> "Phone: " ++ toString phoneNumber Twitter twitterHandle -> "Twitter: " ++ twitterHandle Address myAddress -> "No one sends mail anymore"
No void
Two: immutability
Let’s set the stage
Feature request:Order amounts that are negative
should have a red name
Feature request:Salespeople who are not active
should not be displayed
What about Elm?
No mutation. Can’t modify existing
objects.
Instead, you create copies
myOrder : Order myOrder = { name = "Lumber", contact = Email "[email protected]", amount = 123.45 }
myOrder : Order myOrder = { name = "Lumber", contact = Email "[email protected]", amount = 123.45 }
myOrder.contact = Phone 5551234
myOrder : Order myOrder = { name = "Lumber", contact = Email "[email protected]", amount = 123.45 }
myOrder = { name = "Lumber", contact = Phone 5551234, amount = 123.45 }
myOrder : Order myOrder = { name = "Lumber", contact = Email "[email protected]", amount = 123.45 }
myNewOrder : Order myNewOrder = { myOrder | contact = Phone 5551234 }
Three: error handling
Guess what?
There are
NO RUNTIME ERRORS in Elm
type Maybe a = Just a | Nothing
function getInputCount() { var input = $(".count").val(); return parseInt(input); }
function getInputCount() { var input = $(".count").val(); //bgggs return parseInt(input); //NaN }
type Result error value = Err error | Ok value
String.toInt : String -> Result String Int
multiplyStrings : String -> String -> Maybe Int multiplyStrings str1 str2 = let intStringOne = String.toInt str1 intStringTwo = String.toInt str2 in case (intStringOne, intStringTwo) of (Ok int, Ok int2) -> Just (int * int2) (_, _) -> Nothing
No try/catch blocks
Elm makes you handle errors.
Four: The Elm Architecture
function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } }
function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } }
let store = createStore(counter)
store.dispatch({ type: 'INCREMENT' })
function employeeStore(state = [], action) { switch (action.type) { case 'ADD': return [...state, action.employee] default: return state } }
let store = createStore(employeeStore)
store.dispatch({ type: 'ADD', employee: { firstName: 'Spencer' } })
function employeeStore(state = [], action) { switch (action.type) { case 'ADD': state.push(action.employee); return state; default: return state } }
let store = createStore(employeeStore)
store.dispatch({ type: 'ADD', employee: { firstName: 'Spencer' } })
function employeeStore(state = [], action) { switch (action.type) { case 'ADD': return state.push(action.employee); default: return state } }
let store = createStore(employeeStore)
store.dispatch({ type: 'ADD', employee: { firstName: 'Spencer' } })
Array.push -> number
Model Update
View
-- MODEL
type alias Model = Int
model : Model model = 0
-- UPDATE
type Msg = Increment | Decrement
update : Msg -> Model -> Model update msg model = case msg of Increment -> model + 1
Decrement -> model - 1
-- UPDATE
type Msg = Increment | Decrement
update : Msg -> Model -> Model update msg model = case msg of Increment -> model + 1
-- UPDATE
type Msg = Increment | Decrement
update : Msg -> Model -> Model update msg model = case msg of Increment -> model + 1
Decrement -> model - 1
-- VIEW
view : Model -> Html Msg view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]
1. null handling 2. immutability 3. error handling 4. The Elm Architecture