Design choices

Read this if you’re interested in the motivation behind various language design choices

This page centrally documents some commonly cited design decisions that come up in various feature requests and language proposals. However, note that these decisions are not set in stone and we’re open to changing our minds. The purpose of this page is not to bless any particular design decisions but rather to make them easier to discover so that you don’t learn about them late in the language design process.

These decisions usually fall into one of the following categories:

  • Artifacts of the language’s minimalist origin

    The original author of the language (Gabriel Gonzalez) added very few starting features to the language, mainly because it’s easier to add features than to take them away. If you see a missing feature, maybe that’s just because the language started out cautious and perhaps now’s the time to shake things up.

  • Promoting strongly-typed programming idioms

    Dhall promotes use of the type system to make “invalid states unrepresentable”. Not only do we add support for strongly typed programming idioms, but we also avoid adding support for language features which promote weakly typed idioms. In other words, we strive to design the language so that best practices are the path of least resistance and preferably the only available path.

  • Marketing

    In this context, “marketing” doesn’t mean advertising, but rather means designing the language around a specific use case (i.e. market) in order to gain mainstream traction and adoption.

    Dhall’s current “marketing” focuses on displacing YAML (especially in the context of ops-related tools) and some language features are tailored towards that use case. Sometimes we’ll turn down a feature request if it adds complexity to the language without getting us any closer to displacing YAML.

In addition to those broad rules of thumb, the following choices deserve explicit mention.

Text manipulation

The only text manipulation allowed is concatenation. Other than that, Text values are opaque and there is no way to introspect them. For example, you cannot compare Text values for equality, nor can you compute their length.

The motivation behind this is to encourage the use of more structured representations that catch errors at type-checking time instead of silently failing at runtime . For example, instead of this code:

-- NOTE: This is not valid Dhall code

let isWeekDay
    : String -> Bool
    = \(d : String) ->
            d == "Monday"
        ||  d == "Tuesday"
        ||  d == "Wednesday"
        ||  d == "Thursday"
        ||  d == "Friday"

in  isWeekDay "thursday" -- Oops!

You would instead write this code:

let DayOfWeek =
      < Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday >

let isWeekDay
    : DayOfWeek -> Bool
    = \(d : DayOfWeek) ->
        merge
          { Sunday = False
          , Monday = True
          , Tuesday = True
          , Wednesday = True
          , Thursday = True
          , Friday = True
          , Saturday = False
          }
          d

in  isWeekDay DayOfWeek.Thursday

Operators

All of the operators in the language are associative, meaning that for any operator (which we will denote +), the following property holds:

(x + y) + z = x + (y + z)

… and every operator has an identity value (which we will denote 0), such that:

x + 0 = x

0 + x = x

For example, the identity value for the * operator is 1, the identity value for the operator is {=}, and the identity value for == is True.

This also implies that the type of each operator’s result is the type of its input arguments. For example, this is why the == operator only works on values of type Bool and does not work on values of type Text (nor any other type).

Arithmetic

You cannot perform arithmetic on Doubles. Natural numbers and Integers support addition, subtraction, and multiplication, because those operations are well-defined for all possible inputs unlike division (due to the possibility of division by zero).

As floating-point arithmetic is imprecise and prone to surprising results (e.g. loss of associativity/distributivity, loss of accuracy due to cancellation, difficulty testing for equality, etc.), Doubles are opaque values as far as the language is concerned, meaning that a Dhall configuration file can hold them and shuffle them around but cannot manipulate them.

No dictionaries/maps/hashes

This is a consequence of Dhall values (especially Text) not being comparable so you cannot detect duplicate keys. The closest Dhall data type would be an association list of the following type:

[ { mapKey : Text, mapValue : a } ]

In fact, tools like dhall-to-json will recognize values of this type and convert them to maps in the target configuration format (such as JSON).

Dhall also doesn’t support sets, for the same reason.