I wrote this code:
toCouplesFile = do inputFile <- openFile "deletedId.csv" ReadMode
outputFile <- openFile "couples.txt" WriteMode
readAndChange inputFile outputFile
readAndChange i o = do iseof <- hIsEOF i
if iseof then (return o)
else do line <- hGetLine i
hPutStrLn o (show (extractNameAndId line))
readAndChange i o
I wonder if I can rewrite this code using just one function, using something similar to this pattern:
function x = do ...
label
.....
if ... then label else exit
You're making life difficult by programming in a needlessly imperative way. You're programming in the beautiful Haskell language and you're looking for a goto
construct!
Why not just import Control.Applicative (<$>)
and write
readAndChange' = writeFile "couples.txt" =<<
unlines.map (show.extractNameAndId).lines <$> readFile "deletedId.csv"
(Yup, that's almost a one-liner. It's in clean, functional style and uncluttered by the mechanics of reading and writing lines. As much as possible of the processing is done in pure code, only input and output are IO-based.)
Explanation:
Here unlines.map (show.extractNameAndId).lines
processes your input by chopping it into lines, applying extractNameAndId
then show
to each one using map
, then joining them back together again with unlines
.
unlines.map (show.extractNameAndId).lines <$> readFile "deletedId.csv"
will read the file and apply the processing function. <$>
is pleasant syntax for fmap
.
writeFile "couples.txt" =<< getanswer
is the same as getanswer >>= writeFile "couples.txt"
- get the answer as above then write it to the file.
Try writing greet xs = "hello " ++ xs
then in ghci do these for fun
greet "Jane" -- apply your pure function purely
greet $ "Jane" -- apply it purely again
greet <$> ["Jane","Craig","Brian"] -- apply your function on something that produces three names
greet <$> Just "Jane" -- apply your function on something that might have a name
greet <$> Nothing -- apply your function on something that might have a name
greet <$> getLine -- apply your function to whatever you type in
greet <$> readFile "deletedId.csv" -- apply your function to your file
the final one is how we used <$>
in readAndChange
. If there's a lot of data in
deletedId.csv you'll miss the hello, but of course you can do
greet <$> readFile "deletedId.csv" >>= writeFile "hi.txt"
take 4.lines <$> readFile "hi.txt"
to see the first 4 lines.
So $
lets you use your function on the arguments you gave it. greet :: String -> String
so if you write greet $ person
, the person
has to be of type String
, whereas if you write greet <$> someone
, the someone
can be anything that produces a String
- a list of Strings, an IO String
, a Maybe String
. Technically, someone :: Applicative f => f String
, but you should read up on type classes and Applicative Functors first. Learn You a Haskell for Great Good is an excellent resource.
For even more fun, if you have a function with more than one argument, you can still use the lovely Applicative style.
insult :: String -> String -> String
insult a b = a ++ ", you're almost as ugly as " ++ b
Try
insult "Fred" "Barney"
insult "Fred" $ "Barney"
insult <$> ["Fred","Barney"] <*> ["Wilma","Betty"]
insult <$> Just "Fred" <*> Nothing
insult <$> Just "Fred" <*> Just "Wilma"
insult <$> readFile "someone.txt" <*> readFile "someoneElse.txt"
Here you use <$>
after the function and <*>
between the arguments it needs. How it works is a little mind-blowing at first, but it's the most functional style of writing effectful computations.
Next read up about Applicative Functors. They're great.
http://learnyouahaskell.com/functors-applicative-functors-and-monoids
http://en.wikibooks.org/wiki/Haskell/Applicative_Functors
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Either
readAndChange i o = do
result <- fmap (either id id) $ runEitherT $ forever $ do
iseof <- lift $ hIsEof i
when iseof $ left o -- Break and return 'o'
line <- lift $ hGetLine i
lift $ hPutStrLn o $ show $ extractNameAndId line
-- 'result' now contains the value of 'o' you ended on
doSomeWithingWith result
To understand why this technique works, read this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With