1

I am trying to write every list from a nested list to the same file. However I want every list to start on a newline.

I've made this and it works, but it's very inefficient and looks bad:

appendFile "scraped.txt" (show (take 1 l))
appendFile "scraped.txt" ("\n")
let l3 = (drop 1 l)
appendFile "scraped.txt" (show (take 1 l3))
appendFile "scraped.txt" ("\n")
let l4 = (drop 1 l3)
appendFile "scraped.txt" (show (take 1 l4))
appendFile "scraped.txt" ("\n")
let l5 = (drop 1 l4)
appendFile "scraped.txt" (show (take 1 l5))
appendFile "scraped.txt" ("\n")
let l6 = (drop 1 l5)
appendFile "scraped.txt" (show (take 1 l6))
appendFile "scraped.txt" ("\n")

I tried something like the following, but I can't seem to correctly use the mapping function:

listValues :: [[String]] -> [String]
listValues :: (map . map)

appendValues :: [[String]] -> IO ()
appendValues = appendFile "scraped.txt" listValues

The txt file now looks like this, which is ok, I just want to know how I can improve my code and learn how to use the mapping function.

Title,Subtitle,Date,Author
[["Een gezonde samenleving? \226\128\156Het belang van sporten wordt onderschat\226\128\157","Teamsport","16 maart 2022","HAN redactie"]]
[["Zo vader, zo dochter","Carsten en Kirsten","10 maart 2022","HAN redactie"]]
[["Milieuvriendelijk vervoer met waterstof","Kennisclip","09 maart 2022","HAN redactie"]]
[["\"Ik heb zin in wat nog komen gaat\"","Master Mind","08 maart 2022","HAN redactie"]]
[["Oorlog in Oekra\195\175ne","Statement van het CvB","07 maart 2022","HAN redactie"]]
E_net4
  • 27,810
  • 13
  • 101
  • 139
  • Given the heading line `Title,Subtitle,Date,Author` in the output file you've shown, it rather looks like you are supposed to be taking each nested list and *rendering* it into CSV format (Comma Separated Value), not simply dumping it as a Haskell-source-code formatted list. – Ben Mar 24 '22 at 23:19

1 Answers1

3

To do this in a map-like loop, you would typically use the mapM_ library function. The final underscore_ in the function name means that action results are ignored, something which suits us as we want a result type of IO ().

It seems appropriate to get a file handle, in order to avoid asking the OS to repetitively reopen the same output file, which is wasteful.

Possible code:

import System.IO

type FileName = String

writeNestedList :: Show a => FileName -> [[a]] -> IO ()
writeNestedList fileName xss =
    do
       fh <- openFile  fileName  WriteMode  -- get a file handle
       mapM_  ((hPutStrLn fh) . show)  xss
       hClose fh


main :: IO ()
main = do
   let  xss = [ ["ab","bc"] ,["de","ef"], ["gh","hi"] ]
   writeNestedList  "scraped.txt"  xss

Testing:

$ 
$ ghc q71608762.hs  -o q71608762.x
[1 of 1] Compiling Main             ( q71608762.hs, q71608762.o )
Linking ./q71608762.x ...
$ 
$ q71608762.x
$ 
$ cat scraped.txt
["ab","bc"]
["de","ef"]
["gh","hi"]
$ 

jpmarinier
  • 4,427
  • 1
  • 10
  • 23
  • Thank you for the reply. What do you exactly mean with filehandle? Is that the opening and closing of the file or is it the WriteMode? Is that why you don’t use the appendFile function? – Cent Meister Mar 24 '22 at 21:46
  • The WriteMode thing is just that, an I/O mode, not a file handle. The file handle is something that the OS uses to access the contents of a specific file. Function `appendFile` transparently creates the handle from the file name for you, but to do this once per output item is horribly inefficient. If you are familiar with C/C++ programming, functions such as `fopen()` also return a file handle. – jpmarinier Mar 24 '22 at 21:57
  • Another idea if file handles are unfamiliar: build up the entire `String` to write and then dump it all in one go, as in `writeFile "scraped.txt" $ unlines (map show xss)`. – Daniel Wagner Mar 24 '22 at 22:13
  • @CentMeister you can look up file handles in a Haskell context in this nice and well-known [tutorial](http://learnyouahaskell.com/input-and-output) commonly referred to as LYAH. The other chapters of LYAH are also worth a look. – jpmarinier Mar 24 '22 at 22:28
  • Hey guys, thanks for all the replies. I will check it tomorrow morning and mark this as answered. Thank you for all your time and effort! – Cent Meister Mar 24 '22 at 22:29