Skip to content

Instantly share code, notes, and snippets.

@ThomasLocke
Created July 30, 2015 14:53
Show Gist options
  • Save ThomasLocke/d69cf6253e6342a61418 to your computer and use it in GitHub Desktop.
Save ThomasLocke/d69cf6253e6342a61418 to your computer and use it in GitHub Desktop.
A Haskell noob trying to create sorta-kinda random passwords
import Control.Applicative
import System.Environment
import System.Exit
import System.Random
main = getArgs >>= parseArgs
-- Exit with success.
exit :: IO a
exit = exitWith ExitSuccess
-- Generate an x long password. The initial s String is appended to the
-- password.
generatePassword :: Int -> IO String -> IO String
generatePassword x s
| x < 1 = s
| otherwise = generatePassword (x -1) (liftA2 (:) randChar s)
-- Print the help text.
help :: String
help = "Usage: passgen OPTION\n" ++
" -l x outputs a random password of x length\n" ++
" -h this help text\n" ++
" -v version of the program"
-- Parse the commandline arguments and return one of:
-- help
-- version
-- actual program output
parseArgs :: [String] -> IO ()
parseArgs ("-l":n:[]) = generatePassword (read n :: Int) (return "") >>= putStrLn >> exit
parseArgs [] = putStrLn help >> exit
parseArgs ["-h"] = putStrLn help >> exit
parseArgs ["--help"] = putStrLn help >> exit
parseArgs ["-v"] = putStrLn version >> exit
parseArgs ["--version"] = putStrLn version >> exit
parseArgs (a:as) = putStrLn help >> exit
-- Yields a random char from the validChars list.
randChar :: IO Char
randChar = randNum >>= (\i -> return (validChars !! i))
-- Yields a random integer in the 0 .. length validChars range.
randNum :: IO Int
randNum = getStdRandom (randomR (0, (length validChars) - 1))
-- A list of valid characters
validChars :: String
validChars = ['a'..'z'] ++ ['0'..'9'] ++ ['A'..'Z']
-- Print the version
version :: String
version = "0.0.1"
@ThomasLocke
Copy link
Author

Constructive comments from #haskell IRC channel:

ThomasLocke: you can shorten "randNum >>= (\i -> return (validChars !! i))" to "fmap (validChars !!) randNum"
ThomasLocke: generatePassword could be a pure function which takes a random number generator instead.
ThomasLocke: "take 10 . filter notValidChar . randoms $ mkStdGen 1"
ThomasLocke: you could put "parseArgs _ = putStrLn help >> exit" as the last clause, and remove the other ones that print the help message
ThomasLocke: That's not entirely secure since filtering invalid characters might introduce some weird bias in the generated passwords
ThomasLocke: assuming >>= and fmap are implemented correctly (which they are for IO), "x >>= \y -> return (f y)" can always be reduced to "fmap f x"
:t state random
(MonadState s m, RandomGen s, Random a) => m a
aweinstock: Why use "state random" instead of randoms?
:t randoms
(RandomGen g, Random a) => g -> [a]
randoms just produces an infinite list
:t \size -> replicateM size (state random)
(MonadState s m, RandomGen s, Random a) => Int -> m [a]
:t randoms
(RandomGen g, Random a) => g -> [a]
:t randoms (mkStdGen 1) :: String
> randoms (mkStdGen 1) :: String
"\39335\14070\DC1\959535\514122\923211\263434\59659\367628\671400\744213\298...
> filter isAlpha $ randoms (mkStdGen 1)
"\39335\14070\70334\138983\37111\27202\139598\25181\165723\29657\11327\31112...
dot_Laptop: Don't use read :\

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment