I’m working my way through Graham Hutton’s Programming in Haskell. Despite the fact that I’m not a mathematician (and I still believe that Haskell has syntax only a mathematician could love), it’s an accessible computer science text. It’s nicely clear, too, despite its relatively short length.
Most of the exercises are good too… except for one stumper in the list comprehensions chapter.
Given a list comprehension, defined in the function two_gens:
two_gens :: [(Integer, Integer)]
two_gens = [(x, y) | x <- [1, 2, 3], y <- [4, 5, 6]]
… which uses two generators in a single comprehension and produces:
[(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]
… write two_comps which expresses the same concept using two comprehensions, each with a single generator. The book gives the hint to use concat and nest one comprehension within the other.
I stared at this problem for a long time, and eventually decided that two_gens felt like the most natural expression of the concept. After more thinking, I came up with a horrible hack:
two_comps :: [(Integer, Integer)]
two_comps = concat [ zip [x, x, x] [ y | y <- [4 .. 6] ] | x <- [1, 2, 3] ]
Did I miss the point of the exercise? Is there a better way to rewrite the expression? I’m both proud and horrified at my answer.
Update:
Paul McCann and Adam Turoff both responded to me privately with a more elegant solution:
[[(x, y) | y <- [4..6]] | x <- [1..3]]
I’m convinced that I tried that very code, but I obviously mistyped it, as I remember the error message warned that ghci didn’t understand x. (Now there’s a legitimate gripe for learning Haskell. A simple syntax error can lead to strange output from the type checker that a novice may not have the framework to understand.)
I still find the single comprehension form much more comprehensible.

I haven't read Hutton's book, but as introductions to Haskell go, I think the point of this exercise was not to present a particularly beautiful alternative way of expressing this computation.
Rather, converting a list comprehension to the equivalent concat/map expressions can enable further program transformations (eg, fusion with a neighboring fold), so it is a good thing to be able to do by hand.
Here is my solution:
http://davidtran.doublegifts.com/blog/?p=119