Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin and discriminated unions (sum types)

Does Kotlin have anything like discriminated unions (sum types)? What would be the idiomatic Kotlin translation of this (F#):

type OrderMessage =     | New of Id: int * Quantity: int     | Cancel of Id: int  let handleMessage msg =      match msg with         | New(id, qty) -> handleNew id qty         | Cancel(id) -> handleCxl id 
like image 941
ehnmark Avatar asked Feb 24 '15 11:02

ehnmark


People also ask

Does kotlin have sum types?

In Kotlin, Sum Types are supported through inheritance, using sealed classesand data classes which inherit from said sealed class. It looks like a bit of a hack, and perhaps it is. It seems like it wasn't built into the language and it was more of a discovered feature than a planned feature.

Does kotlin have union types?

Kotlin supports intersection types internally (KT-10741), however they aren't denotable in source code. And there is no support for union types at all.

What is sealed classes in Kotlin?

Sealed classes are abstract by itself, and cannot be instantiated directly. So let's take a pause here. If Sealed classes are abstract by default, why can't we use an abstract class instead of a sealed class in the first place?

Does C# have sum types?

Most programming languages have product types, so it's the lack of sum types that seems to make the difference between languages like C# and Java on the one side, and languages like F#, OCaml, or Haskell on the other side. You can, however, achieve the same objective with object-oriented design.


2 Answers

Kotlin's sealed class approach to that problem is extremely similar to the Scala sealed class and sealed trait.

Example (taken from the linked Kotlin article):

sealed class Expr {     class Const(val number: Double) : Expr()     class Sum(val e1: Expr, val e2: Expr) : Expr()     object NotANumber : Expr() } 
like image 187
Adeynack Avatar answered Sep 24 '22 01:09

Adeynack


The common way of implementing this kind of abstraction in an OO-language (e.g. Kotlin or Scala) would be to through inheritance:

open class OrderMessage private () { // private constructor to prevent creating more subclasses outside     class New(val id: Int, val quantity: Int) : OrderMessage()     class Cancel(val id: Int) : OrderMessage() } 

You can push the common part to the superclass, if you like:

open class OrderMessage private (val id: Int) { // private constructor to prevent creating more subclasses outside     class New(id: Int, val quantity: Int) : OrderMessage(id)     class Cancel(id: Int) : OrderMessage(id) } 

The type checker doesn't know that such a hierarchy is closed, so when you do a case-like match (when-expression) on it, it will complain that it is not exhaustive, but this will be fixed soon.

Update: while Kotlin does not support pattern matching, you can use when-expressions as smart casts to get almost the same behavior:

when (message) {   is New -> println("new $id: $quantity")   is Cancel -> println("cancel $id") } 

See more about smart casts here.

like image 39
Andrey Breslav Avatar answered Sep 20 '22 01:09

Andrey Breslav