All possible combinations of colors for Mastermind Just started out with CIS 194 on Haskell and wonder what the more idiomatic way to solve this problem would have been.

This code is supposed to generate all possible combinations of 6 given colors up to a given length. It works, but I think my solution is not that elegant. The suggested solution included this hint:


  Hint: This exercise is a bit tricky. Try using a helper function that
  takes in all the codes of length n − 1 and uses it to produce all
  codes of length n. You may find the concatMap function helpful.


But I could not figure a way so I did it like this:

-- Mastermind -----------------------------------------

-- A peg can be one of six colors
data Peg = Red | Green | Blue | Yellow | Orange | Purple
         deriving (Show, Eq, Ord)

-- A code is defined to simply be a list of Pegs
type Code = [Peg]

-- List containing all of the different Pegs
colors :: [Peg]
colors = [Red, Green, Blue, Yellow, Orange, Purple]

---- Exercise 6 ----

-- Extends a given Code by prepending a Peg: extendCode [Red, Green] Blue -&gt; [Blue, Red, Green]
extendCode :: Code -&gt; Peg -&gt; Code
extendCode code peg = peg : code

-- Extends a given Code by all possible Pegs, turning the Code into a list of Codes
fullyExtendCode :: Code -&gt; [Code]
fullyExtendCode code = map (\color -&gt; extendCode code color) colors

-- Extends a given number of Codes by all possible Pegs
fullyExtendCodes :: [Code] -&gt; [Code]
fullyExtendCodes [] = map (:[]) colors
fullyExtendCodes codes = concatMap fullyExtendCode codes

-- Helper function for allCodes which includes the Codes list from previous recursions
aCodes :: [Code] -&gt; Int -&gt; [Code]
aCodes codes 0 = codes
aCodes codes l = aCodes (fullyExtendCodes codes) (l - 1)

allCodes :: Int -&gt; [[Peg]]
allCodes l = aCodes [] l


Any suggestions from more experiened Haskell programmers?
You are looking for all of the variations (with repetitions) of a given list!

variations :: Int -&gt; [a] -&gt; [[a]]
variations n cs
 | n &lt;= 0 = []
 | n == 1 = map pure cs --  map (\c -&gt; [c]) cs
 | otherwise = variations (n-1) cs &gt;&gt;= \ps -&gt; map (:ps) cs


Where the right side of the last line could be any of the following:

variations (n-1) cs &gt;&gt;= flip map cs . flip (:)

concatMap (\ps -&gt; map (:ps) cs) (variations (n-1) cs)

concat . map (\ps -&gt; map (\c -&gt; c:ps) cs) $ variations (n-1) cs


Pick your poison. They are all equivalent. as &gt;&gt;= f is the same as concatMap f as, because for the list monad join is concat, and fmap is map, so bind = (&gt;&gt;=) is join . fmap = concat . map.

Let me demonstrate how variations is evaluated for cs = "12", n=2.

Since variations 1 "12" = ["1", "2"], we know that variations 2 "12" = concat . map (\ps -&gt; map (\c -&gt; c:ps) "12") $ ["1", "2"]  Let's see what each mapping does.

map (\c -&gt; c:ps) "12" simply takes a character c from "12" and appends it to the beginning of ps. But what's ps? Let's look at the other map.

The outer map simply applies the inner function \ps -&gt; map (\c -&gt; c:ps) "12" to each element of ["1","2"].
As we have seen, this inner function simply takes the list cs and maps the element c to c:ps. In this case it turns "1" into ["11","21"], and "2" into ["12","22"].

So all in all we have the following.

variations 2 "12" = concat . map (\ps -&gt; map (\c -&gt; c:ps) "12") $ variations 1 "12"
                  = concat . map (\ps -&gt; map (\c -&gt; c:ps) "12") $ ["1", "2"]
                  = concat [["11","21"],["12","22"]] 
                  = ["11","21","12","22"]


Hope this helps.