I'm very new to Haskell, I've read most of learnyouahaskell.com and played around with some simple things, but this is probably the most 'complicated' bit of Haskell I've written so far. I have an implementation in PHP that does basically the same thing, but it is much much faster.. I'm guessing my bottleneck here is that randomRIO takes a long time to return a value, is there any way to increase the speed, or get an array of values instead of grabbing them one at a time?
That aside, any other tips and/or suggestions about improving my implementation would be very helpful!
module Main whereimport System.Environmentimport System.Exitimport System.Random (randomRIO)import Control.Monad (replicateM)import qualified Data.Map as Mimport Text.Regex.Posixmain :: IO ()main = do args <- getArgs let helpOnly = "-h" `elem` args if helpOnly then putStrLn usage else do password <- generatePassword ("-w" `elem` args) putStrLn password exitSuccessusage :: Stringusage = unlines ["","Usage: [ -w, --with-symbols ] [ -h, --help ]","","By default, generates a 16 character password that does not include symbols.","","-w, --with-symbols include symbols","-h, --help print a brief help message" ]symbols :: Stringsymbols = "!$%^&*()-_=+[{]};:@#~,<.>/?"randomReplace :: String -> String -> IO StringrandomReplace [] subject = return subjectrandomReplace (replacement:rs) subject = do randomIndex <- randomRIO (0, length subject - 1) :: IO Int let hash = zip [0 .. length subject - 1] subject (randomReplace rs . map snd . M.toList . M.insert randomIndex replacement . M.fromList) hashgeneratePassword :: Bool -> IO StringgeneratePassword withSymbols = do let passwordLength = 500 -- obviously you wouldn't use 500 as a default here, but I'm just benchmarking numDigits <- randomRIO (1, passwordLength) :: IO Int digits <- replicateM numDigits (randomRIO (1, 9) :: IO Int) numUppercase <- randomRIO (1, passwordLength) :: IO Int uppercaseLetters <- replicateM numUppercase (randomRIO ('A', 'Z') :: IO Char) p1 <- replicateM passwordLength (randomRIO ('a', 'z') :: IO Char) p2 <- randomReplace (concatMap show digits) p1 p3 <- randomReplace uppercaseLetters p2 password <- if withSymbols then do numSymbols <- randomRIO (1, passwordLength) :: IO Int symbolsToReplace <- replicateM numSymbols ((randomRIO (0, length symbols - 1) :: IO Int) >>= (\x -> return $ symbols !! x)) randomReplace symbolsToReplace p3 else return p3 if (password =~ "[a-z]" :: Bool) && (password =~ "[A-Z]" :: Bool) && (password =~ "[0-9]" :: Bool) -- we knows symbols are in there since it went last then return password else generatePassword withSymbols