Just My Type(s)

As I've been explaining Toccata, I've used the core types to illustrate the various concepts. But now that I've cracked open the door on how Toccata uses type assertions to keep code correct, it's time to get a little crazy.

Yep, I'm gonna talk about making your very own types.

It's (almost) all in the definition

There are a couple of pieces of information you need to define a type; a name and names for each of the fields. In the simplest case, this looks like

(deftype Flummer [phlux priv zlog])

This defines a type Flummer that has 3 fields. So each Flummer value will actually be 3 values that are forever bound together. (Implementation detail: each Flummer value is actually a Vector. But you can't get access to it.) Those three values can be literally any valid Toccata value, including other Flummer values or Vectors or HashMaps or ...

And how are these Flummer values created? Glad you asked.

(Flummer 'hook "crook" 23)

Couldn't be easier. The order the values are given in the call to the Flummer function, called the construcor BTW, matches the order of the fields in type definition. So for this particular value the fields are assigned values like this

phlux: 'hook
priv:  "crook"
zlog:  23

And if we have a Flummer value and want the value in it's phlux field? We use the field accessor

(.phlux (Flummer 'hook "crook" 23))

evaluates to the value 'hook.

There's one more trick here with fields. Given a Flummer value, we can create a new Flummer value by replacing a fields value using the a field accessor like so

(let [flm (Flummer 'hook "crook" 23)]
  (.zlog flm 3400))

This let expression evaluates to a new Flummer value with fields

phlux: 'hook
priv:  "crook"
zlog:  3400

and the Flummer value flm was automatically garbage collected because it was no longer used.

But why ...

Since you're reading this post, I'm assuming you're an elite programmer and don't need me to explain why you might want to define you're own types. OTOH, you might be wondering why the title is Product Types. That because you can restrict what types of values can be assigned to the fields. So let's take a look at this totally hypothetical type

2: (deftype HTML [tag attributes contents]
3:   (assert (instance? String tag))
4:   (assert (instance? HashMap attributes)))

For this type, the tag field must always be a string and the attributes must be a HashMap. But the contents field is wide open. It can be anything. So if you try to create an HTML value with a symbol as a tag rather than a string, it's going to blow up at compile time.

6: (main [_]
7:       (HTML 'img {} nothing))

does this

*** Failed type assertion at try.toc: 7.
Expected 'String', got 'Symbol'.
Assertion from
 boom.toc: 3

And one more point for clarification

2: (deftype XML [tag attributes contents]
3:   (assert (instance? Symbol tag))
4:   (assert (instance? HashMap attributes)))

Different types can have the same names for fields. And those fields can be of different types even if the names are the same.

And why is this called a product type? This post has gone long enough, so I'm going to fob you off to someone else to answer that.