Compare commits

...

2 Commits

@ -0,0 +1,264 @@
main = putStrLn "hello, world" -- putStrLn :: String -> IO()
-- IO actions are performed when they are the result of `main`
main = do
putStrLn "Hello, what's your name?" :: IO ()
name <- getLine
putStrLn ("Hey " ++ name ++ "!") :: IO ()
main = do
putStrLn "Enter a number:"
strResult <- getLine
let numResult=(read strResult) :: Int
putStrLn (show (numResult * 10))
-- main is always oftype main :: IO a, but we normally don't specify the type explicitly
-- Well, I/O actions will be performed if we type them out in GHCi...
-- In do-blocks, we can use `let` without `in`:
main = do
putStrLn "Name?"
name <- getLine
let bigName = map toUpper name
putStrLn $ "hey, " ++ bigName ++ "."
-- Example that continually reads line and prints out reversed words:
reverseWords :: String -> String
reverseWords = unwords . map reverse . words
main = do
line <- getLine
if null line
then return ()
else do
putStrLn $ reverseWords line
main
-- putStr - returns IO that prints string to terminal, no \n
-- putChar - returns IO that prints char to terminal
-- print - for some value deriving Show, do putStrLn . show
-- getChar - IO that reads character of input - because of buffering, read won't happen until user hits return
-- when from Control.Monad - if value is True, return the IO passed in. Else, return ():
import Control.Monad
main = do
c <- getChar
when (c /= ' ') $ do
putChar c
main
-- sequence - list of IO and returns IO that performs them in order - sequence :: [IO a] -> IO [a]
-- mapM maps a function over the list then sequences it, mapM_ does the same and throws out the result
-- forever - takes an IO and returns an IO that repeats the original action forever - from Control.Monad - e.g.
import Control.Monad
import Data.Char
main = forever $ do
putStr "Input: "
l <- getLine
putStrLn $ map toUpper l
-- Files & streams
-- getContents reads from stdin in a lazy manner
getContents :: IO String
import Data.Char
main = do
contents <- getContents
putStr (map toUpper contents)
-- This reads from contents as needed, rather than all at once.
-- Now, a program that takes input and prints lines shorter than 10 chars
main = do
contents <- getContents
putStr (shortLinesOnly contents)
shortLinesOnly :: String -> String
shortLinesOnly input = unlines . (filter (\x -> (length x) < 10)) . lines input
-- We can use `interact` to read contents, map them String -> String, then print the result:
main = interact $ unlines . (filter ((<10) . length)) . lines
-- File IO
-- Here's an example reading a file:
import System.IO
main = do
handle <- openFile "file.txt" ReadMode
contents <- hGetContents handle
putStr contents
hClose handle
-- Alternatively, we can use `withFile` to automatically close the Handle:
main = do
withFile "file.txt" ReadMode (\handle -> do
contents <- hGetContents handle
putStr contents)
-- We could implement withFile ourselves:
withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile path mode fn = do
handle <- openFile path mode
result <- fn handle
hClose handle
return result
-- hGetLine, hPutStr, hPutStrLn, hGetChar all exist for file IO
-- readFile :: FilePath -> IO String can simplify the above - reads contents of file to IO String action
-- writeFile :: FilePath -> String -> IO () - overwrites contents of file
-- appendFile :: FilePath -> String -> IO () - appends to contents of file
-- We can change the buffering mode using hSetBuffering and the BufferMode type
-- Can also use hFlush to flush a handle (IO ())
-- Program for removing line from todo.txt:
import System.IO
import System.Directory
import Data.List
main = do
handle <- openFile "todo.txt" ReadMode
(tempName, tempHandle) <- openTempFile "." "temp"
contents <- hGetContents handle
let todoTasks = lines contents
numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks
putStrLn "These are your TODO items:"
putStr $ unlines numberedTasks
putStrLn "Which one do you want to delete?"
numberString <- getLine
let number = read numberString
newTodoItems = delete (todoTasks !! number) todoTasks
hPutStr tempHandle $ unlines newTodoItems
hClose handle
hClose tempHandle
removeFile "todo.txt"
renameFile tempName "todo.txt"
-- We can use getArgs :: IO [String] and getProgName :: IO String from System.Environment to get CLI args
-- Let's build a simple CLI application:
import System.Environment
import System.Directory
import System.IO
import Data.List
add :: [String] -> IO ()
add [fileName, todoString] = appendFile fileName (todoString ++ "\n")
view :: [String] -> IO ()
view [fileName] = do
contents <- readFile fileName
let todoTasks = lines contents
numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks
putStr $ unlines numberedtasks
-- ...similarly for `remove`
dispatch :: [(String, [String] -> IO ())]
dispatch = [
("add", add),
("view", view),
("remove", remove),
]
main = do
(command:args) <- getArgs
let (Just action) = lookup command dispatch
action args
-- Random number generation:
import System.Random
-- random :: (RandomGen g, Random a) -> g -> (a, g)
-- RandomGen - a source of randomness
-- Random - typeclass of things that can take on random values
-- To manually create a random generator, use mkStdGen :: Int -> StdGen
-- So, now we can do this:
random (mkStdGen 100) :: (Int, StdGen) -- this casts the a -> Int
-- We can use the `randoms` function to get an infinite sequence of random values:
take 5 $ randoms (mkStdGen 11) :: [Int]
-- We can get random values in a range using randomR and randomRs
-- randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g)
-- But we may want to randomly initialize StdGen rather than providing a seed
-- To do this, we can use `getStdGen :: IO StdGen`:
main = do
gen <- getStdGen
putStr $ take 20 (randomRs ('a','z') gen)
-- Bytestrings
-- [Char] can be inefficient because Char is not a fixed width
-- Instead, we can use bytestrings which are [Word8] mostly
-- There are 2 kinds - Data.Bytestring (strict) and Data.Bytestring.Lazy (lazy)
-- pack :: [Word8] -> ByteString
-- Word8 is 0 thru 255. For example:
Data.ByteString.pack [99,97,110] -- == Chunk "can" Empty
-- unpack :: ByteString -> [Word8]
-- fromChunks takes a list of strict bytestrings and creates a combined lazy bytestring
-- cons - adds a Word8 to the beginning of a bytestring, lazily
-- cons' - if you are prepending lots of Word8, use the strict version since it's more efficient
-- empty - makes an empty ByteString
-- Example program copying one file to another, lazily. This already exists, but is a good example
import System.Environment
import qualified Data.ByteString.Lazy as B
main = do
(fileName1:fileName2:_) <- getArgs
copyFile fileName1 fileName2
copyFile :: FilePath -> FilePath -> IO ()
copyFile src dst = do
contents <- B.readFile src
B.writeFile dst contents
-- This lazily transfers the file in chunks
-- In I/O context, we can actually catch exceptions arising from IO actions using
-- `catch` from System.IO.Error:
-- catch :: IO a -> (IOError -> IO a) -> IO a
import System.Environment
import System.IO
import System.IO.Error
main = toTry `catch` handler
toTry :: IO ()
toTry = do (fileName:_) <- getArgs
contents <- readFile fileName
putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!"
handler :: IOError -> IO ()
handler e = putStrLn "Whoops, had some trouble!"
-- We can use ioError to re-raise the IOError
-- There are predicates over IOError to figure out what type of error it was:
-- isAlreadyExistsError, isDoesNotExistError, isAlreadyInUseError, isFullError, isEOFError, isIllegalOperation, isPermissionError, isUserError
-- isUserError is True when we use `userError` to create an IOError:
ioError $ userError "Uh, oh!"
Loading…
Cancel
Save