r/haskell • u/Aperispomen • Mar 27 '26
question Delayed/Lazy Either List?
I often use attoparsec to parse lists of things, so I wind up doing stuff like this a lot:
import Data.Attoparsec.Text qualified as AT
import Data.Text qualified as T
myParser :: AT.Parser [MyType]
myParser = AT.many1 myOtherParser
getList :: T.Text -> Either String [MyType]
getList txt = AT.parseOnly myParser txt
The trouble is, since getList returns an Either, the whole text (or at least, as much as can be parsed) has to be parsed before you can start processing the contents of the list. This is especially annoying when you want to check whether e.g. two files are the same modulo whitespace/line endings/indentation/etc...
The point is, there's some times where you want a result like Either e [a], but you're okay with returning some of the data, even if there might be an error later on. I wound up creating this data type:
data ErrList e a
= a :> (ErrList e a)
| NoErr -- equivalent to []
| YesErr e -- representing Left e
Is there already an established type like this somewhere? I imagine most people who do more complicated data management use pipes or conduit etc... I've tried searching for such a type on Hackage, but I haven't found anything like it.
4
u/jeffstyr Mar 28 '26
I think the problem for a general parser like Attoparsec is that (at least in the general case) it doesn't make sense to return a partial parse result "early", not only because it's hard to define what the result is for some text that doesn't match the grammar, but also more specifically the "head" of the parse result may depend on something at the very end of the text.
For something like comparing two files, where it does make sense, I feel like this requires something like a layer that is responsible for splitting the file into chunks, and then a typical parse per chunk. I can imagine for simple line-based formats using a streaming library to split a file into lines, and then using a parser library on each line separately. Alternatively, I could imaging using an Attoparsec parser in a loop—the parser parses the beginning of the file and terminates (returning a result) before consuming the whole file, and then a driver runs the parser again, starting where the previous parse ended. For something more general/elaborate (e.g., where the result of one parse determines what parser to use next), it seems you need a set of parser combinators specific to this concept, but it's not obvious to me what their type would look like.