ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Phantom Types in Kotlin

Danny Preussler
ProAndroidDev
Published in
2 min readDec 1, 2019

https://www.flickr.com/photos/155276005@N06/44239568792

While working on my KotlinConf presentation about types I stumbled upon this post by Maximiliano Felice about Phantom types with Scala.

The idea: prevent objects from having an illegal state or forbid illegal operation at compile time.

The example given was a class Door. A door is either open or closed. In Kotlin this would look like:

sealed class DoorState
object Open: DoorState()
object Closed: DoorState()
class Door(val state: DoorState) {
fun open() = Door(Open)
fun close() = Door(Closed)

}

You could argue that one could make the state mutable instead of returning a new Door all the time but the example came from the functional world where immutability is a core principle.

The problem we face nonetheless is that we could call open on a door that is already opened! That should not be possible!

Use Inheritance?

You could also argue that we could have two variations of the Door via inheritance instead of modelingstate via composition. But is pointed out in the original post as the source of the issue. When deciding between composition and inheritance we mostly consider composition first, also as we can only inherit from one class in most languages.

Solutions?

So how do we forbid the wrong operation?

We could check the current state at runtime:

fun open() = 
if (state == Open) throw IllegalStateException() else Door(Open)

But it would be nice if we could use the power of the compiler.
So a call like: Door(Open).open() should fail at compile time!

Maximiliano is using Phantom types as solution. He quotes them as:

A phantom type is a manifestation of abstract type that has no effect on the runtime.

Scala version

The solution in Scala uses the implicit keyword that allows context-bound references:

case class Door[State <: DoorState](){
def open(implicit ev: State =:= Closed) = Door[Open]()
def close(implicit ev: State =:= Open) = Door[Closed]()
}

Here open can only be called when the state of the Door is Closed.

Kotlin does not have this language feature, so I was thinking of how we can achieve the same.

Kotlin version

I came up with this:

class Door<out T: DoorState>(val state: T)fun Door<Closed>.open() = Door(Open)
fun Door<Open>.close() = Door(Closed)
  • make the Door generic
  • create two extensions functions on specific variations of the Door.

Now, these two are valid expressions:

Door(Closed).open()
Door(Open).close()

But these two would fail to compile with Type mismatch.

Door(Open).open()
Door(Closed).close()

Voilà!

Types are a very powerful to avoid errors as early as possible. This was brought up many decades ago:

Well-typed expressions do not go wrong
(Journal of Computer and System Sciences, Robert Milner, 1978)

Check out my KotlinConf presentation to hear more.

This was just a small experiment. So let me know what you think. Maybe I even missed a simpler version.
PS: thanks to @abbasi

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Danny Preussler

Android @ Soundcloud, Google Developer Expert, Goth, Geek, writing about the daily crazy things in developer life with #Android and #Kotlin

Responses (8)

Write a response