Fork me on GitHub
Elm Copenhagen, February 2018 Meetup @MobileLife

Updated

With some input from Basile Henry, also attended the Meetup, I have made a few modifications to the code in order to support m :: * -> * ((a -> b) -> m a -> m b) as good as Elm allows us to do it, but without transitioning to a signature hell as seen in elm-data, see References for more information on that library.

Obviously not really monads

As a fellow Coding Pirates volunteer, Hans Bugge Grathwohl , pointed out:

1
2
3
4
5
6
7
8
9
10
11
odd :
  { fmap : (Int -> String) -> Float -> List Char
  , join : List Char -> String
  }
odd =
  { fmap = \f x -> String.toList (f (truncate x))
  , join = String.fromList
  }
			    
weird : String
weird = 4.5 |> bind odd toString

this can’t really be considered monads as there is no relation between a and ma as well as mb and mmb.

Code Snippet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
module Main exposing (..)

import Html exposing (br, div, text)


bind :
    { monad
        | fmap : (a -> mb) -> ma -> mmb
        , join : mmb -> mb
    }
    -> ((a -> mb) -> ma -> mb)
bind { fmap, join } f m =
    join (fmap f m)


maybe :
    { fmap : (a -> b) -> Maybe a -> Maybe b
    , join : Maybe (Maybe c) -> Maybe c
    }
maybe =
    { fmap = Maybe.map
    , join =
        \mm ->
            case mm of
                Nothing ->
                    Nothing

                Just m ->
                    m
    }


list :
    { fmap : (a -> b) -> List a -> List b
    , join : List (List c) -> List c
    }
list =
    { fmap = List.map
    , join = List.concat
    }


result :
    { fmap : (a -> b) -> Result c a -> Result c b
    , join : Result e (Result e d) -> Result e d
    }
result =
    { fmap = Result.map
    , join =
        \mm ->
            case mm of
                Result.Err e ->
                    Result.Err e

                Result.Ok m ->
                    m
    }


foo : Maybe Int
foo =
    Just 42
        |> bind maybe (\x -> Just (x + 1))
        |> bind maybe (\x -> Just (x + 1))
        |> bind maybe (\x -> Just (x + 1))


bar : List String
bar =
    [ 0, 1, 2, 4, 5 ]
        |> bind list (\x -> [ x + 10, x + 20 ])
        |> bind list (\x -> [ x + 100 ])
        |> bind list (\x -> [ toString x ])


baz : Result String Int
baz =
    Result.Ok " 42 "
        |> bind result String.toInt
        |> bind result (\x -> Result.Ok (x + 1))
        |> bind result (\x -> Result.Ok (x + 1))
        |> bind result (\x -> Result.Ok (x + 1))


qux : Result String Int
qux =
    Result.Ok " 42 "
        |> bind result (String.trim >> Result.Ok)
        |> bind result String.toInt
        |> bind result (\x -> Result.Ok (x + 1))
        |> bind result (\x -> Result.Ok (x + 1))
        |> bind result (\x -> Result.Ok (x + 1))


main =
    div []
        [ text <| "-- Maybe: " ++ toString foo
        , br [] []
        , text <| "-- Lists: " ++ toString bar
        , br [] []
        , text <| "-- Result: " ++ toString baz
        , br [] []
        , text <| "-- Result: " ++ toString qux
        ]

Code output:

-- Maybe: Just 45
-- Lists: ["110","120","111","121","112","122","114","124","115","125"]
-- Result: Err "could not convert string ' 42 ' to an Int"
-- Result: Ok 45

References: