6

I'm working on parsing a CSV file into a CSV type which is a list of Record which is a list of Field, which are just Strings. After inserting a new row and then trying to access the csv I get the c stack overflow error. I'v read this error may come from too large a "thunk" by using tail recursion but I don't think thats what I'm doing wrong?

type CSV = [Record]
type Record = [Field]
type Field = String

run :: IO()
run =
 do
  inFile <- readFile "myFile.csv"
  let csv = parse inFile
  let csv = (insertRow "abc,def,ghi" csv)
  putStr (show csv)

insertRow :: String -> CSV -> CSV
insertRow newRow csv = csv ++ [toRecord newRow]

parse :: String -> CSV
parse file = map toRecord (parseLines file "" [])

toRecord :: String -> Record
toRecord line = parseWords line "" []

-- parseLine input partialCSV records
parseLines :: String -> String -> [String] -> [String]
parseLines [] partial records = records ++ [partial]
parseLines ('\r':xs) partial records = parseLines xs [] (records ++ [partial])
parseLines (x:xs) partial records = parseLines xs (partial ++ [x]) records

-- parseWords input partialRecord fields
parseWords :: String -> String -> [String] -> [String]
parseWords [] partial fields = fields ++ [partial]
parseWords ('"':xs) partial fields = parseQuotes xs partial fields
parseWords (',':xs) partial fields = parseWords xs [] (fields ++ [partial])
parseWords (x:xs) partial fields = parseWords xs (partial ++ [x]) fields

parseQuotes :: String -> String -> [String] -> [String]
parseQuotes ('"':xs) partial fields = parseWords xs [] (fields ++ [partial])
parseQuotes (x:xs) partial fields = parseQuotes xs (partial ++ [x]) fields
ehird
  • 40,602
  • 3
  • 180
  • 182
  • 5
    Unrelated to your problem, hugs' last release is over five years old. The language evolves further, also ghci performs better (and comes with a compiler ;). Although it is sad, I recommend not using hugs anymore (until somebody revives it). – Daniel Fischer Jan 04 '12 at 14:36

2 Answers2

5

let bindings are recursive, so this line

let csv = (insertRow "abc,def,ghi" csv)

creates an infinite loop, you're defining csv in terms of itself in a way that doesn't terminate. Change it to

let csv' = ...

and print csv' in the next line.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • Thanks for the quick and helpful reply! Although I'm not fully sure about the let. Does this mean I cant change csv, I have to keep using csv', csv''...? – user1130083 Jan 04 '12 at 14:53
  • 2
    In Haskell, all values are immutable, so you can't change csv anyway. So generally you have to give new names to values you obtain as the result of a 'modification'. However, you can reuse names by _shadowing_ the name, but that works only if the shadowed/rebound name doesn't appear on the RHS of its binding, otherwise the occurrence on the RHS would refer to the new name, loop. You can have `do { blah; let { x = foo; }; let { y = bar x; }; let { x = baz y; }; quux x; }` and the second binding of `x` would shadow the first. But not (in general) `let x = foo x`, that loops. – Daniel Fischer Jan 04 '12 at 15:03
  • 2
    And such shadowing is *highly* discouraged. So...pretend like you can't do it, and just use unique names in nested lets (where reasonable). – Dan Burton Jan 04 '12 at 18:36
2

The double let csv = ... looks suspicious. Could you try disentangling the two variables? It probably doesn't do what you want (in Haskell let is recursive).

Volker Stolz
  • 7,274
  • 1
  • 32
  • 50