drathier / elm-test-tables / Fuzz.Category

Fuzz.Category provides fuzz tests for common functions, like map and andThen.

Many Elm data structures share some similar functions. I'm sure you've noticed map and andThen. There's List.map, Set.map, Maybe.map and so on. Since this is a very common pattern in Elm, you should provide them wherever they make sense for your own data structures, and use the functions in this module to make sure they behave as expected.

Map

A common function for data structures in Elm is map : (a -> b) -> T a -> T b for some type T, such as List or Set. In mathematics, this is called a functor.

describe "List.map"
    [ mapv1 List.map Fuzz.list
    , mapv2 List.map Fuzz.list
    , mapv3 List.map Fuzz.list
    ]

Functors follow two laws: Identity: map identity = identity, and Composition: map (f << g) = map f << map g.

mapv1 : ((Basics.Float -> Basics.Float) -> la -> la) -> (Fuzzer Basics.Float -> Fuzzer la) -> Test

This function helps you test your T.map function, for every module T you can think of.

mapv2 : ((String -> String) -> la -> la) -> (Fuzzer String -> Fuzzer la) -> Test

Another version of the map test helper, with a new set of types.

mapv3 : ((List Char -> List Char) -> la -> la) -> (Fuzzer (List Char) -> Fuzzer la) -> Test

A third version of the map test helper, with a third set of types.

AndThen (also known as concatMap)

Another common function for data structures in Elm is andThen : (a -> T b) -> T a -> T b for some type T, such as List or Set. In mathematics, this is called a monad.

describe "List.andThen"
    [ andThenv1 List.singleton List.concatMap Fuzz.list
    , andThenv2 List.singleton List.concatMap Fuzz.list
    , andThenv3 List.singleton List.concatMap Fuzz.list
    ]

Monads follow three laws: Left identity: singleton a |> andThen f ≡ f a, Right identity: m |> andThen singleton ≡ m, and Associativity: (m |> andThen f) |> andThen g ≡ m |> andThen (\x -> f x |> andThen g), where m is anything with the same type as singleton a, and f and g are (a -> a) functions.

andThenv1 : (Basics.Float -> fa) -> ((Basics.Float -> fa) -> fa -> fa) -> (Fuzzer Basics.Float -> Fuzzer fa) -> Test

This function helps you test your T.andThen function, for every module T you can think of.

andThenv2 : (String -> fa) -> ((String -> fa) -> fa -> fa) -> (Fuzzer String -> Fuzzer fa) -> Test

Another version of the andThen test helper, with a new set of types.

andThenv3 : (List Char -> fa) -> ((List Char -> fa) -> fa -> fa) -> (Fuzzer (List Char) -> Fuzzer fa) -> Test

A third version of the map test helper, with a third set of types.

Confessions

Actually, the real laws aren't quite as strict as what I wrote above. In order to make the Elm type checker happy, I had to apply more constraints than the mathematical theory strictly requires. We're using endofunctors (a -> a) instead of covariant functors (a -> b), for example.