Created
March 27, 2021 07:35
-
-
Save hyperrealgopher/b53d65b7bb37b00e1fca6b2d803edc1f to your computer and use it in GitHub Desktop.
system-f/fp-course: FileIO.hs
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
{-# LANGUAGE NoImplicitPrelude #-} | |
{-# LANGUAGE ScopedTypeVariables #-} | |
{-# LANGUAGE OverloadedStrings #-} | |
{-# LANGUAGE RebindableSyntax #-} | |
module Course.FileIO where | |
import Course.Core | |
import Course.Applicative | |
import Course.Monad | |
import Course.Functor | |
import Course.List | |
{- | |
Useful Functions -- | |
getArgs :: IO (List Chars) | |
putStrLn :: Chars -> IO () | |
readFile :: FilePath -> IO Chars | |
lines :: Chars -> List Chars | |
void :: IO a -> IO () | |
Abstractions -- | |
Applicative, Monad: | |
<$>, <*>, >>=, =<<, pure | |
Tuple Functions that could help -- | |
fst :: (a, b) -> a | |
snd :: (a, b) -> b | |
(,) :: a -> b -> (a, b) | |
Problem -- | |
Given a single argument of a file name, read that file, | |
each line of that file contains the name of another file, | |
read the referenced file and print out its name and contents. | |
Consideration -- | |
Try to avoid repetition. Factor out any common expressions. | |
Example -- | |
Given file files.txt, containing: | |
a.txt | |
b.txt | |
c.txt | |
And a.txt, containing: | |
the contents of a | |
And b.txt, containing: | |
the contents of b | |
And c.txt, containing: | |
the contents of c | |
To test this module, load ghci in the root of the project directory, and do | |
>> :main "share/files.txt" | |
Example output: | |
$ ghci | |
GHCi, version ... | |
Loading package... | |
Loading ... | |
[ 1 of 28] Compiling (etc... | |
... | |
Ok, modules loaded: Course, etc... | |
>> :main "share/files.txt" | |
============ share/a.txt | |
the contents of a | |
============ share/b.txt | |
the contents of b | |
============ share/c.txt | |
the contents of c | |
-} | |
-- Given the file name, and file contents, print them. | |
-- Use @putStrLn@. | |
printFile :: | |
FilePath | |
-> Chars | |
-> IO () | |
printFile filePath contents = do | |
putStrLn filePath | |
putStrLn contents | |
-- Given a list of (file name and file contents), print each. | |
-- Use @printFile@. | |
printFiles :: | |
List (FilePath, Chars) | |
-> IO () | |
printFiles listFiles = do | |
_ <- sequence $ f <$> listFiles | |
pure () | |
where | |
f :: (FilePath, Chars) -> IO () | |
f (filePath, chars) = void $ printFile filePath chars | |
-- Given a file name, return (file name and file contents). | |
-- Use @readFile@. | |
getFile :: | |
FilePath | |
-> IO (FilePath, Chars) | |
getFile filePath = do | |
fileContents <- readFile filePath | |
pure (filePath, fileContents) | |
-- Given a list of file names, return list of (file name and file contents). | |
-- Use @getFile@. | |
getFiles :: | |
List FilePath | |
-> IO (List (FilePath, Chars)) | |
getFiles filePaths = | |
foldRight f (pure Nil) filePaths | |
where | |
-- get the io of file append it but put the tuple inside the io | |
f v acc = do | |
(filePath, chars) <- getFile v | |
((filePath, chars) :.) <$> acc :: IO (List (FilePath, Chars)) | |
-- Given a file name, read it and for each line in that file, read and print contents of each. | |
-- Use @getFiles@, @lines@, and @printFiles@. | |
run :: | |
FilePath | |
-> IO () | |
run filePath = do | |
mainFileContents <- readFile filePath | |
let fileNames = lines mainFileContents | |
(files :: List (FilePath, Chars)) <- getFiles fileNames | |
printFiles files | |
-- /Tip:/ use @getArgs@ and @run@ | |
main :: | |
IO () | |
main = do | |
(fileName :. Nil) <- getArgs | |
void $ run fileName | |
---- | |
-- Was there was some repetition in our solution? | |
-- ? `sequence . (<$>)` | |
-- ? `void . sequence . (<$>)` | |
-- Factor it out. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment