Go: Making state explicit using the type system
Tags: go, functional, oop
Published: 2022-05-15 21:53:53

In the [last post](https://dev.to/mse99/creating-simple-self-documenting-types-in-go-3d8i) we learned how to create self documenting data types, we used the type system to create data types that would be in a constant valid state (because of validation on construction and when updating the value), now we're going types to more easily express states that our data could be in.

Consider the following types:

```go
type chatroom struct {
    id      uuidStr
    name    nameStr
    members membersList
}

type membersList []member

type member struct {
    id   uuidStr
    role membership
}

type membership int

const (
    owner membership = iota
    removed
    banned
    regular
)
```

Most of them are self explanatory the one we're interested in is `member` because depending on the value of `role` field the other data inside a `member` might be interpreted differently and require us to *preserve* different *invariants*, this is a pretty common situation we say that `member` has state because how we process `member` differs due to the value of one or more of the fields.

This simple example doesn't do this problem justice, imagine a much bigger data structure like a shipping order for example you'd probably have a lot of fields with different data filled at different times depending on the state of the order, at best you'd probably have an enum to tell what state the order is in or at worst you'd have a few boolean flags for each state.

Generally speaking there are two ways to fix this problem:

* The functional way
* The object oriented way

#### The functional way

In functional programming data is just data, nothing more nothing less.

All objects in our programs have a type, and a type is a name for a set of values, a type can be either a product type (sometimes known as a composite, or record type) or it can be a sum type (sometimes known discriminated union, OR type, choice type)

**product and sum types (RUST)**

```rust
// Sum type
enum AwesomeFruit {
    Apple,
    Banana,
    Orange,
}

// Product type
struct Fruit {
    name: String,
    calories: i32,
}
```

In a functional language you'd declare a choice type, create a few objects with it and then when it's time to preform some data processing, we use pattern matching to match the the objects against type patterns like this:


**matching an object of choice type against patterns (RUST)**

```rust
match apple {
    AwesomeFruit::Apple => println!("You created an apple"),
    AwesomeFruit::Banana => println!("You created a banana"),
    AwesomeFruit::Orange => println!("You created an Orange"),
    };
```

The compiler will refuse to compile code if we don't handle all the possible permutations (or at least provide a default catch all handler, you can do that in rust with `_ => println!("")`)

Choice types are pretty powerful, sadly go doesn't support sum types ([although there's current debate over adding some form of support for them](https://github.com/golang/go/issues/19412)), most go code out there is written in a OOP/imperative style, but don't let that discourage you there's a lot of people that write functional programs in go, functional go code is idiomatic.

Even though go doesn't have choice types, or pattern matching, there are multiple ways of faking them and we're going to look at the most common way which involves modeling the choice type as an interface type and have the variants/permutations of the type be objects that implement that interface:

```go
type member interface{ member() }

type owner   struct{ id uuidStr }
type banned  struct{ id uuidStr }
type removed struct{ id uuidStr }
type regular struct{ id uuidStr }

func (*owner) member() {}
func (*banned) member() {}
func (*removed) member() {}
func (*regular) member() {}
```

We can then make constructors for each of the variants

```go
func makeOwner(id uuidStr) member {
    return &owner{id}
}
func makeRegular(id uuidStr) member {
    return &regular{id}
}
func makeBanned(id uuidStr) member {
    return &banned{id}
}
func makeRemoved(id uuidStr) member {
    return &removed{id}
}
```

And whenever we have a member object we can "match" against it using a type switch:

```go
switch v := m.(type) {
    case owner:
    // Do stuff for the owner
    case regular:
    // Do stuff for the regular
    case banned:
    // Do stuff for the banned
    case removed:
    // Do stuff for the removed

    default:
      panic("missing handling for " + fmt.Sprintf("%T"))
}
```

This mimics pattern matching, but it has some important downsides to consider:

* This isn't pattern matching and isn't as powerful as pattern matching, also the compiler won't check that you've handled all the variants in the type switch.
* The default guard above will only be triggered in runtime causing a panic, panicking is an anti-pattern in go and even in functional programming.

We can fix these two problems by relying on static analyzers such as [go-sumtypes](https://github.com/BurntSushi/go-sumtype)


#### The object oriented way 

In object oriented programming (unlike functional programming) data is protected & encapsulated and is grouped with public behavior that mutates the data.

To deal with an object that has many possible states, we use the strategy pattern, this pattern allows an object to easily have different behaviors by extracting the API of the required behaviors into a class or interface and then have implementations inherit that class or interface and implement the methods, we can then dynamically load instances of the subclass at runtime and use them.

You probably already used the strategy pattern, it's pretty common in go!

```go
f, err := os.Open("foo")
if err != nil {
    return err
}
defer f.Close()

sink := make([]byte, 250)
defer.Read(sink)
```

`*os.File` implements the `io.Reader` interface, which only has one method called `Read` any object that implements `Read` can be used as an `io.Reader`.

in a similar fashion we can create objects that implement a `member` interface and then define whatever services we want from a member on it

```go
type interface member {
    canSendMsg() bool
}

type banned struct {}
func (*banned) canSendMsg() bool { return false }

type removed struct{}
func (*removed) canSendMsg() bool { return false }

type owner struct {}
func (*owner) canSendMsg() bool { return true }

type regular struct{}
func (*regular) canSendMsg() bool { return true }
``` 

You'll see people use the OOP solution more often, and it's a lot more common in the standard library too, so learning how to use polymorphism and other OOP stuff is a must for any golang dev even you want to code in a functional style.