All possible combinations of a product type

-- Features
type Symbol = Triangle | Circle | Wave
type Shading = Striped | Empty | Solid
type Color = Red | Green | Purple
type Number = One | Two | Three

-- Card
type Card = Card Symbol Shading Color Number

-- Deck
type alias Deck = List Card

-- A deck has 81 cards, which is all the possible combinations to combine
-- the four features in a card. Each card is unique and exists only once in the deck.
-- Now the question: How can I generate the deck? 
-- (it does not have to be random (yet)).
-- extending the question: How can I generally generate all possible
-- combinations of a product type?
deck : Deck
deck =
    let
        listSymbol = [Triangle,Circle,Wave]
        listShading = [Striped,Empty,Solid]
        listColor = [Red,Green,Purple]
        listNumber = [One,Two,Three]

        productApply : List a -> List (a -> b) -> List b
        productApply l1 =
            List.map (\b -> l1 |> List.map b)
            >> List.concat
    in
    Card
    |> List.singleton
    |> productApply listSymbol
    |> productApply listShading
    |> productApply listColor
    |> productApply listNumber
1 Like

Hey thanks very much for your reply. Can you explain what productApply does a little, because I am not sure I understand this, well. Thanks!

Well, essentially what you are asking for is called a “cartesian product” (thats the mathematical term for it)

product : List a -> List b -> List (a,b)
product l1 l2 =
    l2
    |> List.map (\b -> l1 |> List.map (\a -> (a,b)))
    |> List.concat

with this we would get something like

listSymbol
    |> product listShading
    |> product listColor
    |> product listNumber
    |> List.map (\(number,(color,(shading,symbol))) ->
           Card symbol shading color number
       )

This got me thinking. Instead of returning a tuple I could immediately apply the value to the function Card.

productApply : List a -> List (a->b) -> List b
productApply l1 l2 =
    l2
    |> List.map (\b -> l1 |> List.map (\a -> b a))
    |> List.concat

Note that b is now a function.
At the end I removed l2 and replaced (\a -> b a) with b as that’s essentially the same. This last step is just how I like my code to look like, so you don’t have to do it.

Thanks. So the elements of l2 become b. So for every element b you map a function that takes every elements a from l1 and applies a to b. Then you flatten the result. but where is List (a -> b) coming from? And is List (a->b) a list of functions from a to b?

The constructor function Card is of type Symbol -> Shading -> Color -> Number -> Card.

So in the first step Card |> List.singleton is the List (a -> b) where

  • a equals Symbol
  • b equals Shading -> Color -> Number -> Card
1 Like

Possibly easier to read and debug https://ellie-app.com/7pRVnPVm8Z5a1

2 Likes

Another one using List.Extra https://ellie-app.com/7pSvFtsTcP3a1

1 Like

Thank you all. I think I understand now!

1 Like

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