Think like an Elm developer
@piperniehaus
Piper Niehaus● Denver, CO, USA● Backpacker / skier● Nonprofit board chair
@piperniehaus
Software Engineer at Pivotal
● Pivotal Tracker team○ Elm in Production since 2016
● Internal Products and Services team○ Kotlin
@piperniehaus
We all get excited about new languages
@piperniehaus
Production changes everything● Bugs matter● Testing matters● Maintainability matters
@piperniehaus
Goals● The promise (level set)● The production hump● What’s next?
@piperniehaus
Agenda● About me● The promise of Elm
○ Background○ Language○ Architecture
● The Pivotal Tracker experience● Is Elm right for you?
@piperniehaus
Elm: A DSL for web apps● Language● Framework● Ecosystem
@piperniehaus
Elm in production
@piperniehaus
Elm background
● Evan Czaplicki’s Harvard thesis● Now at noredink ● Elm’s Benevolent Dictator For Life
@piperniehaus
Benevolent dictator for life● Design● Direction● Priorities● Pace
@piperniehaus
Agenda● About me● The promise of Elm
○ Background○ Language○ Architecture
● The Pivotal Tracker experience● Is Elm right for you?
@piperniehaus
Elm is a programming language that compiles to JavaScript
● Pure functional● Strong static type system with a friendly compiler
Elm language
@piperniehaus
Agenda● About me● The promise of Elm
○ Background○ Language
■ Pure functional■ Strong static type system with a friendly compiler
○ Architecture
● The Pivotal Tracker experience● Is Elm right for you?
@piperniehaus
What is a pure function?A function that takes some input and returns output without affecting or being affected by external state
@piperniehaus
Pure functionFunction (arguments) {
Computations
}
External state
Impure function
Return value
Return value
Function (arguments) {
Computations
}
@piperniehaus
@piperniehaus
Pure vs impure functional programming languagesPure functional languages:
● Languages that support only functional paradigms (Haskell, Elm)
Impure functional languages:
● Languages that support both functional and imperative style programming (Kotlin, Python)
@piperniehaus
Benefits of pure functional programming● Consistency● Code is easy to follow and understand● A function given the same values always has the same result● Lack of race conditions● Time travel debugger
@piperniehaus
Elm time travel debugger● Because every function in Elm is pure● See the system at any state● Roll back, roll forward
@piperniehaus
Time travel debugging
@piperniehaus
Agenda● About me● The promise of Elm
○ Background○ Language
■ Pure functional■ Strong static type system with a friendly compiler
○ Architecture
● The Pivotal Tracker experience● Is Elm right for you?
@piperniehaus
What is a type?● An object’s type describes
○ The kind of data in the object○ What it can do
● Examples: ○ String○ Int
● All languages must check types○ Can’t do 1 + “two“
@piperniehaus
What is static typing?● All languages must check types but statically typed languages check at
compile time, while dynamically typed languages check at runtime.● Example: Can’t do 1 + “two“
○ In ruby: we find this out when we try to load a page and see an error○ In Elm: we can’t even compile our code to run the app at all
@piperniehaus
Dynamic typingTypes are checked at runtime
Static typingTypes are checked at compile time
Strong static* typingStatic typing with the goal of minimizing the gap between code that compiles and code that runs error-free
* While static is a definite CS term, strong is colloquial. This is our definition going forward.
@piperniehaus
Code compiles
Static typing
Strong static typing
Code compiles
Code runs without errors
Code compiles
Code runs without errors
Dynamic typing
Code runs without errors
@piperniehaus
Strong static typing tools in ElmExtra checks to prevent runtime errors. EG:
● Custom types● Null checks● Exhaustiveness checks
○ type Pet = Cat | Dog | Fish○ Fido is a pet. ○ Everywhere we use fido, we must account for the possibility that fido could be a cat, a dog
or a fish
@piperniehaus
Benefits of strong static typing...● Catches errors early and preempts null pointer exceptions● Provides parameter + return type matching● Makes impossible states impossible (no need to test them)● Encode business logic into type system
@piperniehaus
For the developer, strong static typing means...● Enables developers to focus on business logic● Provides fast feedback● Makes code easy to read● Refactoring is easy
@piperniehaus
Elm’s friendly compiler● Elm’s compiler messages are easy to read● Makes it easy to rely on the compiler
@piperniehaus
Example, a misspelled function nameFunction addInts is defined
Oops! We tried to call it but misspelled
Compiler error is helpful and friendly
@piperniehaus
Agenda● About me● The promise of Elm
○ Background○ Language○ Architecture
● The Pivotal Tracker experience● Is Elm right for you?
@piperniehaus
What is the Elm architecture?A way of building web apps that separates an application into:
● Model: the application’s current state● Update: the only way that the state is updated● View: the application’s current state in html format
The elm architecture feels natural in Elm, but it also works in other languages
● Inspiration for Redux
@piperniehaus
Elm architecture example
View
Model
Update
User makes a change in the UI(onClick)
Message is sent to update function
Update function returns a new model based on the old model and function arguments
New model is passed to view function and rendered in the browser
@piperniehaus
An example
@piperniehaus
@piperniehaus
Benefits of the Elm architecture● Single source of truth● Easy to understand● Removes race conditions● Model and view stay in sync
@piperniehaus
Why use Elm?
Static typing Pure functions Architecture+
=Few runtime errors
+
Language Architecture
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Types & FP in practice○ Scaling○ Testing○ JavaScript interop
● Is Elm right for you?
@piperniehaus
Elm and functional programming at Pivotal Tracker
RoR / React /
Backbone
Dashboard in Elm
Project Memberships Page in Elm
2016 2017 2018
Kotlin, TypeScript
experiments
New user experience
in Elm
Much discussion
@piperniehaus
Elm here!
@piperniehaus
The decision to use Elm ● Developers using Elm in personal projects● Multiple meetings, much thought
○ Would Elm stick around?○ Interop between multiple languages?○ Developers using multiple languages?
● Started in an isolated piece of the app○ Not interconnected with other stuff○ Easy to pull out if it didn’t work
@piperniehaus
Projects● The Dashboard page
○ Learning curve○ (Most) developers like it○ Challenge of scaling an Elm app○ Testing philosophies in Elm vs on our team
● Expanded usage○ Testing hitting a stride○ Challenge of interop with Javascript external libraries
@piperniehaus
Continuing to use Elm mentalities● Where else can we apply functional programming and strong types?
○ More Elm?○ TypeScript?○ Kotlin?
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Types & FP in practice○ Scaling○ Testing○ JavaScript interop
● Is Elm right for you?
@piperniehaus
Elm lives up to its promise● Runtime errors● Race conditions● Easy refactors● Mindset change
@piperniehaus
Encoding business logic into typesExample: Managing memberships
● Project member○ First name○ Last name○ Email
● Invitee○ Email
@piperniehaus
People
@piperniehaus
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Types & FP in practice○ Scaling○ Testing○ JavaScript interop
● Is Elm right for you?
@piperniehaus
How to structure a large Elm app?● It depends™● One of the biggest hurdles and biggest FAQs● Big files? Small files?● Big update loops? Multiple update loops?
¯\_(ツ)_/¯
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Types & FP in practice○ Scaling
■ Puzzle ■ Mindset
○ Testing
● Is Elm right for you?
@piperniehaus
File structure puzzle● Pure function = less obvious structure● Elm apps are broken up into Modules● Each file is a module● Modules have public (exposed) functions and private functions
@piperniehaus
Hello World
Main.elm
● Model & Types● Update loop● View
The first modules
Main.elm
● Model & Types● Update loop
View.elm
● Takes: Model● Returns: Html
More modules
Main.elm
● Model & Types● Update loop
View.elm
● Takes: Model● Returns: Html
(Etc).elm
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Types & FP in practice○ Scaling
■ Puzzle ■ Mindset
○ Testing
● Is Elm right for you?
@piperniehaus
Elm structure mindset● Each Tracker Elm app has a different structure● We debated
○ Conventions?○ Top-down structures in advance?○ Big files?
● Conclusions○ Refactor often (for now)○ Follow guidelines
@piperniehaus
Ease of refactoring Elm● Ease of refactoring Elm allows delay of architectural decisions● Compiler makes large files easier to manage● We have restructured all Elm projects multiple times
@piperniehaus
Landing point: Conventional Wisdom +● Conventional wisdom:
○ Keep everything together until it starts to clump naturally○ Break clumps into modules○ Modules generally structure around a type
● Tracker addendum:○ Use tests and module exposures to drive structure
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Types & FP in practice○ Scaling○ Testing
■ Elm language■ Elm application
○ JavaScript interop
● Is Elm right for you?
@piperniehaus
Testing in ElmThe myth:
● If it compiles, it works
The reality:
● The elm compiler catches many errors● Testing business logic is still important
The compiler prevents using a String where we need an Int, but it can’t prevent using the wrong Int all together.
@piperniehaus
Test/Type Driven Development (TDD)● Use compiler failures AND tests to drive code● Compiler ensures that code will always run● Tests ensure that code will fulfill business logic
@piperniehaus
@piperniehaus
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Scaling○ Testing
■ Elm language■ Elm application
○ JavaScript interop
● Is Elm right for you?
@piperniehaus
Testing phase 1 The first modules
Main.elm
● Model & Types● Update loop
View.elm
● Takes: Model● Returns: Html
Tests here
Not here
@piperniehaus
Testing the update loop● Update loop is the main logic of the app● Pure functional makes testing easier● Elm architecture makes testing resultant updates hard
@piperniehaus
Testing HTML● We test HTML output via Happy Path integration tests
○ Testing HTML is brittle
● We don’t test the view○ Much debate○ Testing view output in Elm is cumbersome
@piperniehaus
Selector patternThe first modules
Main.elm
● Update function● Model & Types
Selector.elm
● Takes: Model● Returns: Selector
View.elm
● Takes: Selector● Returns: Html
Tests here
Tests here
@piperniehaus
Selectors● Middle layer between model and view● Computed, not stored● “View state”
Model
● Stored application state
Selector
● Calculated state
View
● HTML based on selector calculations
@piperniehaus
Elm tests driving structure● If no external modules use a function, don’t expose it● If a module exposes a function, test it● If an unexposed function feels like it needs testing, make a new module
and expose it
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Scaling○ Testing○ JavaScript interop
● Is Elm right for you?○ It depends™○ Think like an elm developer
@piperniehaus
JavaScript interop● Elm sends messages to and from JavaScript
○ Ports ○ Native code (EEK!)
● Multi-language code base
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Scaling○ Testing○ JavaScript interop
■ Background■ Multi-language code base
● Is Elm right for you?○ It depends™○ Think like an elm developer
@piperniehaus
Background on JS interop● Ports
○ Recommended○ Hooks into the update loop
● “Native code”○ Not recommended○ Less safe
@piperniehaus
What’s a port?Elm app
Outgoing
Update loop sends a Cmd to contact JS via a port
Incoming:
Subscribe to the port, Msg enters the update loop
JavaScript
Incoming:
Subscribe to the port, get the message, do the thing
Outgoing
Send to a port in Elm
@piperniehaus
Good:● Anything JS can do, Elm can do● More libraries
● Communication via JSON● In and out of type safety● Multiple languages in your
codebase
Meh:
@piperniehaus
“Native” code● Very discouraged, hacky way of wrapping native (JS) code in Elm to create
Elm libraries● Won’t work in Elm 0.19
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience
○ Background○ Scaling○ Testing○ JavaScript interop
■ Background■ Multi-language code base
● Is Elm right for you?○ It depends™○ Think like an elm developer
@piperniehaus
Elm devs support multiple languages long-term● Elm is growing but lacks libraries
○ Need to maintain Ports long term
● Transition to Elm is gradual○ During the transition, you’ll need Elm and JS to still work
● Transition may not be the goal
@piperniehaus
Issues with supporting multiple languages ● Developers need proficiency in multiple languages● Same code in multiple languages?● Dependent on libraries and webpack knowledge
○ React-elm-components doesn’t work with React 16
● “Native” libraries will disappear in next Elm version
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience● Is Elm right for you?
○ It depends™○ Think like an elm developer
@piperniehaus
:)● Coding feels good● Learning● Excitement● High productivity after
ramp up● Easy refactors
As a developer...
:(● Need to know multiple
languages● Port / JS complexity● Longer ramp-up
@piperniehaus
:)● Time travel debugger● No runtime exceptions
As a product owner or tester...
:(● Potential higher cost for
features that require libraries
@piperniehaus
:)● No runtime exceptions
As a user...
:(● Excitement is the spice
of life?
@piperniehaus
Agenda● About me● The promise of Elm● The Pivotal Tracker experience● Is Elm right for you?
○ It depends™○ Think like an elm developer
@piperniehaus
What if Elm isn’t right for you?Elm learnings can still be important
● Encode business logic in the type system● Make impossible states impossible● Minimize side effects to minimize confusion
Culture can compensate for language
● Similar to TDD
@piperniehaus
Think like an Elm developer
Language + culture
Code compiles
Code runs without errors
Code runs without errors
Code compiles
@piperniehaus
Language + Culture
CultureLanguageElm
CultureLanguageKotlin
CultureLanguageTypescript
@piperniehaus
Cultural shifts● Kotlin● Typescript
@piperniehaus
● Pay attention to warnings○ compileKotlin { kotlinOptions.allWarningsAsErrors = true }
● Be functionally minded (avoid side-effects)● Avoid !!
Strong static typing / FP in Kotlin
@piperniehaus
Compiles with a warning
Bird is missing
@piperniehaus
● Be functionally minded● Always turn on --strictNullChecks● Avoid Any
○ Exception: 3rd party libraries, sometimes
● Avoid type casting● Avoid returns after case statements
Strong static typing / FP in Typescript
@piperniehaus
Don’t override TypeScript exhaustiveness checking
Adding a return after the case statement ends disables exhaustiveness checking
@piperniehaus
You should● Use Elm● Think like an Elm developer