# Attributes

I use a library called [Pathom3](https://pathom3.wsscode.com/). The [Getting Started page](https://pathom3.wsscode.com/docs/) opens with

> **Pathom** is a Clojure/script library to model attribute relationships.

The [README](https://github.com/wilkerlucio/pathom3#readme) states

> Logic engine for attribute processing for Clojure and Clojurescript.

There are a few big ideas in here, but I want to focus on just one - attributes.

# What's an attribute?

Before working with Pathom, I'd never really worked with attributes. It's my blog post, so I get to define it:

> A pairing of:
> 
> 1. an unambiguous global name and
>     
> 2. a value
>     

# What do attributes look like in Pathom/Clojure?

```clojure
:com.brettrowberry.posts/display-name ; unambiguous global name
"Pathom: Attributes"                  ; value
```

If you don't recognize the `:com.brettrowberry.posts/display-name` syntax, here's a [primer on Clojure keywords](https://clojuredocs.org/clojure.core/keyword). In this example, it's a namespaced or qualified keyword. The Clojure snippet below demonstrates that namespaced keywords are first class:

```clojure
(namespace :com.brettrowberry.posts/display-name) 
;=> "com.brettrowberry.posts"
(name :com.brettrowberry.posts/display-name) 
;=> "display-name"
```

Notice that we're following the time-honored tradition of using [reverse-DNS notation](https://en.wikipedia.org/wiki/Reverse_domain_name_notation) for the namespace part.

# What isn't an attribute?

* A bare value, e.g. `"Pathom: Attributes"`
    
* A name, no matter how unambiguous, e.g. `:name`
    
* An *ambiguous* local name and a value, e.g. `:name "Pathom: Attributes"`
    

`:name` could mean any number of things across a variety of contexts, like the name of a blog post, or the name of its author! I consider ambiguous local names to be "normal" programming practice (in Clojure or otherwise), as compared to the more enlightened attribute-driven world. Let's take a look at another thing that isn't an attribute: aggregates.

## Aggregates

Aggregates are a composition of values, or key-value pairs (think [Clojure maps](https://clojure.org/reference/data_structures#Maps)).

### Good Aggregates

```clojure
(def global-names
  {:com.brettrowberry.posts/post-name "Pathom: Attributes"
   :com.brettrowberry.posts/author-name "Brett Rowberry"})

(defn greet-attribute
  [{:keys [com.brettrowberry.posts/author-name]}]
  (str "Hi, " author-name))

(greet-attribute global-names)
```

This `global-names` aggregate is unusually nice in that it's composed of attributes. In `greet-attribute`, there's no tricky traversal and no wondering what exactly the input is. Isn't it great?

### Bad Aggregates

Generally, the aggregates I've encountered are sets of ambiguous, and often nested, local names and value pairs, e.g.:

```clojure
(def local-names
  {:post   {:name "Pathom: Attributes"}
   :author {:name "Brett Rowberry"}})
```

Consider the `greet-local` function below. There's no context for what `:name` is – the name of a post, of an author, or something else. When aggregates like `names` spread across a codebase, it can be hard to know to what `:name` refers.

```clojure
(def local-names
  {:post   {:name "Pathom: Attributes"}
   :author {:name "Brett Rowberry"}})

(defn greet-local
  [{:keys [name]}]
  (str "Hi, " name))

(greet-local (:author local-names))
```

An alternative that preserves the context requires traversing the whole structure, as in the `greet-traversal` function below:

```clojure
(def local-names
  {:post   {:name "Pathom: Attributes"}
   :author {:name "Brett Rowberry"}})

(defn greet-traversal
  [{{:keys [name]} :author}]
  (str "Hi, " name))

(greet-traversal local-names)
```

It can become tiresome when many functions have to perform the same traversal.

# Blast from the F# Past

Sorry, F# was my favorite language before Clojure, so I can't resist a comparison. The closest thing to attributes I encountered before Pathom was [single-case Discriminated Unions in F#](https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/designing-with-types-single-case-dus.html). Here's the best I could come up with:

```fsharp
namespace Com.Brettrowberry

module Post =
  type Name = Name of string

module Author =
  type Name = Name of string
```

Grouping these into an aggregate, an [F# Record](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/records), isn't great:

```fsharp
type Names = { PostName: Com.Brettrowberry.Post.Name
               AuthorName: Com.Brettrowberry.Author.Name }
```

Identifiers in F# (unlike Clojure keywords) can't have `.` in them, so I can't just write something like the right side. So, I made up more local names: `PostName`, and `AuthorName`.

I could have avoided declaring the `Names` type using an [Anonymous Record](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/anonymous-records).

## SQL

Relational databases struggle with attributes as well. We can make schemas and tables, but that's as deep as the hierarchies go. We can't put `.` in schema names, but we can put `_`. We could make a single-column table per attribute, but that's pretty unusual.

```pgsql
CREATE SCHEMA com_brettrowberry;
CREATE TABLE  com_brettrowberry.post (name text);
CREATE TABLE  com_brettrowberry.author (name text);

insert into   com_brettrowberry.post values ('Pathom: Attributes');
insert into   com_brettrowberry.author values ('Brett Rowberry');
```

# Closing

When it comes to data, I assert that the smallest unit is the attribute. Clojure, with its keywords, is uniquely suited to represent attributes. Doing so can provide us with the most flexible designs. In the words of Rich Hickey:

> ...design is taking things apart in order to be able to put them back together.
> 
> [Design, Composition and Performance](https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/DesignCompositionPerformance.md)
