Skip to content

Instantly share code, notes, and snippets.

@tjmw
Last active July 13, 2018 16:30
Show Gist options
  • Save tjmw/e9f1dd5b13a6f93485618d3126900771 to your computer and use it in GitHub Desktop.
Save tjmw/e9f1dd5b13a6f93485618d3126900771 to your computer and use it in GitHub Desktop.
Elm Building Blocks Lightning Talk Notes

Elm Building Blocks

An introduction to a few FP concepts with Elm.

Currying

Functions are curried by default in Elm.

From Wikipedia: "the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument".

> String.repeat
<function> : Int -> String -> String
> String.repeat 2 "Elm is Great!"
"Elm is Great!Elm is Great!" : String
> String.repeat 2
<function> : String -> String
> repeatTwice = String.repeat 2
<function> : String -> String
> repeatTwice "Elm is Great!"
"Elm is Great!Elm is Great!" : String

Function Application

We can apply arguments to functions in the way we saw above, by separating with whitespace. E.g. functionName <arg1> <arg2> etc. We can also use the two pipe operators |> and <| to control how we apply arguments to functions.

Here's a super basic example:

> "Elm is Great!" |> repeatTwice
"Elm is Great!Elm is Great!" : String

But this really shines when we want to perform a sequence of operations in a particular order:

> String.reverse
<function> : String -> String
> String.reverse "Elm is Great!"
"!taerG si mlE" : String
> repeatTwice (String.reverse "Elm is Great!")
"!taerG si mlE!taerG si mlE" : String
> "Elm is Great!" |> String.reverse |> repeatTwice
"!taerG si mlE!taerG si mlE" : String
> "Elm is Great!" |> String.reverse |> String.repeat 2
"!taerG si mlE!taerG si mlE" : String
>

Function Composition

We can also compose functions with compatible type signatures together to give us new functions. We use the >> and << operators to do this (which one we chose depends on the order).

> repeatTwice >> String.reverse
<function> : String -> String
> (repeatTwice >> String.reverse) "Tom"
"moTmoT" : String
> "Tom" |> repeatTwice >> String.reverse
"moTmoT" : String
> repeatTwiceAndReverse = repeatTwice >> String.reverse
<function> : String -> String
> repeatTwiceAndReverse "Tom"
"moTmoT" : String

When order matters:

> addOneThenDouble = (+) 1 >> (*) 2
<function> : number -> number
> addOneThenDouble 6
14 : number
> doubleThenAddOne = (+) 1 << (*) 2
<function> : number -> number
> doubleThenAddOne 6
13 : number

Type signatures have to match up:

> repeatTwice
<function> : String -> String
> String.length
<function> : String -> Int
> repeatTwice >> String.length
<function> : String -> Int
> "foo" |> repeatTwice >> String.length
6 : Int
> repeatTwice << String.length
💥

Maybe (& friends)

Maybe formalises the concept that something may or may not exist.

type Maybe a = Just a | Nothing

> List.head
<function> : List a -> Maybe.Maybe a
> List.head []
Nothing : Maybe.Maybe a
> List.head [1,2,3,4]
Just 1 : Maybe.Maybe number

What if you want to perform an operation on a maybe type? Maybe.map!

Let's say we want to take the first of a list of integers and double it:

> double = (*) 2                                                                                                                                               
<function> : number -> number
> List.head >> double
💥
> doubleTheHead = List.head >> Maybe.map double
<function> : List number -> Maybe.Maybe number
> doubleTheHead []
Nothing : Maybe.Maybe number
> doubleTheHead [1,2,3]
Just 2 : Maybe.Maybe number
>

There are a number of types which support mapping over the inner value of something which wraps them up (List, Result, Maybe). In Learn You A Haskell, Mira Lipovaca talks about this concept of something in a box, which scurries out to have an operation perfomed on it, before scurrying back in to the box. You can also think of it as a value with some context. Elm doesn't really formalize the relationship between all of these, but Haskell does, with typeclasses. So all of these types which support mapping over them implement the Functor typeclass. You can think of it as an interface. The dreaded Monad is just another typeclass which supports some specific operations and can be thought of as an extension of Functor.

@tjmw
Copy link
Author

tjmw commented Jul 13, 2018

Feedback:

  • Include some diagrams around the Maybe section to allow visualising the concept a bit more
  • Slides with headings to keep the context of what I'm talking about

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