Hello! Following a discussion with a Haskell/math-minded colleague, I’ve realized there are multiple List Applicative instances (andMap
+ succeed
pattern). Notably, zipping and “all combinations”.
The List.mapN
functions from elm/core
are zipping, and I’ve created a package with mapN
functions that instead give you all combinations (the Cartesian product).
Here it is: elm-list-cartesian 1.0.2
Motivating example, illustrating the difference:
List.map2 (+) [ 1, 2 ] [ 100, 200, 300 ]
--> [ 101, 202 ]
List.Cartesian.map2 (+) [ 1, 2 ] [ 100, 200, 300 ]
--> [ 101, 201, 301, 102, 202, 302 ]
I feel like it has the potential to free you from hard-to-read and hard-to-write nested List.concatMap
expressions or converting to/from a list of lists with List.Extra.cartesianProduct
:
colors = [Red, Green, Blue]
numbers = [1, 2, 3, 4]
players = [Human, NPC]
allCombinationsBefore : List (Color, Int, Player)
allCombinationsBefore =
colors
|> List.concatMap (\color ->
numbers
|> List.concatMap (\number ->
players
|> List.map (\player -> (color, number, player))
)
)
allCombinationsAfter : List (Color, Int, Player)
allCombinationsAfter =
List.Cartesian.map3 (\color number player -> (color, number, player))
colors
numbers
players
So, that’s it! Hopefully a nicer API for some not-so-common tasks (I’m glad the zipping List behaviour is the default, the need for it comes up much more often in FE work than “get all combinations” in my experience).
Aside: Of course,
List.Extra
already had most of these functions already in the form oflift2
etc., but it was missing the Cartesian version ofandMap
– you’d have to define it yourself withcartesianAndMap = List.Extra.lift2 (|>)
).
Haskell-y aside: Interestingly the zipping Applicative instance is not lawful in Elm (and we most likely can’t make it lawful without infinite lists):
[ identity ] -- using List.singleton as our `pure` |> List.Extra.andMap [1,2,3] -- or `List.Zip.andMap` from my pkg --> [1] ❌
We’d need a
pure
/singleton
/succeed
function that returns an infinite list (or somehow get around the zipping behaviour in a different way).I’ve remarked about this briefly in the documentation for
List.Zip.map5
.