リスト内包表記の舞台裏

以下のリスト内包表記が実際にはどう動いているのかを確認する。

[ (x,y) | x<-[1..3], y<-[1..3] ]

内包表記をdo記法に変形

do x<-[1..3]; y<-[1..3]; return(x,y)

do記法を>>=演算子に変形

[1..3]>>=(\x-> [1..3]>>=(\y-> return(x,y)))

m>>=f を concatMap f m に、return x を [x] に変形

concatMap (\x-> concatMap (\y-> [(x,y)]) [1..3]) [1..3]

念のためチェック

Prelude> [ (x,y) | x<-[1..3], y<-[1..3] ]
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)]
Prelude> concatMap (\x-> concatMap (\y-> [(x,y)]) [1..3]) [1..3]
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)]