Elm
Functional Programming
Maybe Type
Type Conversion
Elm Techniques

Converting List Maybe a to Maybe List a in Elm

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Converting List (Maybe a) to Maybe (List a) is a common operation in functional programming called sequence. It takes a list of optional values and returns Nothing if any value is Nothing, or Just of the complete list if all values are Just. Elm does not include sequence in its core library, but it can be implemented with List.foldr or by using packages like elm-community/maybe-extra.

The Transformation

elm
-- Input:  [Just 1, Just 2, Just 3]  → Just [1, 2, 3]
-- Input:  [Just 1, Nothing, Just 3] → Nothing
-- Input:  []                         → Just []

If every element is Just, unwrap them all into a single Just list. If any element is Nothing, the entire result is Nothing. This is an all-or-nothing operation.

Implementation with foldr

elm
1sequence : List (Maybe a) -> Maybe (List a)
2sequence =
3    List.foldr
4        (\maybeElem acc ->
5            case ( maybeElem, acc ) of
6                ( Just elem, Just list ) ->
7                    Just (elem :: list)
8
9                _ ->
10                    Nothing
11        )
12        (Just [])

How it works:

  1. Start with Just [] as the accumulator
  2. For each element, if both the element and accumulator are Just, prepend the unwrapped value
  3. If either is Nothing, the result becomes Nothing and stays Nothing

Usage Examples

elm
1-- All values present
2sequence [ Just "a", Just "b", Just "c" ]
3-- → Just ["a", "b", "c"]
4
5-- One missing value
6sequence [ Just 1, Nothing, Just 3 ]
7-- → Nothing
8
9-- Empty list
10sequence []
11-- → Just []
12
13-- Single element
14sequence [ Just 42 ]
15-- → Just [42]
16
17sequence [ Nothing ]
18-- → Nothing

Implementation with Maybe.andThen

elm
1sequence2 : List (Maybe a) -> Maybe (List a)
2sequence2 list =
3    List.foldr
4        (\maybeElem accMaybe ->
5            Maybe.andThen
6                (\acc ->
7                    Maybe.map (\elem -> elem :: acc) maybeElem
8                )
9                accMaybe
10        )
11        (Just [])
12        list

This version uses Maybe.andThen (equivalent to >>= or bind in Haskell) and Maybe.map instead of explicit pattern matching.

Traverse: Map and Sequence in One Step

traverse applies a function that returns Maybe to each element, then sequences the result. It is more efficient than List.map f >> sequence because it combines both operations:

elm
1traverse : (a -> Maybe b) -> List a -> Maybe (List b)
2traverse f =
3    List.foldr
4        (\elem acc ->
5            case ( f elem, acc ) of
6                ( Just val, Just list ) ->
7                    Just (val :: list)
8
9                _ ->
10                    Nothing
11        )
12        (Just [])
13
14
15-- Usage: parse a list of strings to integers
16parseAll : List String -> Maybe (List Int)
17parseAll =
18    traverse String.toInt
19
20parseAll [ "1", "2", "3" ]
21-- → Just [1, 2, 3]
22
23parseAll [ "1", "abc", "3" ]
24-- → Nothing

Keeping Only Just Values (Alternative)

If you want to keep the Just values and discard Nothing instead of failing entirely:

elm
1-- List.filterMap does this — built into Elm core
2List.filterMap identity [ Just 1, Nothing, Just 3 ]
3-- → [1, 3]
4
5-- Custom version
6catMaybes : List (Maybe a) -> List a
7catMaybes list =
8    List.filterMap identity list
9
10catMaybes [ Just "a", Nothing, Just "c" ]
11-- → ["a", "c"]

Use sequence when all values must be present (validation, parsing). Use List.filterMap when missing values are acceptable.

Practical Example: Form Validation

elm
1type alias FormData =
2    { name : String
3    , email : String
4    , age : String
5    }
6
7type alias ValidatedData =
8    { name : String
9    , email : String
10    , age : Int
11    }
12
13validateName : String -> Maybe String
14validateName name =
15    if String.length name > 0 then
16        Just name
17    else
18        Nothing
19
20validateEmail : String -> Maybe String
21validateEmail email =
22    if String.contains "@" email then
23        Just email
24    else
25        Nothing
26
27validateForm : FormData -> Maybe ValidatedData
28validateForm form =
29    Maybe.map3 ValidatedData
30        (validateName form.name)
31        (validateEmail form.email)
32        (String.toInt form.age)
33
34-- Or with sequence for a list of validations:
35validateFields : FormData -> Maybe (List String)
36validateFields form =
37    sequence
38        [ validateName form.name
39        , validateEmail form.email
40        , Maybe.map String.fromInt (String.toInt form.age)
41        ]

Comparison with Haskell and Other Languages

haskell
1-- Haskell: sequence is built-in
2sequence [Just 1, Just 2, Just 3]  -- Just [1, 2, 3]
3
4-- It works for any Traversable + Applicative
5sequence [Right 1, Right 2]  -- Right [1, 2]
6sequence [Right 1, Left "err"]  -- Left "err"
python
1# Python equivalent
2def sequence_maybe(lst):
3    result = []
4    for item in lst:
5        if item is None:
6            return None
7        result.append(item)
8    return result

Elm does not have type classes, so sequence must be implemented specifically for each wrapper type (Maybe, Result, etc.) rather than generically.

Common Pitfalls

  • Using List.filterMap when sequence is needed: List.filterMap identity silently drops Nothing values. If you need to know whether all values were present (e.g., form validation), use sequence which returns Nothing when any value is missing.
  • Performance with large lists: The foldr-based sequence processes the entire list even if the first element is Nothing. For large lists where early failure is common, consider a recursive implementation that short-circuits on the first Nothing.
  • Confusing sequence with traverse: sequence unwraps a List (Maybe a) into Maybe (List a). traverse f maps a function a -> Maybe b over a list and then sequences. Use traverse when you need both operations — it avoids creating an intermediate List (Maybe b).
  • Forgetting the empty list case: sequence [] returns Just [], not Nothing. This is the correct mathematical behavior (vacuous truth), but it can be surprising in validation contexts where an empty input might be considered invalid.
  • Not using elm-community/maybe-extra: The Maybe.Extra package provides combine (equivalent to sequence), traverse, and other utilities. Installing it avoids reimplementing these functions and provides well-tested, optimized versions.

Summary

  • sequence converts List (Maybe a) to Maybe (List a) — returns Nothing if any element is Nothing
  • Implement with List.foldr and pattern matching on (maybeElem, acc)
  • traverse combines mapping and sequencing in one pass for better efficiency
  • Use List.filterMap identity when you want to discard Nothing values instead of failing
  • The elm-community/maybe-extra package provides combine (sequence) and traverse ready to use
  • sequence [] returns Just [] (empty list is valid) — add an explicit length check if empty input should fail

Course illustration
Course illustration

All Rights Reserved.