Created
July 30, 2015 14:53
-
-
Save ThomasLocke/d69cf6253e6342a61418 to your computer and use it in GitHub Desktop.
A Haskell noob trying to create sorta-kinda random passwords
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 :\