Skip to content

Instantly share code, notes, and snippets.

@hyperrealgopher
Created March 27, 2021 07:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyperrealgopher/b53d65b7bb37b00e1fca6b2d803edc1f to your computer and use it in GitHub Desktop.
Save hyperrealgopher/b53d65b7bb37b00e1fca6b2d803edc1f to your computer and use it in GitHub Desktop.
system-f/fp-course: FileIO.hs
{-# 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