More notes
This commit is contained in:
parent
cc5a1154be
commit
68b3bec6e1
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
03-starting-out.hs
Normal file → Executable file
0
03-starting-out.hs
Normal file → Executable file
0
04-types-and-typeclasses.hs
Normal file → Executable file
0
04-types-and-typeclasses.hs
Normal file → Executable file
0
05-syntax-in-functions.hs
Normal file → Executable file
0
05-syntax-in-functions.hs
Normal file → Executable file
0
06-recursion.hs
Normal file → Executable file
0
06-recursion.hs
Normal file → Executable file
0
07-higher-order-functions.hs
Normal file → Executable file
0
07-higher-order-functions.hs
Normal file → Executable file
0
08-modules.hs
Normal file → Executable file
0
08-modules.hs
Normal file → Executable file
0
09-types-and-typeclasses-redux.hs
Normal file → Executable file
0
09-types-and-typeclasses-redux.hs
Normal file → Executable file
264
10-input-and-output.hs
Executable file
264
10-input-and-output.hs
Executable file
@ -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!"
|
||||
|
||||
|
2
11-thinking-functionally.hs
Executable file
2
11-thinking-functionally.hs
Executable file
@ -0,0 +1,2 @@
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user