АT
Size: a a a
АT
I
if (is Err t) { /* t: Err */ } else { /* t: a */ }
IP
VP
ch
AM
A | B
— это A
или B
, A & B
— это A
и B
одновременно. То есть:A
на A | B
— это расширение типа, ты позволяешь использовать новые значения кроме тех, которые были позволены ранее.A
на A & B
— это сужение типа, ты запрещаешь использовать некоторые значения, которые раньше были позволены.fun <T> f(a: List<T>): Any
на fun <T> f(a: Collection<T>): Any
fun <T> f(): Collection<T>
на fun <T> f(): List<T>
IP
KD
A | B
— это A
или B
, A & B
— это A
и B
одновременно. То есть:A
на A | B
— это расширение типа, ты позволяешь использовать новые значения кроме тех, которые были позволены ранее.A
на A & B
— это сужение типа, ты запрещаешь использовать некоторые значения, которые раньше были позволены.fun <T> f(a: List<T>): Any
на fun <T> f(a: Collection<T>): Any
fun <T> f(): Collection<T>
на fun <T> f(): List<T>
KD
VP
I
A
к A | B
позволяет сократить код и не требует явного вызова конструкторов Left
и Right
монады Either
.f: A -> R
и мы хотим добавить новый тип в параметры. В случае union типов функция примет вид f: A | B -> R
, в случае же ADT она примет вид f: Either A B -> R
. Вроде бы всё одно, отличие только синтаксическое. Тогда в чём же проблема? Проблема в вызывающей стороне. Из-за того, что для Either
приходится явно вызывать конструктор, изменение сигнатуры несовместимо. Придётся менять весь пользовательский код. В случае же union типов такой проблемы нет.f: A -> R
на f: A -> R & T
не ломает пользовательский код. Поэтому, кстати, я приветствую обеими руками Dotty. Там хотя бы source совместимость будет сохранена.@JvmName
надо будет повесить. Вообще, такая проблема присутсвует и для примитивов, поэтому такое поведение соответствует нашему видению, что инлайн классы подобны примитивам. Убирание же Result и замена на сырой тип не бинарно-совместимое изменение, в отличие от некидания исключения. Конечно, исключения всё равно не видны в сигнатуре, а так хотя бы можно сказать, чтобы принимающая сторона проверила на существование значения. Но Result не решает проблему checked exceptions полностью. Из-за как раз проблем обратной совместимости. Идеальное решение должно её принимать во внимание.KD
VP
с#
A
заменится на A | B
, что выглядит несовместимым.I
I
I
АХ
newtype Left a = Left a
АХ
if (is Err t) { /* t: Err */ } else { /* t: a */ }
АХ