DEV Community

Nicola Todorov
Nicola Todorov

Posted on • Edited on

Designing with Wrapper types

It compiles. It runs. So what's wrong?

Let's start with a puzzle.

void resetPassword(String email) {
    // Logic for sending reset password email
}
Enter fullscreen mode Exit fullscreen mode

What is the problem with this method?

Answer
It might be used like so
resetPassword(user.getPassword());
Enter fullscreen mode Exit fullscreen mode

You might be thinking I've gone off the rails for claiming there is problem in piece of code that has no logic in it, but there is a bug waiting.
If you've spotted the problem you sure know that it will be very embarrassing if you need to fix such a bug.

Let's have another.

record User(String name, String email, String address) { }
Enter fullscreen mode Exit fullscreen mode

What is wrong this time?

Answer
When instantiating, arguments can be swapped around.
new User("todoniko", "Sofia, Bulgaria", "drama@mail.com");
Enter fullscreen mode Exit fullscreen mode

It compiles. It runs. No warnings. No type errors. But the potential for bug is there.

Actually it is a whole class of bugs that the compiler could prevent - but doesn't, because we never told it how.

Addressing the root cause

As with many things in programming we have jargon for this - it is a code smell it is primitive obsession.
We can encode way too many things with a String - password, username, email, file path etc. But all those instances mean different things and communicate distinct purpose. In our heads we infer the meaning from the names and expose the encoding for everyone to (ab)use. We have failed at abstraction.

Let's remedy that by wrapping/hiding the encoding/primitive type and expose only the meaning.

record Email(String value) {}
record UserName(String value) {}
record Address(String value) {}
record User(UserName name, Email email, Address address) { }

void resetPassword(Email email) {
    // Logic for sending reset email
}
Enter fullscreen mode Exit fullscreen mode

What have we achieved?

Now the potential for bugs from passing the wrong argument has come down significantly. When our types are clear, bugs become rare.
Now our types communicate meaning instead of encoding and this is equally useful for both humans and AI agents.

Conclusion

The problem and solution in this article are not novel in any way. Actually they are too old to keep pretending that they do not exist.

I have drawn inspiration from several sources and if you are daring enough to step out of the mainstream languages we use today you can have a look at:

Please, don't be primitive.

What is next

In the next article we will upgrade our wrapper type so that we are not going to care if it needs validation. If it exists it is valid. The instance of the type will be the only evidence we need. We will achieve correctness-by-construction with opaque types.

Top comments (0)