Parsing Liquid using Haskell and Megaparsec
September 1, 2019
For whatever reason I’ve always found it easy to pick up new programming languages. Nothing gets my juices flowing as fast as learning new syntax, new way of doing things I already know how to do. It is simply one of my favorite pastimes, and it should be evident from looking at my Github account, although to be honest there’s maybe 30% of stuff I’ve played in my spare time with there.
Haskell is one of those languages where I failed to write anything substantial over the years. It never clicked for me, which is caused at least partialy by the poor quality of learning resources for the language. People are just too distracted by category theory and writing mathematical like equations instead of practical code.
This year I tried again to hack something together, this time my idea was to write a parser for Liquid templates using Megaparsec. The result is Liquidhs it’s not completely functional but it does handle a lot of the basic stuff.
The experience of writing Haskell was nice, using Emacs and Intero it felt I always got help when I needed it, but I think this project was not the best fit for my needs. I wanted something to play with the language as it is usually used. Writing a parser is not that difficult but Liquid templates can have Liquid + html + json + yaml on one page which makes it difficult even with Megaparsec to write it in a simple way.
Megaparsec wraps this idea of parser combinators. You basically write a lot of functions to parse tokens and the library takes care of handling erros, and processing as much of the input as possible (it obviously does way more than that read more about it ) So you end up with things like:
assignement :: Parser LiquidObject
assignement = do
void (symbol "assign")
var <- identifier
void (symbol "=")
expr <- (stringValue <|> identifier)
skipMany $ symbol "| "
filters <- filterCall `sepBy` (symbol "| ")
return (Assign var expr filters)
And this knows how to parse things like:
assign a = "alpha" | uppercase
It’s quite easy to undersant: you start with the “assign” symbol, then you have the identifier (in our case “a”), followed by equals sign and either a string value or a identifier, followed by one or many pipe ("|") connected filters. In our case the value assigned to “a” would end up being “ALPHA” but that is not important in the context of the parser, we only want to interet this to a certain structure. The actual structure here would be:
(Assign "a" "alpha" [FilterName "upcase"])
I have mixed feelings about Haskell, I’m sure it’s not the best language for general programmer population, but is it good for the advanced develeopers? I’m not sure.. On one hand some of the ideas are very nice and clear on the other it kinda reminds me of Clojure where everyone writes everything in the way they think is the only true way. That basically means every time you join a project you have to learn a new framework and it’s hard to be productive from day 1.
I’m thinking of doing something else with Haskell, probably something more web based this time… stay tuned.