漠然とした質問で申しわけないのですが
お力を貸していただけるとうれしいです

『Ruby初心者必見!?「ビンゴカード作成問題」のリファクタリング風景をお見せします #codeiq』
http://blog.jnito.com/entry/2015/03/06/090106
にあった『ビンゴカード作成問題』をHaskellで挑戦してみました

=================
■ルール■
①各列の値は以下の条件を満たすこと
 B:1~15のどれか
 I:16~30のどれか
 N:31~45のどれか
 G:46~60のどれか
 O:61~75のどれか
②毎回異なるカードを生成すること
③どの数値も重複しないこと
④各列はパイプ(|)で区切ること
⑤数字や"BINGO"の文字は右寄せで出力すること
⑥真ん中(FREEになる場所)はスペースを出力すること
=================

{-# OPTOINS -Wall -Werror #-}
{-# LANGUAGE OverloadedStrings, ViewPatterns #-}

import qualified Data.List as L
import qualified Data.Text as T
import qualified System.Random as R

type RangeUnit = (Int, Int)

header = "BINGO"
bingoSize = T.length header
numrange = 15
rangeUnit = take bingoSize $ zip [1,numrange+1..] [numrange, numrange*2..]

body = do
    nums <- mapM (createRondomRange bingoSize) rangeUnit
    return $ formatCell 2 2 nums

viewBingo = do
    b <- body
    return $ map formatline $ map appendSeparatedWord $ headerList : b
    where
        headerList = map (\x -> justifyRightSpace $ T.pack [x]) $ T.unpack header
        appendSeparatedWord = L.intersperse $ T.pack " | "
        formatline = foldl T.append T.empty

createRondomRange :: Int -> RangeUnit -> IO [Int]
createRondomRange range (start, end) = do
    gen <- R.newStdGen
    return $ take range . L.nub $ R.randomRs (start, end) gen

formatCell :: Int -> Int -> [[Int]] -> [[T.Text]]
formatCell rowIndex colIndex vals = 
    L.transpose $ mmap (justifyRightSpace . centerReplaced) vals
    where
        centerReplaced x = if centerVal == x then " " else T.pack $ show x
        centerVal = vals !! rowIndex !! colIndex

justifyRightSpace = T.justifyRight 2 ' ' 

mmap f = map (map f)

main = do
    b <- viewBingo
    mapM_ print b

■質問内容
・Haskellらしく書くにはこうしたほうがいい
・この便利関数を使えばこんな回りくどいことしなくていい
・そもそも書く上での考え方が悪(ry

などなど、ソースを改善するお力を貸していただきたいです

また①の数値の範囲について
import qualified Data.List.Split as S
ranges = S.splitEvery 15 [1..75]
で作成したrangesの格リストの値をシャッフルすることで
作りたかったのですが方法がわかりませんでした。
リストの要素をランダムに入れ替える関数はあるのでしょうか