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
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()
}
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.
The sealed class in Kotlin has been designed to be able to represent sum types, as it happens with the sealed trait in Scala.
Example:
sealed class OrderStatus {
object Approved: OrderStatus()
class Rejected(val reason: String): OrderStatus()
}
The key benefit of using sealed classes comes into play when you use them in a when expression for the match.
If it's possible to verify that the statement covers all cases, you don't need to add an else clause to the statement.
private fun getOrderNotification(orderStatus:OrderStatus): String{
return when(orderStatus) {
is OrderStatus.Approved -> "The order has been approved"
is OrderStatus.Rejected -> "The order has been rejected. Reason:" + orderStatus.reason
}
}
There are several things to keep in mind:
In Kotlin when performing smartcast, which means that in this example it is not necessary to perform the conversion from OrderStatus to OrderStatus.Rejected to access the reason property.
If we had not defined what to do for the rejected case, the compilation would fail and in the IDE a warning like this appears:
'when' expression must be exhaustive, add necessary 'is Rejected' branch or 'else' branch instead.
when it can be used as an expression or as a statement. If it is used as an expression, the value of the satisfied branch becomes the value of the general expression. If used as a statement, the values of the individual branches are ignored. This means that the compilation error in case of missing a branch only occurs when it is used as an expression, using the result.
This is a link to my blog (spanish), where I have a more complete article about ADT with kotlin examples: http://xurxodev.com/tipos-de-datos-algebraicos/
One would be doing something like this:
sealed class Either<out A, out B>
class L<A>(val value: A) : Either<A, Nothing>()
class R<B>(val value: B) : Either<Nothing, B>()
fun main() {
val x = if (condition()) {
L(0)
} else {
R("")
}
use(x)
}
fun use(x: Either<Int, String>) = when (x) {
is L -> println("It's a number: ${x.value}")
is R -> println("It's a string: ${x.value}")
}
Related
I'm building a Kotlin microservice that processes events of various types and publishes them out to external partner systems. I have all of the event classes implementing a common interface called GenericPartnerEvent:
interface GenericPartnerEvent
data class EventTypeOne(
val key: String,
val payload: String
) : GenericPartnerEvent
I then have a series of event handler classes, one per external partner, that are defined like this:
interface PartnerEventHandler<in T : GenericPartnerEvent> {
fun handleEvent(event: T)
}
class PartnerOneEventTypeOneHandler : PartnerEventHandler<EventTypeOne> {
...
}
I want a generic way to fetch the event handler class of the correct type given a partner and event type; something like:
fun getEventHandler(partner: String, eventType: String): PartnerEventHandler<GenericPartnerEvent> {
if (...something...) return PartnerOneEventTypeOneHandler()
}
But what I inevitably get is something like:
Type mismatch.
Required: PartnerEventHandler<GenericPartnerEvent>
Found: PartnerOneEventTypeOneHandler
Is there a way to do this?
No, since your PartnerEventHandler type is contravariant (in), a subtype's type cannot be upcast. A PartnerEventHandler<EventTypeOne> cannot accept any subtype of GenericPartnerEvent as an input, so a PartnerEventHandler<EventTypeOne> is not a subtype of PartnerEventHandler<GenericPartnerEvent>.
Since you retrieve handlers using a String representation of the type, I don't see how generics buy you anything here anyway. The handle function might as well take an input type of Any and reject incorrect types with an exception or by returning null or something.
FWIW, this approach seems to pass muster with the compiler:
inline fun <reified T : GenericPartnerEvent> getHandlerForPartner(partner: String): PartnerEventHandler<T>? {
return when (partner) {
"partner-one" -> when (T::class) {
EventTypeOne::class -> PartnerOneEventTypeOneHandler() as PartnerEventHandler<T>
EventTypeTwo::class -> PartnerOneEventTypeTwoHandler() as PartnerEventHandler<T>
else -> null
}
"partner-two" -> when (T::class) {
EventTypeOne::class -> PartnerTwoEventTypeOneHandler() as PartnerEventHandler<T>
EventTypeTwo::class -> PartnerTwoEventTypeTwoHandler() as PartnerEventHandler<T>
else -> null
}
else -> null
}
}
It does give me a warning: Unchecked cast: PartnerOneEventTypeOneHandler to PartnerEventHandler<T>. So I guess we'll see what happens at runtime.
I'm trying to sort a list of objects - lets call them StockRows, by their values, which all implement
interface DetailedStockCell <in T: DetailedStockCell<T>> : Comparable<T>
for example, this class represents a value in StockRow:
data class SharesCell(val shares: Int?) : DetailedStockCell<SharesCell> {
override fun compareTo(other: SharesCell): Int {
return this.shares.compareTo(other.shares)
}
}
Now, this is StockRow - with all it's values. It also contains a Map to associate each value to an index.
data class StockRow(
val symbolCell: SymbolCell,
val sharesCell: SharesCell,
val priceCell: PriceCell,
val totalGainCell: TotalGainCell,
val percentOfPortfolioCell: PercentOfPortfolioCell
) {
val columnIndex : Map<Int, DetailedStockCell<*>> = mapOf(
0 to symbolCell,
1 to sharesCell,
2 to priceCell,
3 to totalGainCell,
4 to percentOfPortfolioCell
)
But when I try to sort a List of StockRows by a selected values via columnIndex map, sortBy{} fails to infer the sorted type
val masterList: List<StockRow> = //whatever list
fun sortStocksBy(selectedColumn: Int) : List<StockRow>{
return masterList.sortedBy { stockRow -> stockRow.columnIndex[selectedColumn] }
}
Error:
Type parameter bound for R in inline fun <T, R : Comparable<R>> Iterable<T>.sortedBy(crossinline selector: (T) -> R?): List<T>
is not satisfied: inferred type DetailedStockCell<*> is not a subtype of Comparable<DetailedStockCell<*>>
It's the star projection argument that needs to be recursively fulfilled.
Now, I can get around this by not using Generics at all, and just using casting in each implementation of DetailedStockCell, But I'd like to get this working somehow with Generics.
From what I understand so far, is that there is a clash between in and out type bounds.
Comparable requires an in bound for it's type, but the map holding the values must have an out Any bound to be read successfully by sortBy{}. I mean - this almost works, except that now T in Comparable<T> is shouting it need to be in:
interface DetailedStockCell <out T> : Comparable<T>
data class DetailedStockRow(...){
val columnIndex: Map<out Int, DetailedStockCell<Any>> = mapOf(...)
}
I feel like I'm missing something simple here, so any wise help is appreciated!
You can't really combine sorting and generics like this. Sorting requires knowing the type to sort by so the method signature can be known at runtime, but this isn't possible with generics at runtime because of type erasure.
The existence of the DetailedStockCell doesn't really accomplish anything. sortedBy wants something that's comparable to its own type, but something being a DetailedStockCell doesn't guarantee that to the compiler. It could be comparable to some other class that implements it, but not to instances of the same class. I would just remove that interface and have each cell type implement Comparable<itsOwnType>.
So you need to specifically sort each type with its concrete type. You can drop the columnIndex map and make a function like this:
fun Iterable<StockRow>.sortedByColumn(columnIndex: Int): List<StockRow> {
return when (columnIndex) {
0 -> sortedBy(StockRow::symbolCell)
1 -> sortedBy(StockRow::sharesCell)
2 -> sortedBy(StockRow::priceCell)
3 -> sortedBy(StockRow::totalGainCell)
4 -> sortedBy(StockRow::percentOfPortfolioCell)
else -> error("Nonexistant column: $columnIndex")
}
}
I came across something and wondered all the time why you should do this.
You implement an interface in Kotlin through a simple function type:
"It is possible for a class to implement a function type as if it were an interface. It must then supply an operator function called invoke with the given signature, and instances of that class may then be assigned to a variable of that function type:"
class Divider : (Int, Int) -> Double {
override fun invoke(numerator: Int, denominator: Int): Double = ...
}
But why should I do this? Why should I add an interface in that way? I think its only possible to add one function and not more.
Or is it an advantage that I can implement a function with a function body and not only the function head like in normal interfaces? I think it is possible in Java to add default methods to interfaces with a function body. So maybe it is something like that?
Function as a class can have state. For example you could store the last invocations and use the history as a cache:
class Divider : (Int, Int) -> Double {
val history = mutableMapOf<Pair<Int, Int>, Double>()
override fun invoke(numerator: Int, denominator: Int): Double {
return history.computeIfAbsent(Pair(numerator, denominator)) {
numerator.toDouble() / denominator.toDouble()
}
}
}
fun main() {
val divider = Divider()
println(divider(1,2))
println(divider(2,3))
println(divider.history)
}
It is probably not very useful to write a class that only implements a function type interface; however, it might be useful to write a class that can among other things be used in place of a function.
An example from the standard library is the KProperty1 interface. You can write code like this:
data class C(val id: Int, val name: String)
val objs = listOf(C(1, "name1"), C(2, "name2"), C(3, "name3"))
val ids = objs.map(C::id)
Here, C::id is a property reference of type KProperty1<C, Int>, and it can be used as an argument to List.map in place of a lambda because KProperty1<C, Int> extends (C) -> Int. However, KProperty1 has a lot of other uses besides being passed as a function.
I (often) have a resource with two states, pre-created and post-created, where both states have the same fields except for an id field. id is null in the pre-created state and non-null in the post-created state.
I would like to define and use this resource in a clean and type-safe way.
It's common to represent this ID field as a nullable, which handles both scenarios with minimal boilerplate in the class definition. The problem is that it creates a lot of boilerplate in the business logic because you can't assert whether a resource is pre-created or post-created by looking at its type.
Here is an example of the nullable approach:
data class Resource(val id: String?, val property: String)
This is simple to define, but not as simple to handle with due to lack of compile-time guarantees.
Here's an example of a more type-safe approach:
sealed class Resource(val property: String) {
class WithoutID(property: String): Resource(property)
class WithID(val id: String, property: String): Resource(property)
}
This allows me to pass around Resource.WithID and Resource.WithoutID, which have all the same fields and methods, except for id.
One inconvenience with this type-safe approach is that the resource definition code gets quite bloated when you have many property fields. This bloating makes the code harder to read.
I'm wondering if there's an alternative approach with less boilerplate, or if Kotlin has any features that make this kind of thing simpler.
What about defining
sealed class MayHaveId<T> { abstract val record: T }
class WithId<T>(val id: String, override val record: T): MayHaveId<T>()
class WithoutId<T>(override val record: T): MayHaveId<T>()
class Resource(val property: String)
// and other similar types
and using WithId<Resource> and WithoutId<Resource>? In Scala you could add an implicit conversion from MayHaveId<T> to T, but not in Kotlin, alas, nor can you write : T by record. Still should be clean enough to use.
One of the options is to get into composition relying on properties inside interfaces.
interface Resource {
val property: String
}
interface WithId : Resource {
val id: Int
}
interface WithOtherField : Resource {
val otherField: Any
}
class WithoutIdImpl(override val property: String) : Resource
class WithIdImpl(override val id: Int, override val property: String) : WithId
class WithIdAndOtherField(
override val id: Int,
override val otherField: Any,
override val property: String) : WithId, WithOtherField
I didn't get from your example, how you're going to switch between two states of Resource. So probably there is a gap to overcome.
Probably, Smart casts will allow to switch states.
Let's say I have a function:
fun doSomething(vararg pairs: Pair<String, *>) {
// Do things with the pairs
}
The problem with this approach is that it allows any type for the second half of Pair (e.g. Pair<String, CustomType1>).
What if I only want to allow a finite number of types, how would I achieve that?
If the function had a simpler signature, I could achieve the restriction via overload, like so:
fun doSomethingSimpler(param: Boolean) {
// Boolean implementation
}
fun doSomethingSimpler(param: Int) {
// Int implementation
}
// etc.
If the restricted type "set" was in my control, I could use an interface or a sealed class to achieve this. E.g.
sealed class Root
class Child1 : Root()
class Child2 : Root()
fun doSomethingICanControl(param: Root) {
// Root implementation
}
Yet what if I don't have control over the types or they are primitive, how do I prevent * from allowing everything through?
I know I could use smart-casts to get run-time safety, but can this be done at compile time?
Or does the language disallow it?
Edit 1
I know I could create my own box types (e.g. MyBoolean) and use a common interface or sealed class, but that would be boilerplate that everyone would have to write every time they needed to.
Edit 2
To be clear, I'd like to be able to make an invocation like so:
doSomething(
"key1" to false,
"key2" to "value2",
"key3" to 86
)
... I.e. Have a mixed set of "second" (of Pair) types.
So to sum it quickly up:
You want to call methods from a library that expects Pair<String, *>,
but limit the possible values that * can be.
TL;DR: What you are trying to accomplish is not possible without some kind of wrapper, because
We have no Sum-Types in Kotlin, so no way to tell the compiler that you expect an Int or a Double or a Float and nothing else
If a library-method expects something to be Pair<String, *>, there is no way for us to tell the compiler, that we just want to be able to give it a String instead of *
One way to get that behaviour is to create a Decorator (Decorator Pattern), e.g. create your own extension methods that allow only a subset
class Foo {
//Allows everything
fun doSomething(param: Pair<String, *>)
}
//Now lets create our own extension methods
fun Foo.doSomethingWithInt(param: Pair<String, Int>)
fun Foo.doSomethingWithBoolean(param: Pair<String, Boolean>)
fun Foo.doSomethingWithString(param: Pair<String, String>)
Or if you dont want to be able to call Foo.doSomething() you
can create a Decoractor-Class:
class FooDecorator {
val foo = Foo()
fun doSomething(param: Pair<String, Int>) { }
}
And the following example is not possible without some kind of Wrapper, because there are no Sum-Types in Kotlin:
doSomething(
"key1" to false,
"key2" to "value2",
"key3" to 86
)
What you could do is something like:
At first, create your own JSONItem type and add Extension-Methods to Types that can be used as one
class JSONItem<T> private constructor (item: T)
fun Int.asJSONItem() = JSONItem(this)
fun String.asJSONItem() = JSONItem(this)
fun Boolean.asJSONItem() = JSONItem(this)
Then you are able to do something like that:
//Your own personal doSomething
fun doSomething(varargs: param: Pair<String, JSONItem>) {
//Call the real doSomething()
doSomething(param.map { Pair(it.first, it.second.item) }}
}
doSomething(
"key1" to false.asJSONItem(),
"key2" to "value2".asJSONItem(),
"key3" to 86.asJSONItem()
)
Denotable union and intersection types are not currently supported in Kotlin (as of 1.1.x).
This is the relevant issue.