Go right to the Examples Page and work through each of the examples, one by one. They actually have useful comments.
Then go visit the Docs Page and visit most of the articles (in order). The important ones are the Quick Start for JavaScript Programmers, the Syntax Reference. Browse the entire Elm Language Guide.
Also of interest is the cool Elm Cheat Sheet.
Now, here’s one version of Hello, World:
import Html exposing (text) main = text "Hello World"
You can run this in the Try Elm playground. (There are other playgrounds out there, too, including Ellie and runelm.io.) To make your own app on your own machine, store the program in the file hello.elm and use the command line:
$ elm make hello.elm
If you’re running Elm for the first time in this directory, Elm will realize you haven’t set anything up, so it will ask you for permission to install three packages (elm-lang/core, elm-lang/html, and elm-lang/virtual-dom). The installation will write information about the packages into the file elm-package.json and will download the packages themselves from a global repository and place them in the folder elm-stuff.
After the packages are installed, the elm make
command continues by producing the output file index.html. Go ahead and run this however you normally “run” (or view) HTML pages (e.g., open index.html
on macOS, or start index.html
on Windows, or just drag it into a browser).
Here’s another example:
import Html exposing (Html, ul, li, text) import List exposing (reverse, map, repeat) fibsUpTo : Int -> List Int fibsUpTo n = let accumulate fibs = case fibs of x :: y :: rest -> if x + y > n then fibs else accumulate (x+y::fibs) _ -> [] in reverse (accumulate [1, 0]) main : Html Never main = fibsUpTo 100000 |> map (toString >> text >> repeat 1 >> li []) |> ul []
You can run this in an online Elm runner, or on your local machine. Here’s an easy way to build and run in one step locally (on macOS):
$ elm make fib.elm && open index.html
Do you see what’s going on? The Elm expression
ul [] [ li [] [text 0] , li [] [text 1] , li [] [text 1] , li [] [text 2] , ... , li [] [text 75025] ]
evaluates to the HTML:
<ul><li>0</li><li>1</li><li>1</li><li>2</li>...<li>75025</li></ul>
So ul
is a function which takes a list of HTML attributes (yes it was empty in this example), and a list of (child) HTML elements, and produces an HTML unordered list element.
You may have been wondering....
Are those type annotations necessary?
No, Elm can figure out types for you. But the annotations add some nice documentation, and most Elm programmers use them.
What is the typeHtml Never
thatmain
has?
It is the type of HTML elements that generate messages of typeNever
.
Why are Elm type annotations on a separate line, and not interleaved into the function definition?
It’s so they are unobtrusive. And so you can add them later to code that doesn’t have them, with less chance of messing things up.
How about some graphics? Just like Elm has functions for HTML elements and attributes, it has functions for the elements and attribites of SVG also. Here’s a little app that makes a pretty picture:
import Html exposing (Html) import Svg exposing (..) import Svg.Attributes exposing (..) main : Html Never main = svg [ width "300", height "300", viewBox "-150 -150 300 300" ] (List.map shape (List.range 0 23)) shape : Int -> Svg Never shape n = let angle = 15 * toFloat n |> degrees in circle [ cx (100 * cos angle |> toString) , cy (100 * sin angle |> toString) , r "15" , fill <| "hsl(" ++ (15 * n |> toString) ++ ", 90%, 60%)" ] []
If you try to compile this with elm make colored_circles.elm
now, you’ll get an error because Elm cannot find the Svg
module by default. That module exists in the package elm-lang/svg. So first enter:
$ elm package install elm-lang/svg
Now you can compile and open (on a Mac) with:
$ elm make colored_circles.elm && open index.html
Pssst, this code works in an online editor, too.
We’ll begin our techincal introduction to Elm with its type system. Elm is statically typed. This means you can tell the type of every expression in your program without ever running it.
The five simple types are Bool
, Int
, Float
, Char
, and String
. Type names start with an uppercase letter.
$ elm repl > 7.5 * 3.88 29.099999999999998 : Float > False && True False : Bool > 7 / 2 3.5 : Float > 7 // 2 3 : Int > 7 % 2 1 : Int > 'π' 'π' : Char > "Привет мир" "Привет мир" : String > String.length("Привет мир") 10 : Int
Elm has lists. Good to know: List String
and List Float
are different types.
> ["spot", "spike"] ["spot","spike"] : List String > [1.5, 2.2, sqrt(110)] [1.5,2.2,10.488088481701515] : List Float > [[], ['c'], ['c','a','t']] [[],['c'],['c','a','t']] : List (List Char) > 'd' :: ['o', 'g'] ['d','o','g'] : List Char > False :: True :: False :: [] [False,True,False] : List Bool
Elm’s product types are called tuples:
> (10//2, "dog", False) (5,"dog",False) : ( Int, String, Bool ) > ('$', 'π') ('$','π') : ( Char, Char ) > () () : ()
That last type is the type of zero-element tuples. You can make tagged prodcuts, called records:
> p = {x=5.0, y=3.2, color="red"} { x = 5, y = 3.2, color = "red" } : { color : String, x : Float, y : Float } > p.x 5 : Float > .x p 5 : Float > {p | color="green"} { x = 5, y = 3.2, color = "green" } : { x : Float, y : Float, color : String }
Elm handles sum types through its custom types. Use pattern matching to get at the variants:
> type Shape = Circle Float | Rectangle Float Float > area s =\ | case s of\ | Circle r -> pi * r * r\ | Rectangle w h -> w * h: Repl.Shape -> Float > > area (Circle 10) 314.1592653589793 : Float > area (Rectangle 7 6) 42 : Float
Those backslashes are not part of Elm. I just had to type them because that is the way you tell the REPL that your entry continues on the next line. And those vertical bar symbols are the prompt you get when you are continuing an entry across multiple lines.
This type declaration created a new type called Shape
, a function Circle
that takes in a float and produces a shape, and a function Rectangle
that (essentially) takes in two floats and produces a shape. You can call Circle
and Rectangle
constructors if you like, but don’t call them types.
Functions are values so they have types too:
> \n -> n // 2 <function> : Int -> Int > \(x, y) -> 2 * x % y <function> : ( Int, Int ) -> Int > \a -> \b -> "hello" :: b :: a <function> : List String -> String -> List String > Circle <function> : Float -> Repl.Shape > Rectangle <function> : Float -> Float -> Repl.Shape
You can infer that the type of \x -> [x*pi, 2*x*pi]
is Float -> List Float
.
But what is the type of \x -> [x]
? Interesting question. Certainly, the type of the result of calling this function is pretty obvious:
> (\x -> [x]) "dog" ["dog"] : List String > (\x -> [x]) False [False] : List Bool
So it looks like the argument of the function can be of any type, and the result is a list of whatever the argument type is. Is that so?
> \x -> [x] <function> : a -> List a
Yep it is. And as you might have guessed, type variables start with a lower case letter. Here are some more examples:
> \x -> (x, x) <function> : a -> ( a, a ) > \x -> \y -> (x, (y, x), y) <function> : a -> b -> ( a, ( b, a ), b ) > \x -> \y -> \(z, w) -> y / 3 <function> : a -> Float -> ( b, c ) -> Float > type Tree a = Leaf a | Node (Tree a) (Tree a) > Node (Leaf 3) (Node (Leaf 5) (Leaf 8)) Node (Leaf 3) (Node (Leaf 5) (Leaf 8)) : Tree number > [] [] : List a
That tree datatype was pretty cool, wasn’t it? It is really common to see type variables in custom types. In fact, one of the most common such types in Elm is the Maybe
type, which Elm uses because it doesn’t have exceptions or null references:
type Maybe a = Just a | Nothing
Maybes are used a lot:
> import List exposing(head, tail, minimum, maximum) > head [1,2,3] == Just 1 True : Bool > head [] == Nothing True : Bool > tail [1,2,3] == Just [2,3] True : Bool > tail [] == Nothing True : Bool > List.maximum [6, -9, 4, 20, -1] == Just 20 True : Bool > List.maximum [] == Nothing True : Bool > maximum [6, -9, 4, 20, -1] == Just 20 True : Bool > maximum [] == Nothing True : Bool
Generally, a type variable can be instantiated with (or unified with) any concrete type whatsoever. That is, a function that takes a List a
as its parameter can be called with a List Float
argument. No problem. But sometimes not just any type will do:
\x -> \y -> x + y
makes sense only for Ints or Floats
\x -> \y -> x < y
makes sense only for Ints, Floats, Chars, Strings, and certain others
\x -> \y -> x ++ y
makes sense only for Lists or Strings
So how to we infer these types? if we have to give a type to \x -> \y -> x ++ y
we CANNOT say:
a -> a -> a
, because that allows too many types for a
String -> String -> String
, because that would exclude lists
List a -> List a -> List a
, because that would exclude strings
Elm’s answer is to introduce special type variables:
appendable
, which can be instantiated only with the type String
or any type with the form List a
.
number
, which can be instantiated only with Int
or Float
comparable
, which can be instantiated only with numbers, characters, strings, lists of comparable things, and tuples of comparable things with 6 or less elements.
If you are coming to Elm from Haskell-land, you might think thatnumber
,comparable
, andappendable
are type classes. Well they are not. They are simply type variables that the compiler has intimate knowledge of and are treated very special. They are a far weaker concept than type classes. Elm does not have type classes. You cannot create your own special type variables.
Also, you might run into the type variablecompappend
which either is or is not one of these special variables: even the official Elm FAQ seems conflicted on this question. Sigh.
\(x, y) -> (x+1, y+1)
? (Use the REPL)
At the language level, Elm programs are made up of modules. Each module can export only certain things and import certain things. If you don’t start your file with a module
declaration, a module named Main
is assumed. Learn by example. Here is a module that contains three values (all functions) but exports only one of them:
module Anagrams exposing (anagrams) import String exposing (toList, fromList) import List exposing (concatMap, map, foldr) insertEverywhere : a -> List a -> List (List a) insertEverywhere x xs = case xs of [] -> [[x]] (y::ys) -> (x::y::ys) :: map ((::)y) (insertEverywhere x ys) permutations : List a -> List (List a) permutations = foldr (concatMap << insertEverywhere) [[]] anagrams : String -> List String anagrams s = s |> toList |> permutations |> map fromList
And here is an app that uses the module:
import Anagrams exposing (anagrams) import Html exposing (Html, pre, text) main = pre [] [anagrams "aeginrst" |> String.join "\n" |> text]
WHat do you have to explicitly import? Good question. There are a few imports that are implicitly done for you at the top of every Elm file. These imports as of Elm 0.18 are:
import Basics exposing (..) import List exposing ( List, (::) ) import Maybe exposing ( Maybe( Just, Nothing ) ) import Result exposing ( Result( Ok, Err ) ) import String import Tuple import Debug import Platform exposing ( Program ) import Platform.Cmd exposing ( Cmd, (!) ) import Platform.Sub exposing ( Sub )
In addition to the modules you write yourself, there are hundreds and hundreds of standard modules and community-contributed modules stored in a global package repository. (Just FYI, the modules in the default import list, come from a very important package called elm-lang/core
.) You grab these modules via the Elm package manager, which we’ll cover pretty soon. But first, more about Elm itself.
Elm predefines the following infix operators:
Operators | Prec | Assoc | Description | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
<< | 9 | R | composition: (f << g)x == f(g(x))
|
To use an infix operator in the prefix position, enclose it in parentheses:
> (*) 3 7 21 : number > times3 = (*) 3 <function> : number -> number > times3 50 150 : number
You can create your own infix operators, too. Set the precedence and associativity with the infixl
(left associative), infixr
(right associative) or infix
(non-associative) functions. Example:
import Html exposing (div, text) (<-*->) x y = 2 * x + y infixr 7 <-*-> main = div [] [text <| toString <| 8 <-*-> 2 ^ 3 <-*-> 5 + 2]
The above script produces the value 39. We made the new operator right associative and placed its precedence lower than expoentation but higher than addition. The evaluation proceeds like so:
8 <-*-> 2 ^ 3 <-*-> 5 + 2 = 8 <-*-> 8 <-*-> 5 + 2 = 8 <-*-> 21 + 2 = 37 + 2 = 39
Elm’s let
-expression lets you bind local variables to compose a more complex expression. Example:
let x = 3 * 8 y = 4 ^ 2 in 2 * x + y
It’s pretty much shorthand for:
(\x -> \y -> 2 * x + y)(3 * 8)(4 ^ 2)
Elm wants to be a pure functional language. This means: Every function should return the same value for the same arguments no matter when it is called. So there shouldn't be any global state. There should be no side effects, no mutable variables, no assignments, no loops.
Hold on! Why would we ever care about this? Well, it turns out that pure functions have lots of advantages:
- They are easier to reason about and prove correct
- They are easier to to compose
- They are easier to to test
- They are easier to to debug
- They are easier to to parallelize
Okay, now that we know pure functions are good, how do we program in such a way? You might have to change your thinking:
Avoid | Alternative |
---|---|
While loops | Tail recursion |
For loops through sequences | Map, filter, reduce, or other functions on lists |
For loops over numbers | Functions on ranges, or tail recursion |
Adding elements to a data structure | Return a new data structure, just like the old one, except with a new element |
Modifying a data structure | Return a new data structure, just like the old one, except with the modification made |
Modifying the DOM | The DOM is just a data structure, so see above, and use a Virtual DOM that backs the real DOM |
Throwing exceptions or setting global error codes | Chain maybes and results |
Asking for a random number, the current time, or the current position of the mouse | Don’t ask for these things! We cannot make pure functions that return these! But the callbacks for these things can be pure |
We’ll look at the basics first. Then we’ll get into the interactive “effects” after that.
Here’s a typical solution using a while loop that determines how many years you need to grow $1000 into $2000 at a 3.5% annual interest rate:
years = 0 amount = 1000 while amount < 2000: amount = amount * 1.035; years = years + 1 return years
Elm doesn’t have assignable, mutable variables, so we have to create a function that takes us from one (years, amount)
pair to the next one. Here’s the progression we need to capture in a function:
yearsNeeded(0, 1000) = yearsNeeded(1, 1035.0) = yearsNeeded(2, 1071.225) = yearsNeeded(3, 1108.7178749999998) = yearsNeeded(4, 1147.5230006249997) = yearsNeeded(5, 1187.6863056468746) = yearsNeeded(6, 1229.2553263445152) = yearsNeeded(7, 1272.279262766573) = yearsNeeded(8, 1316.809036963403) = yearsNeeded(9, 1362.897353257122) = yearsNeeded(10, 1410.598760621121) = yearsNeeded(11, 1459.9697172428603) = yearsNeeded(12, 1511.0686573463602) = yearsNeeded(13, 1563.9560603534826) = yearsNeeded(14, 1618.6945224658543) = yearsNeeded(15, 1675.348830752159) = yearsNeeded(16, 1733.9860398284845) = yearsNeeded(17, 1794.6755512224813) = yearsNeeded(18, 1857.489195515268) = yearsNeeded(19, 1922.5013173583022) = yearsNeeded(20, 1989.7888634658427) = yearsNeeded(21, 2059.431473687147) = 21
We want a function (years, amount)
and make a recursive call with (years + 1, amount * 1.035)
. But if amount
is already over 2000, we have our answer and return years
.
-- Here's the function yearsNeeded years amount = if amount > 2000 then years else yearsNeeded (years + 1) (amount * 1.035) -- Call it to solve our initial problem yearsNeeded 0 1000
Another example. How many steps are there in the Collatz Sequence for a given number?
def collatz(n): count = 0 while n > 1: n = even(n) ? n / 2 : 3 * n + 1 count = count + 1 return count
Our first inclination might be to simply write
-- Inefficient collatz n = if n == 1 then 0 else collatz(if n % 2 == 0 then n // 2 else 3 * n + 1) + 1
but this is inefficient because it is not tail-recursive. Instead, let’s use the technique from the previous example:
collatz n = let c count n = if n == 1 then count else c (count+1) (if n % 2 == 0 then n // 2 else 3 * n + 1) in c 0 n
while
-loops, you also replace for
-loops with this technique. First give a program in C that runs through all the integers in the range 0 to 300 (inclusive) and sums up those and only those values evenly divisible by 3 or 7. Then, write the same program in Elm, using tail recursion.
Most for-loops over lists iterate over each list item or each list index (or both).
So how do we do functional programming on the web? Surely interaction requires state? Right?
Here’s how Elm does it. Up to now, we’ve only seen examples where main
has the type Html.Html a
. But it can also have the type Platform.Program a b c
. The easiest ways to make objects of type Program a b c
are with the functions Html.beginnerProgram
and Html.program
. Here’s a “beginner program” the computes a BMI from two text fields, as you type:
import Html exposing (Html, body, input, text, h1, p) import Html.Attributes exposing (style, value) import Html.Events exposing (onInput) type alias Model = { weight: String, height: String } type Msg = ChangeWeight String | ChangeHeight String bmi pounds inches = let kilosPerPound = 0.453592 metersPerInch = 0.0254 kilos = pounds * kilosPerPound meters = inches * metersPerInch in kilos / (meters * meters) bmiMessage weightString heightString = case (String.toFloat weightString, String.toFloat heightString) of (Ok w, Ok h) -> "The BMI is " ++ toString (bmi w h) (Err s, _) -> s (_, Err s) -> s main = Html.beginnerProgram { model = model, view = view, update = update } model : Model model = { weight = "0", height = "0"} update : Msg -> Model -> Model update msg model = case msg of ChangeWeight w -> { model | weight = w } ChangeHeight h -> { model | height = h } view : Model -> Html Msg view model = body [style [("textAlign", "center")]] [ h1 [] [text "Find Your BMI"] , p [] [text "Weight in pounds ", input [onInput ChangeWeight, value model.weight] []] , p [] [text "Height in inches ", input [onInput ChangeHeight, value model.height] []] , p [] [text <| bmiMessage model.weight model.height] ]
The beginnerProgram
function creates a program out of three parts:
All the functions in the code are pure. They just get called at times you can’t predict.
beginnerProgram
to program
. Read about subscriptions in the Elm Tutorial and the Elm Guide.
Elm programs are made up of modules. Where can you find modules? Thousands of these have been bundled up into pacakges which you can install with the Elm package manager. There are many hundreds of packages out there. Here are few of them:
Package | Modules |
---|---|
elm-lang/core | Array Basics Bitwise Char Color Date Debug Dict Json.Decode Json.Encode List Maybe Platform Platform.Cmd Platform.Sub Process Random Regex Result Set String Task Time Tuple |
elm-lang/virtual-dom | VirtualDom |
elm-lang/html | Html Html.Attributes Html.Events Html.Keyed Html.Lazy |
elm-lang/svg | Svg Svg.Attributes Svg.Events Svg.Keyed Svg.Lazy |
elm-lang/http | Http Http.Progress |
elm-lang/websocket | WebSocket WebSocket.LowLevel |
elm-community/basics-extra | Basics.Extra |
elm-community/webgl | WebGL WebGL.Settings WebGL.Settings.Blend WebGL.Settings.DepthTest WebGL.Settings.StencilTest WebGL.Texture |
elm-community/elm-test | Expect Fuzz Test Test.Runner |
elm-community/linear-algebra | Math.Matrix4 Math.Vector2 Math.Vector3 Math.Vector4 |
mgold/elm-date-format | Date.Format Date.Local Time.Format |
Make sure to browse the entire package repository.
Finally, just for fun, here are partial contents of some of the more popular modules:
(==): a -> a -> Bool -- equality, may give runtime error (/=): a -> a -> Bool -- inequality, may give runtime error (<): comparable -> comparable -> Bool (>): comparable -> comparable -> Bool (<=): comparable -> comparable -> Bool (>=): comparable -> comparable -> Bool (<): comparable -> comparable -> Bool max: comparable -> comparable -> Bool min: comparable -> comparable -> Bool type Order = LT | EQ | GT compare: comparable -> comparable -> Order not: Bool -> Bool -> Bool (&&): Bool -> Bool -> Bool -- logical and (short circuit) (||): Bool -> Bool -> Bool -- logical or (short circuit) xor: Bool -> Bool -> Bool (+): number -> number -> number (-): number -> number -> number (*): number -> number -> number (/): Float -> Float -> Float -- division (promotes Int args to Float) (^): number -> number -> number -- exponentiation (//): Int -> Int -> Int -- integer division (rem): Int -> Int -> Int -- remainder (%): Int -> Int -> Int -- modulo negate: number -> number abs: number -> number sqrt: Float -> Float clamp: number -> number -> number -> number -- low hi value logBase : Float -> Float -> Float -- base value e : Float pi : Float cos : Float -> Float sin : Float -> Float tan : Float -> Float acos : Float -> Float asin : Float -> Float atan : Float -> Float -- don't use this atan2 : Float -> Float -> Float -- y x round : Float -> Int floor : Float -> Int ceiling : Float -> Int truncate : Float -> Int -- round toward zero toFloat : Int -> Float degrees : Float -> Float radians : Float -> Float turns : Float -> Float toPolar : (Float, Float) -> (Float, Float) fromPolar : (Float, Float) -> (Float, Float) isNaN : Float -> Bool isInfinite : Float -> Bool toString : a -> String (++) : appendable -> appendable -> appendable identity : a -> a always : a -> b -> a (<|) : (a -> b) -> a -> b (|>) : a -> (a -> b) -> b (<<) : (b -> c) -> (a -> b) -> a -> c (>>) : (a -> b) -> (b -> c) -> a -> c flip : (a -> b -> c) -> b -> a -> c curry : ((a, b) -> c) -> a -> b -> c uncurry : (a -> b -> c) -> (a, b) -> c type Never -- the type with no values never : Never -> a
Strings are enclosed in double quotes and are not lists of characters.
isEmpty: String -> Bool length: String -> Int reverse: String -> String repeat: Int -> String -> String cons: Char -> String -> String uncons: String -> Maybe (Char, String) fromChar: Char -> String append: String -> String -> String concat: List String -> String split: String -> String -> List String -- sep, stringToSplit join: String -> List String -> String -- sep, stringToJoin words: String -> List String lines: String -> List String slice: Int -> Int -> String -> String -- start, end, stringToSlice left: Int -> String -> String right: Int -> String -> String dropLeft: Int -> String -> String dropRight: Int -> String -> String contains: String -> String -> Bool startsWith: String -> String -> Bool -- sub, largerString endsWith: String -> String -> Bool -- sub, largerString indexes: String -> String -> List Int indices: String -> String -> List Int toInt: String -> Result String Int toFloat: String -> Result String Float toList: String -> List Char fromList: List Char -> String toUpper: String -> String toLower: String -> String pad: Int -> Char -> String -> String -- len, padChar, stringToPad padLeft: Int -> Char -> String -> String -- len, padChar, stringToPad padRight: Int -> Char -> String -> String -- len, padChar, stringToPad trim: String -> String trimLeft: String -> String trimRight: String -> String map: (Char -> Char) -> String -> String filter: (Char -> Bool) -> String -> String foldl: (Char -> b -> b) -> b -> String -> b foldr: (Char -> b -> b) -> b -> String -> b any: (Char -> Bool) -> String -> Bool all: (Char -> Bool) -> String -> Bool
isEmpty : List a -> Bool length : List a -> Int reverse : List a -> List a member : a -> List a -> Bool head : List a -> Maybe a tail : List a -> Maybe (List a) filter : (a -> Bool) -> List a -> List a take : Int -> List a -> List a drop : Int -> List a -> List a singleton : a -> List a repeat : Int -> a -> List a range : Int -> Int -> List Int (::) : a -> List a -> List a append : List a -> List a -> List a concat : List (List a) -> List a intersperse : a -> List a -> List a partition : (a -> Bool) -> List a -> (List a, List a) unzip : List (a, b) -> (List a, List b) map : (a -> b) -> List a -> List b map2 : (a -> b -> result) -> List a -> List b -> List result map3 : (a -> b -> c -> result) -> List a -> List b -> List c -> List result map4 : (a -> b -> c -> d -> result) -> List a -> List b -> List c -> List d -> List result map5 : (a -> b -> c -> d -> e -> result) -> List a -> List b -> List c -> List d -> List e -> List result filterMap : (a -> Maybe b) -> List a -> List b concatMap : (a -> List b) -> List a -> List b indexedMap : (Int -> a -> b) -> List a -> List b foldr : (a -> b -> b) -> b -> List a -> b foldl : (a -> b -> b) -> b -> List a -> b sum : List number -> number product : List number -> number maximum : List comparable -> Maybe comparable minimum : List comparable -> Maybe comparable all : (a -> Bool) -> List a -> Bool any : (a -> Bool) -> List a -> Bool scanl : (a -> b -> b) -> b -> List a -> List b sort : List comparable -> List comparable sortBy : (a -> comparable) -> List a -> List a sortWith : (a -> a -> Order) -> List a -> List a
type Maybe a = Just a | Nothing withDefault : a -> Maybe a -> a map : (a -> b) -> Maybe a -> Maybe b map2 : (a -> b -> value) -> Maybe a -> Maybe b -> Maybe value map3 : (a -> b -> c -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe value map4 : (a -> b -> c -> d -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe value map5 : (a -> b -> c -> d -> e -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe e -> Maybe value andThen : (a -> Maybe b) -> Maybe a -> Maybe b
type Result error value = Ok value | Err error map : (a -> value) -> Result x a -> Result x value map2 : (a -> b -> value) -> Result x a -> Result x b -> Result x value map3 : (a -> b -> c -> value) -> Result x a -> Result x b -> Result x c -> Result x value map4 : (a -> b -> c -> d -> value) -> Result x a -> Result x b -> Result x c -> Result x d -> Result x value map5 : (a -> b -> c -> d -> e -> value) -> Result x a -> Result x b -> Result x c -> Result x d -> Result x e -> Result x value andThen : (a -> Result x b) -> Result x a -> Result x b withDefault : a -> Result x a -> a toMaybe : Result x a -> Maybe a fromMaybe : x -> Maybe a -> Result x a mapError : (x -> y) -> Result x a -> Result y a