Domain Modeling For Better Contracts

1 | var api = { |

### Higher-Kinded Types and You

First and foremost, let's discuss what a higher-kinded type actually is. (It's a type.) Once we have a better grasp on that, we will use it in code to make everything a little more clear. A higher-kinded type is simply a type which takes a type as an argument and returns a type. I know, that sounds weird. How does a number take a string and return a type? I asked the same thing. It turns out, however, that it's not nearly as foreign as it might seem. One very common type we rarely think about in Javascript which could easily be handled as a higher-kinded type is an array. An array is, itself, a type, but it contains values which are also typed. This means, if we had a language to express it, we could declare an array which contains only a single type. As it turns out, there are potentially infinite different types which are, or could be, higher-kinded. In this post we are going to look at just two: array and function. With the type signature language available with signet, we can explicitly declare an array type as needed. This means we can do things like the following.1 | var isArrayOfNumbers = signet.isTypeOf('array<number>'); |

1 | var api = { |

### Subtyping With Characteristics

Now we just have the 'number' type scattered everywhere throughout our code. Although this is better than nothing, it would be SO MUCH better if we actually knew something about those numbers. What do they mean? How are they used? What are the constraints? It turns out we have just the thing to remedy this pain, it's called characteristic functions. As we know from our earlier discussions on characteristics, we can add richness to our type system through set-describing predicate functions. (Protip: all predicates describe sets) Before we dive into creating new types willy-nilly, let's take a moment to account for the different number types we have. By properly identifying the actual domain language we care about, we can create better types which will allow us to clearly describe our application to people who might know nothing about it. Ultimately, we care about tax, price, amount of tax to pay (tax amount) and purchase total. If we were to simplify this list a bit, we can identify a couple of distinct bits of information. First let's consider tax. Tax is a percentage amount. Since, where I live, taxes are always greater than or equal to 0%, but always less than 100%, I am going to say tax is a percent value which will always fall between 0 and 1. For example, in San Diego, sales tax is currently around 8% or 0.08. Now, we can take a look at price, tax amount and purchase total. Each of these is a value which is related to a value an amount our customer will be paying. This means we can roll these all into some aspect of price. We will say a price value will be greater than or equal to 0. This describes our data pretty accurately for the moment, so let's go with that. With our base types sorted out in a way we can jump off from, we can start building characteristics. By clearly defining our characteristics, we give our new types programmatic meaning. Let's see what our basic characteristic functions will look like for price and percent.1 | function checkTax(value) { |

1 | signet.subtype('number')('tax', checkPercent); |

1 | signet.alias('taxAmount', 'price'); |

1 | var api = { |

### Duck Typing our Object

At this point, our API is pretty clear, but there is still one last type which just doesn't quite convey the information we want to know. Our array of purchases is still described, simply, as an array of objects. This could be much better, if only there were a way to check it. As it turns out, the Go language has popularized the notion of object similarity through duck typing and that is precisely what we are going to do here. If we know enough information, we can tell whether our object satisfies the Liskov substitution principle, and can be used in place of our intended object. Signet provides a means to perform duck typing as well, so we don't have to build our characteristic function from the ground up every time, because that could end up being a LOT of repeated code. Let's build a duck typing characteristic and finish up our API types.1 | let checkPurchase = signet.duckTypeFactory({ price: 'price', quantity: 'int' }); |

1 | api = { |