import Control.Monad
import Control.Monad.Random
import Data.List

password :: MonadRandom m => [String] -> Int -> m String
password charSets n = do
  parts <- getPartition n
  chars <- zipWithM replicateM parts (uniform <$> charSets)
  shuffle (concat chars)
  where
    getPartition n = adjust <$> replicateM (k-1) (getRandomR (1, n `div` k))
    k = length charSets
    adjust p = (n - sum p): p

shuffle :: (Eq a, MonadRandom m) => [a] -> m [a]
shuffle [] = pure []
shuffle lst = do
  x <- uniform lst
  xs <- shuffle (delete x lst)
  return (x: xs)