
I have a CSV file containing some data about trains: stations from, to, times etc. and I wanted to 'learn some more Haskell' and, to my astonishment, I have gotten thus far but I am not sure *why* it works or *how* I got there! LMAO Here is the relevant code ... ====> trains :: String -> IO () trains csvfile = do legodata <- parseCSVFromFile csvfile case legodata of Left error -> print error Right legodata -> mapM_ putStrLn (trainCodes legodata) -- Assumes row 1 contains cell header information -- Note: the train-code is always the third cell trainCodes :: [Record] -> [String] trainCodes = nub . map (!! 2) . tail ====> I was chuffed with writing the trainCodes as a point-free function, that sort of thing is getting a little easier to work with but I still have real head-banging frustrations sometimes with seemingly simple things, like looping and just printing stuff out, despite having taught myself LISP six years ago and Erlang in recent years! I quit!! I really do!!! My confusion arises over: mapM_ putStrLn (traincodes legodata) Given that: mapM_ :: Monad http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Control-Monad... m => (a -> m b) -> [a] -> m () http://haskell.org/ghc/docs/6.12.2/html/libraries/ghc-prim-0.2.0.0/GHC-Unit.... Here's how I read it: For any m that is a Monad, mapM_ takes a function that "takes an 'a' and returns it wrapped in a monad", a "list of a's" and returns a "monad containing 'unit'", the empty-list LISP-() undefined voidy thing. Given that: putStrLn :: String http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.htm... -> IO http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.htm... () http://hackage.haskell.org/packages/archive/base/latest/doc/ghc-prim-0.2.0.0..., this means that 'a' is String and 'm b' is IO () and my list of [a] is the result of calling 'traincodes legodata'. trainCodes = nub . map (!! 2) . tail legodata is [Record] (from Text.CSV) and so, 'tail' removes the header row from the data, 'map (!! 2)' extracts the third field from each row and finally 'nub' removes the duplicates. Thus the return type from trainCodes is [String]. Gluing it all together: mapM_ :: Monad http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Control-Monad... m => (a -> m b) -> [a] -> m () http://haskell.org/ghc/docs/6.12.2/html/libraries/ghc-prim-0.2.0.0/GHC-Unit.... putStrLn :: String http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.htm... -> IO http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.htm... () http://hackage.haskell.org/packages/archive/base/latest/doc/ghc-prim-0.2.0.0... trainCodes :: [Record] -> [String] the type of my call then would seem to be: String -> IO () -> [String] -> IO () "putStrLn" -> (trainCodes legodata) -> IO () which means that not only have I got the types correct for the call but the result type of 'IO ()' also satisfies the type return for my function and hence it executes from 'main' (where it is called from) with no issues. So, am I finally beginning to get a grip on it all ? This list is a constant source of education and I don't post very often as you guys give me far too much stuff to be reading all the time! :) I am using Text.CSV to read my file and all I wanted to do was to output a list of unique codes from column three of the spreadsheet data, one per line, so that I can use this Haskell as part of a bigger 'bash' script. Any detailed explanations that might help me better understand my solution would be welcome; right now I feel I 'just got lucky' although there must be a glimmer of understanding somewhere! LOL Thanks, Sean. PS: Phew!