There is a Github repository for the stuff I built while following this book.
Chapter 1: Get Started with Elm
Introduction to the language, and initialization of the example app: Picshare
All variables are defined as constants, but REPL allows us to change them for convenience.
Elm is an expression-oriented language. An expression is anything a programming language can evaluate to produce a value. Literals such as strings and numbers, math operators, and calling functions are all expressions in Elm.
Branching
The if
statement is similar to ternary
expression in JavaScript. Elm's if
statements have two advantages:
- It makes us to supply an
else
branch. - It expects all branches to return same type of value.
Partial Application
Elm functions are curried, which is the fancy way of saying they take one argument at a time.
Filling one argument at a time is known as partial application. Calling a function with only some arguments is partially applying it.
Create curried functions, partially apply arguments.
Lists
Lists (also records) doesn't have notion of indices. Lists work by letting each element reference the next element in the list, similar to the links in chain. The language protects us from potential undefined/null
values with this approach.
Lists can also include same type of elements.
Chapter 2: Create Stateful Elm Applications
Introduction to Elm Architecture: View-Model-Update pattern.
Records
Elm developers typically refer to the entries in a record as fields.
Record update syntax newDog = { d | age = dog.age + 1 }
differs from Object.assign
because it creates a new object each time, where the other lets us merge together different JavaScript objects.
Benefits of Immutability
- It makes the data flow explicit.
- Instances of data structures can share data internally because there is no risk of code accidentally or intentionally mutating the shared data.
- In multithread languages, there is no risk of threads mutating the shared data.
Custom Types and Pattern Matching
Elm developers refer to the values of a custom type as constructors.
Each branch of a case expression is itself an expression so we don't need a break
statement. _
can be used as the wildcard for pattern matching.
update msg model = case msg of Like -> { model | liked = True } _ => { model | liked = False }
Chapter 3: Refactor and Enhance Elm Applications
Using Type Aliases as Constructor
Elm also creates a constructor function with the same name as the type alias.
type alias Model = { url : String , caption: String , liked : Bool } initialModel = Model "https://programming-elm/1.jpg" "Surfing" False
Checking List Length
Pattern matching is more versatile and faster compared to counting elements.
list = ["a", "b"] case list of [] -> text "empty" _ -> ul [] [(List.map ... list])
Chapter 4: Communicate with Servers
JSON decoding with elm-decoders
, also uses NoRedInk's elm-json-decode-pipeline
package for the railway.
type Result error value = Ok value | Err error
Special string syntax (""")
myElmPoem = """ Roses are red... """
HTTP commands
We create the command, but execution is delegated to the Elm Architecture for the sake of purity. Sending commands can be done in the application lifecycle or as a result of a Msg
handled in the update
function.
Safely Handle null
type Maybe a = Just a | Nothing type alias Model = { photo = Maybe Photo } case model.photo of Just Photo -> "yey!" Nothing -> "ney!"
Chapter 5: Go Real-Time with WebSockets
- Started with handling
loading
anderror
states for the HTTP request in the UI. - Created a JS-Elm bridge for listening WebSocket events, with
ports
listen
→ outgoing ports that return aCmd
→elmApp.ports.subscibe.listen(webSocketHandler)
receive
→ incoming ports that return aSub
→socket.onMessage = (e) => elmApp.ports.receive.send(e.data)
- Added a notification banner to show there are new photos available to be put into the
Feed
, then anonClick
handler to put it into feed fromstreamQueue
.
Backward Composition (<<) Operator
WebSocket.receive (LoadStreamPhoto << decodeString photoDecoder) --- same as --- WebSocket.receive (\ json -> LoadStreamPhoto (decodeString photoDecoder json))
Cons (::) Operator
Creates a new list by prepending the left operand to the list on the right.
oldList = ["b", "c"] newList = "a" :: oldList -- -> ["a", "b", "c"]
Chapter 6: Build Larger Applications
Refactored of the salad-builder
app.
Deconstructed view
to have multiple functions.
view .... everything viewInput viewCheckbox
Simplified messages
SelectLettuce SelectSpinach SelectSpringMix --- to --- type Base = Lettuce | Spinach | SpringMix SelectBase Base
Learnt two ways to make model
more maintainable:
type alias Model = { building : Bool , sending : Bool , success : Bool , error : Maybe String , base : Base , toppings : Set String , dressing : Dressing , name : String , email : String , phone : String } ---- first approach: nested state ---- type alias Salad = { base: Base , toppings: Set String , dressing: Dressing } type alias Model = { --- view state --- salad: Salad --- contact details state --- } ---- second approach: extensible records ---- type alias Contact c = { c | name : String , email : String , phone : String } -- any object can be treated as Contact if it contains the fields --
Prevented invalid states
type alias Model = { building : Bool , sending : Bool , success : Bool , error : Maybe String -- rest of the state -- } --- combined fields --- type Step = Building (Maybe Error) | Sending | Confirmation type alias Model = { step: Step -- rest of the state -- }
Chapter 7: Develop, Debug, and Deploy with Powerful Tooling
- Usage of
Debug.log
andDebug.todo
withJson.Decode
examples. elm reactor
andcreate-elm-app
- Ported
picshare
tocreate-elm-app
Chapter 8: Integrate with JavaScript
Added an Elm powered file uploader to a React application that has three inputs (title, description, images) for a note taking service.
Sent input events from Elm to JS with ports, also received the image list from the source of truth, App
component from the JS context, again through ports.
Used Flags
for passing data during the initialization step of the Elm app.
Chapter 9: Test Elm Applications
Applied TDD while developing a date helper library called awesome-date
- Unit tests isolate and verify the behavior of one small piece of software.
- Integration testing verifies that your units work together correctly.
- Added a custom assertion for
ExpectDate
. - Used
Fuzz
module for property based testing.
Tested an Elm app called awesome-date-app
which uses the awesome-date
module.
- Tests for
model
,view
,update
functions based on the changes in the app. - Used
Test.Html
to interact with the virtual DOM for selecting elements and simulating events.
Chapter 10: Build Single-Page Applications
Router boilerplate. How to match parameters, re-use components for multiple pages etc.