• she/her

Principal engineer at Mercury. I've authored the Dhall configuration language, the Haskell for all blog, and countless packages and keynote presentations.

I'm a midwife to the hidden beauty in everything.

đź’– @wiredaemon


discord
Gabriella439
discord server
discord.gg/XS5ZDZ8nnp
location
bay area
private page
cohost.org/newmoon

I think one of the best design decisions for Dhall's user experience was to "just" eliminate almost all runtime error handling

What do I mean by that? In Dhall, most potentially failing operations that you could write do not exist. The language is actually very limited and doesn't permit a lot of functionality that you'd expect most programming languages to have. For example, in Dhall you cannot divide two numbers, nor can you compare two strings for equality. In fact, out of all of Dhall's built-in functions and operators there are only two that can "fail" which are List/head and List/last (both of which model failure by returning an Optional result). Dhall's Prelude doesn't even provide an Either/Result-like type because it's not necessary! There's (basically) nothing that can fail!

One of the consequences of this is that most errors are caught at compile time rather than at runtime, because most primitive operations cannot fail. On top of that, the language has no escape hatches whatsoever. There is no way to "turn off the type system", there is no undefined value, there is no recursion permitted, and there is no unsafe keyword.

I think most people understand the safety benefits of catching errors at type-checking time, but I don't think enough people appreciate the USER EXPERIENCE benefits of doing so. For example, when all errors become type errors programmers no longer need to think about crafting a good error message because there are no userland error messages. The interpreter is now solely responsible for reporting errors, not the programmer. This frees up the programmer to focus more on the business logic and focus less on error handling (which no longer exists in userland).

I don't think this design tradeoff is appropriate for all programming languages, but I think this is the natural evolution of high-level languages, especially as type systems become more advanced. Error messages and error handling will eventually become concerns of the language implementation and no longer be userland concerns.

If this topic interests you, I also blogged a bit about this idea of pushing more userland concerns into the language implementation in this post:


You must log in to comment.

in reply to @fullmoon's post:

That’s all well & good when your single source of truth is your dhall code, but once you take user input (or stuff coming from out of the “typed boundary”), you need to be able to parse; and parsing is—by definition—going from a larger domain to a smaller domain; so you need to put the other values somewhere, like into an error message.

Sorry if this is a stupid question, but since Dhall has anonymous functions and let bindings, wouldn't you be able to define some kind of Y combinator to get recursion? Or is there some kind of mechanism / limitation that prevents this?

You can’t get “general” recusion out of Dhall, but you can get finite/total recursion. This library is completely undocumented, but https://github.com/sellout/dada implements total recursion schemes (the documented sister library is https://hackage.haskell.org/package/yaya). Dada also provides corecursion, but forcing that is partial and thus can’t be normalized away in Dhall. (However, there are ways to do stuff like extract finite “chunks” from corecursive structure, so you can eliminate corecursion as long as you don’t need to traverse the entier (possibly infinite) structure). So Dada provides a Haskell interpreter to execute that possibly non-terminating evaluation.