Think like an Elm developer Piper Niehaus Denver, CO, USA - - PowerPoint PPT Presentation
Think like an Elm developer Piper Niehaus Denver, CO, USA - - PowerPoint PPT Presentation
Think like an Elm developer 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
@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 function
Function (arguments) { Computations } External state
Impure function
Return value Return value Function (arguments) { Computations }
@piperniehaus
@piperniehaus
Pure vs impure functional programming languages
Pure 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 typing
Types are checked at runtime
Static typing
Types are checked at compile time
Strong static* typing
Static 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 Elm
Extra 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
- r 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 name
Function 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 types
Example: Managing memberships
- Project member
○ First name ○ Last name ○ Email
- Invitee
@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 Elm
The 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
@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 pattern
The 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
- f 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
Culture Language Elm Culture Language Kotlin Culture Language Typescript
@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
@piperniehaus
Happy (FUNctional) coding!
pniehaus@pivotal.io