Котлин и размеченные союзы (типы сумм)

Есть ли в Котлине что-нибудь вроде размеченных объединений (типов сумм)? Каким будет идиоматический перевод этого слова на Котлин (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

person ehnmark    schedule 24.02.2015    source источник


Ответы (3)


Обычным способом реализации такого рода абстракции на объектно-ориентированном языке (например, Kotlin или Scala) было бы наследование:

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()
}

Вы можете передать общую часть в суперкласс, если хотите:

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)
}

Средство проверки типов не знает, что такая иерархия закрыта, поэтому, когда вы выполняете для нее сопоставление по регистру (when-выражение), оно будет жаловаться, что оно не является исчерпывающим, но это скоро будет исправлено.

Обновление: хотя Kotlin не поддерживает сопоставление с образцом, вы можете использовать when -выражения в качестве умного приведения, чтобы получить почти такое же поведение:

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

Дополнительную информацию об умном приведении см. здесь.

person Andrey Breslav    schedule 25.02.2015
comment
Привет, спасибо за ответ! В Scala я бы использовал sealed Trait OrderMessage и case class New(..) extends OrderMessage и т. Д. Я мог бы затем сопоставить шаблон для типов сообщений заказа и получить доступ к их полям того же типа (как в примере с F # выше). Есть ли шанс, что мы сможем сделать это с when в Котлине в ближайшее время? :) - person ehnmark; 25.02.2015
comment
comment
@AndreyBreslav будет жаловаться на полноту. Вы забыли поставить "не там"? В противном случае я не понимаю эту часть вашего ответа. - person HRJ; 28.08.2015
comment
@HRJ вроде да, я сделал - person Andrey Breslav; 02.09.2015
comment
В Kotlin теперь есть запечатанные классы, которые позволяют контролировать возможную иерархию и попросите компилятор проверить, исчерпаны ли все параметры в операторе / выражении when. Это решает проблему, о которой упоминают Андрей и @HRJ. - person Jayson Minard; 31.12.2015

подход Kotlin sealed class к этой проблеме очень похож на Scala sealed class и sealed trait.

Пример (взят из связанной статьи Kotlin):

sealed class Expr {
    class Const(val number: Double) : Expr()
    class Sum(val e1: Expr, val e2: Expr) : Expr()
    object NotANumber : Expr()
}
person Adeynack    schedule 19.08.2016

Класс sealed в Kotlin был разработан, чтобы иметь возможность представлять типы сумм, как это происходит с трейтом sealed в Scala.

Пример:

sealed class OrderStatus {
    object Approved: OrderStatus()
    class Rejected(val reason: String): OrderStatus()
}

Ключевое преимущество использования запечатанных классов проявляется, когда вы используете их в выражении when для совпадения.

Если можно убедиться, что оператор охватывает все случаи, вам не нужно добавлять в него предложение else.

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
   }
}

Следует помнить о нескольких вещах:

  • В Kotlin при выполнении smartcast, что означает, что в этом примере нет необходимости выполнять преобразование из OrderStatus в OrderStatus.Rejected для доступа к свойству reason.

  • Если бы мы не определили, что делать для отклоненного случая, компиляция завершится ошибкой, и в среде IDE появится такое предупреждение:

Выражение "когда" должно быть исчерпывающим, добавьте вместо него необходимую ветвь "отклонено" или "иначе".

  • когда его можно использовать как выражение или как утверждение. Если оно используется как выражение, значение удовлетворенной ветви становится значением общего выражения. При использовании в качестве инструкции значения отдельных ветвей игнорируются. Это означает, что ошибка компиляции в случае отсутствия ветки возникает только тогда, когда она используется как выражение, используя результат.

Это ссылка на мой блог (на испанском), где у меня есть более полная статья об ADT с примерами котлина: http://xurxodev.com/tipos-de-datos-algebraicos/

person xurxodev    schedule 05.10.2018