Kotlin and Algebraic Data Types
As Kotlin steadily spreads into the masses more and more Java developers are being exposed to “new” concepts which were actually available in other languages for years now. Algebraic Data Types (ADT for short) is one of these concepts.
What are ADT?
Simply spoken, an ADT is a type which is represented by several other subtypes. Still sounds too complicated? Okay. How about this one?
Wait, isn’t this a Java enum? Yes it is. It also happens to be a very simple example of ADT. DeliveryStatus
is a type which is indeed represented by several other types (in this case enum
entries). But wait, here is more!
Actually, it turns out that when a parcel is dispatched we would also like to know a tracking number.
Here is how an implementation could look like in Java.
Doesn’t look that bad, right? Yet, it is flawed in one important way — trackingNumber
is used only if Stage
is DISPATCHED
and is null
in every other case. This is tolerable if the amount of such “special” cases is small, but as code evolves more and more such fields emerge.
Kotlin to the rescue
Kotlin helps us to solve this problem by providing sealed
classes. Here is how you would define an ADT in Kotlin.
Let’s see what’s going here.
- We define a
sealed
class. This means that no other class outside of this Kotlin file can extendDeliveryStatus
which in turn means that the compiler knows about every possible subtype ofDeliveryStatus
. We’ll get to that at a later point. - We define some types which extend
DeliveryStatus
. - We use an
object
if there is no point in having different instances of this type.
Notice that now only DeliveryStatus.Dispatching
contains a tracking number. No other types have to know about it.
Using ADT
That is all great but how do we use our DeliveryStatus
now? One of the ways would be to use when
operator which makes working with ADTs especially convenient.
Important things to notice:
- There is no need to cast
status
toDispatched
because the compiler already knows that. - We have
return
even though there is no return type specified. That is because every function returns theUnit
by default. - It does not compile.
Wait what? You read it right — it indeed does not compile, but can you guess why?
The reason for compilation error also happen to be another advantage of ADTs. It does not compile because we forgot to specify Delivered
status in our when
clause. Ouch!
Now it works. ADTs are helping you to ensure that when new types are added you handle them properly. But of course you don’t have to do that and if you’ll remove return
from showDeliveryStatus
it will compile just fine even without Delivered
.
In the end
- ADTs are very helpful in cases when you need a single value which represents one of several possible states.
- It is easy to get started. Just write your next class using ADT. No need to change anything in the existing code.
- ADTs are not a replacement for all model classes, just the ones which have subtypes.
enum
is still an ADT.
Let us know what you think in the comments!