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