Think like an Elm developer - QConSP · programming (Kotlin, Python) @piperniehaus Benefits of pure...

Post on 03-Aug-2020

6 views 0 download

transcript

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

@piperniehaus

Happy (FUNctional) coding!

pniehaus@pivotal.io