Дальше можно работать, например, в функциональном стиле, используя:
fun <A, B> Either<A, B>.onLeft(action: (A) -> Unit): Either<A, B>
fun <A, B> Either<A, B>.onRight(action: (B) -> Unit): Either<A, B>
Или использовать функции getOrNull, getOrElse, leftOrNull. В данном случае, get подразумевает получение right, потому что это полезные данные, а это основной сценарий использования.
Проблема решается заданием интерфейса и дополнительными функциями вокруг Either.
Правильный тип выведется за счет типа интерфейса.
interface MyService {
fun f(): Either<Error, String>
}
enum class Error {
NO_DATA
}
class MyImpl : MyService {
fun getData(): String?
override fun f() = either {
val data = getData()
ensure(data != null) {
NO_DATA
}
data
}
}
Более подробно такой подход к работе с ошибками можно в статье из документации Arrow
Можно, если указать явно тип. По умолчанию компилятор выводит тип в лоб.
В твоем примере a имеет тип Either.Left<Nothing?> - потому что указано значение null и не ясно это Int?, String? или Any?, компилятор сам по себе тип не расширяет, поэтому в данном примере он считает, что это самый минимальный тип и это Nothing?.
Для понятности я заменю строчку с нуллом на var a = Either.Left(1) , тогда тип будет Either.Left<Int> это то же самое, что и Either<Int, Nothing> по определению класса Left.
Чтобы все работало так, как ты хочешь, надо явно указать какие типы ожидаются и вывод будет работать.
var a: Either<Int?, String> = Either.Left(null)
a = Either.Right("string")
Если не указывать ни in, ни out, то дженерик является инвариантным. В таком случае будет между Desk<User> и Desk<Manager> нет никакой иерархии наследования. Экземпляр с наследником создать тоже получится, но без ковариантности не получится его использовать там, где ожидался Desk<User>
Дальше можно работать, например, в функциональном стиле, используя:
fun <A, B> Either<A, B>.onLeft(action: (A) -> Unit): Either<A, B>
fun <A, B> Either<A, B>.onRight(action: (B) -> Unit): Either<A, B>
Или использовать функции getOrNull, getOrElse, leftOrNull. В данном случае, get подразумевает получение right, потому что это полезные данные, а это основной сценарий использования.
Проблема решается заданием интерфейса и дополнительными функциями вокруг Either.
Правильный тип выведется за счет типа интерфейса.
Более подробно такой подход к работе с ошибками можно в статье из документации Arrow
Можно, если указать явно тип. По умолчанию компилятор выводит тип в лоб.
В твоем примере
a
имеет типEither.Left<Nothing?>
- потому что указано значениеnull
и не ясно этоInt?
,String?
илиAny?
, компилятор сам по себе тип не расширяет, поэтому в данном примере он считает, что это самый минимальный тип и этоNothing?
.Для понятности я заменю строчку с нуллом на
var a = Either.Left(1)
, тогда тип будетEither.Left<Int>
это то же самое, что иEither<Int, Nothing>
по определению класса Left.Чтобы все работало так, как ты хочешь, надо явно указать какие типы ожидаются и вывод будет работать.
Если не указывать ни in, ни out, то дженерик является инвариантным.
В таком случае будет между
Desk<User>
иDesk<Manager>
нет никакой иерархии наследования. Экземпляр с наследником создать тоже получится, но без ковариантности не получится его использовать там, где ожидалсяDesk<User>