New package: elm-list-cartesian

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).

:arrow_right: Here it is: elm-list-cartesian 1.0.2 :arrow_left:

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).

:information_source: Aside: Of course, List.Extra already had most of these functions already in the form of lift2 etc., but it was missing the Cartesian version of andMap – you’d have to define it yourself with cartesianAndMap = List.Extra.lift2 (|>)).

:books: 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.

13 Likes

I wonder why elm ecosystem packages should be ever prefixed with the elm-? :thinking:

Dunno about others, but I do it because these packages don’t only live in the Elm packages list, they also live in my GitHub repositories. From that perspective, it makes sense to me to organize my repositories that way.

3 Likes

So package names actually are package addresses here.

They are the “qualified” names of GitHub repos: author/project.

So eg. Janiczek/cmd-extra is something else than GlobalWebIndex/cmd-extra and for both you can be sure you’ll find the source on https://github.com/author/project-like URL.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.