In fifteen lines of code I have a simple parser for templated text which reports errors intelligently and knows how to dump its parse tree:
*HTML> parse parseTemplate "" "foo bar"
Right [RawText:"foo bar"]
*HTML> parse parseTemplate "" "foo ^baz^ bar"
Right [RawText:"foo ",Variable:"baz",RawText:" bar"]
*HTML> parse parseTemplate "" "foo ^baz bar"
Left (line 1, column 9):
unexpected " "
expecting letter or "^"
Then in ten more lines of code I have a renderer that can run templates and report errors:
*HTML> let testTemplate = (\(Right x) -> x) (parse parseTemplate "" "demo: x is ^x^ and foo is ^foo^")
-- the bit with the "Right" is just to skip the "did it parse ok" check and wouldn't be in real code
*HTML> renderTemplate testTemplate [("x","meow"),("foo","bar")]
Right "demo: x is meow and foo is bar"
*HTML> renderTemplate testTemplate [("x","meow")]
Left "unknown variable foo"
But what's the most awesome about this all is that the language doesn't have exceptions or special support for parsing or anything; all of this can be accomplished with their unified monad syntax and a lot of abstraction. The static typing also means there ought to be no gotchas like an exception I forgot to handle or a type error.