Functional Reactive Domain Modeling - Part 1 - ADTs in Rust

Welcome to the Functional-Reactive Domain Modeling in Rust series! I’m going to cover the basics of functional-reactive programming in Rust with a domain focus. While I won’t be covering the basics of domain-driven design except where necessary to explain things in Rust, if you know Rust, it should be easy enough to pick up.

Since this is a series, each post will build on the last. See the available posts at the end of each post.

So let’s get started with Abstract Data Types.

What the hell are ADTs?.

Hold on to your caps, y’all, because we are getting into theoretical territory. The easiest way to think about ADTs is that they are a simplified, non-language-specific way of looking at the data and operations in your domain model.

For example, we talk about a car, which might have an engine, a make and model, a color, etc. And the things we can do to a car are things like “unlock the door”, “start the engine”, “steal your neighbors Rivian”. These are all operations you can do to a car. Now maybe that last one is only for a specific instance of a car, but you get the picture.

Let’s look at a basic example that we will use for this series. It would look something like this:

Account {
   number account_number
   number balance

   credit(number amount)
   debit(number amount)
   balance() number
}

See, it’s simple to define a ADT. And you’d want to do this for every type in your domain.

Product Types

Okay, back to the theoretical side again, this time into a small amount of type theory. Our example above is called a product type. Dear gods, why?

Let me see if I can explain a bit, and gloss over a whole lot of theory that you don’t really need to write software. When we think about types, we treat them as mathematical sets. Like say Ι is the set of all integers.

If we look at Account above, account_number can be any number in Ι and balance can also be any number in Ι.

And for any given account_number, balance can still be any number in Ι; so the total size of the set of potential values in Account are size(Ι) • size(Ι), hence it’s a product type.

Sum Types

There is one other special type you need to know, called a sum type. I like to think of it as a “choice” type.

Like if we were representing a vehicle in our domain, we might want to represent it as one of car, bike, motorcycle, or hoverboard. Each of them would contain types specifically dedicated to that type of vehicle.

So the size of the set of potential vehicles is a choice between each of the vehicles types, so we add those together to get the final size of the vehicle set.

Where’s the rust?

Okay, enough theory, and I swear, there will be less and less theory as we go on, so let’s get some Rust written.

Product types in Rust are usually structs. We’ll cover their operations in the next post.

So for our example:

struct Account {
    account_number: u64,
    balance: u32,
}

Now, what if we need to make sure there is a difference in the domain between savings accounts and checking accounts? Well, that’s where sum types come in!

enum CustomerAccount {
    Checking(Account),
    Savings(Account),
}

As you can see, we have an enum that represents the sum type because it’s either a checking account or a savings account, but not both.

That’s it for the basics of ADTs in rust. Next post, we’ll cover two different ways to implement the operations in our domain model.