Skip to content

Instantly share code, notes, and snippets.

@tombusby
Last active December 11, 2019 20:40
Show Gist options
  • Save tombusby/3588357e816a41acf6390e5bd835fe20 to your computer and use it in GitHub Desktop.
Save tombusby/3588357e816a41acf6390e5bd835fe20 to your computer and use it in GitHub Desktop.
Elm Study Notes

Elm Study Notes

Decoding Recursive Data Structures:

From Elm Slack

For example:

type ExtractFields
    = List ExtractField


type alias ExtractField =
    { name : String
    , selector : Selector
    , extractType : Maybe ExtractType
    , extractFields : ExtractFields
    }


extractFieldsDecoder : Decoder ExtractFields
extractFieldsDecoder =
    extractFieldDecoder
        |> Decode.list


extractFieldDecoder : Decoder ExtractField
extractFieldDecoder =
    Decode.map4 ExtractField
        nameDecoder
        selectorDecoder
        extractTypeDecoder
        (field "extract_fields" extractFieldsDecoder)

This gives the following error:

Error message for recursive data structure

You need the following function:

Basic Higher-Order Functions Removed

The following were removed in 0.19:

uncurry
curry
flip

These can now be found in the following package:

Row Polymorphism and Type Aliases

type alias Model =
    { name : String
    , password : String
    , passwordAgain : String
    }

The main benefit of using a type alias for this is when we write the type annotations for the update and view functions. Writing Msg -> Model -> Model is so much nicer than the fully expanded version! It has the added benefit that we can add fields to our model without needing to change any type annotations.

When you create a type alias specifically for a record, it also generates a record constructor. So if we define a User type alias, we can start building records like this:

> type alias User = { name : String, age : Int }

> User
<function> : String -> Int -> User

> User "Sue" 58
{ name = "Sue", age = 58 } : User

> User "Tom" 31
{ name = "Tom", age = 31 } : User

This is only for records. Making type aliases for other types will not result in a constructor.

Elm Equivalents

  • Subscriptions & Commands - A bit like Haskell's IO monad: can get impure stuff from the runtime system like the current time and issue commands to the runtime system that produce side-effects
  • Result - Elm's name for the Haskell Either type

Why Isn't Functional Programming the Norm? (Potential FP Killer Apps)

elm-ui is mentioned in this talk as a "potential killer app of Elm". Worth looking into.

JSON Decoding

In JavaScript, the approach is to just turn JSON into JavaScript objects and hope nothing goes wrong. But if there is some typo or unexpected data, you get a runtime exception somewhere in your code. Was the code wrong? Was the data wrong? It is time to start digging around to find out!

In Elm, we validate the JSON before it comes into our program. So if the data has an unexpected structure, we learn about it immediately. There is no way for bad data to sneak through and cause a runtime exception three files over. This is accomplished with JSON decoders.

Use NoRedInk/elm-json-decode-pipeline to avoid all the tedious mucking about with map2 etc.

(The Lack of) Monads and Functors

Note how map2 is implemented separately for JSON.Decode and List since there's no polymorphic instances for tuples/lists/whatever for something like fmap, and no way to get multiple monadic values with a do notation and return the tuple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment