Thought Experiment: Namespaced record fields
Tags: elm
Published: 2018-03-21 13:22:00

---
title: Thought Experiment: Namespaced record fields
published: true
description: An idea for making flat records more workable
tags: elm
---

I don’t like to nest records in Elm. It’s not that big of a deal, but it always seem to be lead to more noise than initially thought. Part of this is that Elm doesn’t have syntax that’s convenient for nested updates. Take a look at this record definition:

```elm
type alias Person =
  { name : Int
  , age : Int
  , pet : Pet
  }

type alias Pet =
  { name : String
  , age : Int
}
```

If I had a person record and wanted to rename the pet, the code would look like something like this:

```elm
-- Can't do this
{ person.pet | name = "Fido" }

-- Or this
{ person | pet = { person.pet | name = "Fido" } }

-- It has to be this
let
  pet = person.pet
in
{ person | pet = { pet | name = "Fido" } }
```

As I said previously, this isn't a big deal but it does leave me with an itch. One way to allieviate this is by avoiding nested records all together:

```elm
type alias PersonWithPet =
  { personName : String
  , personAge : Int
  , petName : String
  , petAge : Int
  }
```

We can make this scale by using extensible record syntax:

```elm
type alias Pet a =
  { a |
    petName : String
  , petAge : Int
  }

{- Works with PersonWithPet -}
renamePet : String -> Pet a -> Pet a
renamePet name pet =
  { pet | petName = name }
```

So, this actually solves the problem but does require me to prefix all fields in the record. What if there was support in the compiler for making this nicer?

Let's switch gears a little bit and talk about my previous language-of-choice, Clojure. Clojure is a Lisp and is dynamically typed. Instead of Records one simply uses maps (in Elm we call it `Dict`) to group together data. Clojure has its own type to serve as keys in a map, called keywords. They look like this:

```clojure
:name ;; keyword

;; Person with a Pet
(def person
  { :name "Robin"
    :age 29
    :pet { :name "Fido"
           :age 4 } } )
```

In Clojure, nested updates is pretty simple. If I wanted to rename the pet using the person definition above, I would do this:

```clojure
(assoc-in person [:pet :name] "Baldur")
```

However, sometimes it makes perfect sense to avoid nesting and Clojure has wonderful support for that:

```clojure
(def person-with-pet
  { :person/name "Robin"
    :person/age 29
    :pet/name "Fido"
    :pet/age 4 } )
```

But this still requires us prefix everything. This is where namespaced keywords comes into play:

```clojure
(ns person) ;; namespace is set to person

;; This equals our previous definition
(def person-with-pet
  { ::name "Robin" ;; notice the double colon
    ::age 29
    :pet/name "Fido"
    :pet/age 4 } )

;; This is also the same thing
(def person-with-pet
  #:person{ :name "Robin"
            :age 29
            :pet/name "Fido"
            :pet/age 4 } )
```

The double colon in the example above will fill in the current namespace as the prefix of the keyword. What could this potentially look like in Elm?

```elm
module Pet exposing (Pet)

type alias Pet a =
  { a |
    :name : String ;; expands to pet/name
    :age : Int ;; expands to pet/age
  }

module Person

import Pet as P

type alias PersonWithPet =
  { :name : String -- person/name
  , :age : Int -- person/age
  , :P/name : String -- pet/name
  , :P/age : Int -- pet/age
  }

{- Works in PersonWithPet -}
renamePet : String -> P.Pet a -> P.Pet a
renamePet name pet =
  { pet | :P/name = name }
```

Is this an improvement? Maybe. It might be better to instead find a good syntax for nested updates, but I wouldn't mind just having a simple syntax to work with flat records.