Either è un tipo funzionale che rappresenta un valore di una delle due varianti possibili: Left per gli errori e Right per il successo. A differenza delle eccezioni, Either rende la gestione degli errori esplicita a livello di tipi e non richiede blocchi try-catch. Secondo Arrow, 2026, Either è ampiamente utilizzato nei progetti Kotlin per comporre operazioni che possono fallire, senza effetti collaterali.
Punti chiave
Either è un tipo di dato algebrico, preso in prestito dalla programmazione funzionale, che rappresenta esattamente uno di due possibili tipi. Nel contesto della gestione degli errori, la convenzione è: il tipo Left (Left) contiene una descrizione del problema, mentre il tipo Right (Right) contiene un risultato positivo.
Il concetto di Either proviene dal linguaggio Haskell e dalla categoria di tipi Either a b, dove a è il tipo del valore sinistro e b quello del valore destro. Nello sviluppo mobile, Either ha guadagnato popolarità grazie alla libreria Arrow per Kotlin e agli approcci funzionali in Swift.
Il vantaggio principale di Either rispetto alle eccezioni è l'assenza di percorsi di esecuzione nascosti. Una funzione che restituisce Either dichiara esplicitamente nella sua firma che può fallire. Il compilatore verifica che entrambe le varianti siano gestite.
A differenza del lancio di eccezioni, Either preserva la trasparenza del flusso di dati. Chiamare una funzione che restituisce Either non richiede try-catch dal lato chiamante — è sufficiente il pattern-matching o fold. Questo è particolarmente importante nelle architetture con approccio reattivo, dove ogni fonte di dati restituisce Either e le catene di trasformazione vengono costruite tramite map e flatMap.
Either è composto da due sottotipi: Left e Right. Un'istanza di Either può essere solo uno di essi in ogni momento. La firma tipica in Kotlin è Either<E, A>, dove E è il tipo di errore e A è il tipo di valore di successo.
Left rappresenta il caso di fallimento. A differenza delle eccezioni, Left non interrompe il flusso di esecuzione — restituisce semplicemente un valore che deve essere gestito. Il tipo di errore può essere qualsiasi cosa: String, Int, una classe sealed personalizzata o un modello di dominio.
Right contiene il risultato corretto dell'operazione. Il nome riflette la correttezza — per convenzione, Right significa successo. Le trasformazioni come map e flatMap vengono applicate su Right, consentendo catene di calcoli senza verificare ogni passaggio.
flatMap è il meccanismo principale di composizione di Either. Se il valore corrente è Right, flatMap applica la funzione passata e restituisce un nuovo Either. Se è Left, flatMap salta la trasformazione e propaga l'errore. Questo comportamento è chiamato valutazione a cortocircuito.
Oltre a flatMap, Either supporta mapLeft per la trasformazione dell'errore, fold per gestire entrambe le varianti in un unico posto e getOrElse per estrarre un valore con un valore predefinito. Queste funzioni coprono tutti gli scenari: dalla semplice estrazione alla composizione complessa con chiamate asincrone in coroutine Kotlin o Combine in Swift.
fun parseInt(input: String): Either<String, Int> =
input.toIntOrNull()?.let { Right(it) }
?: Left("Impossibile convertire: $input")
fun divide(a: Int, b: Int): Either<String, Int> =
if (b == 0) Left("Divisione per zero")
else Right(a / b)
val result = parseInt("10")
.flatMap { divide(it, 2) }
Nell'ecosistema Kotlin, Either è implementato nella libreria Arrow. La libreria standard di Kotlin offre Result, ma Either offre maggiore flessibilità: tipo di errore arbitrario, composizione tramite flatMap e supporto per pattern funzionali.
Arrow è una libreria funzionale per Kotlin che aggiunge Either, Option, Validated e altri tipi. Arrow.Either è una classe sealed con due sottoclassi: ArrowCore.Left e ArrowCore.Right. La libreria fornisce anche estensioni pratiche: .getOrElse(), .fold(), .mapLeft().
Consideriamo uno scenario reale — una richiesta di rete in un'app Android con possibili errori: assenza di rete, errore del server, risposta non valida. Either consente di combinare tutte le varianti in un unico tipo di ritorno.
sealed class NetworkError {
data class NoConnection(val message: String): NetworkError()
data class ServerError(val code: Int): NetworkError()
data class ParseError(val detail: String): NetworkError()
}
suspend fun fetchUser(id: String): Either<NetworkError, User> {
return try {
val response = api.getUser(id)
if (response.isSuccessful) {
Right(response.body()!!)
} else {
Left(NetworkError.ServerError(response.code()))
}
} catch (e: IOException) {
Left(NetworkError.NoConnection(e.message ?: ""))
}
}
In Swift, a partire dalla versione 5.0, è disponibile il tipo integrato Result, concettualmente simile a Either ma con limitazioni: l'errore deve essere conforme al protocollo Error e il valore di successo deve essere di un singolo tipo. Either in Swift viene implementato tramite un enum con due parametri generici.
Result<Success, Failure> è il tipo integrato di Swift, dove Failure: Error. Either non impone vincoli sul tipo di errore, consentendo di memorizzare String, strutture personalizzate o anche più tipi di errore tramite enum annidati.
enum Either<E, A> {
case left(E)
case right(A)
func map<B>(_ transform: (A) -> B) -> Either<E, B> {
switch self {
case .left(let e): return .left(e)
case .right(let a): return .right(transform(a))
}
}
}
let result: Either<String, Int> = .right(42)
let mapped = result.map { $0 * 2 }
Either è ottimale per scenari che richiedono una gestione degli errori esplicita e type-safe senza eccezioni. Consideriamo i principali casi d'uso nello sviluppo mobile con Kotlin e Swift.
Non utilizzare Either per operazioni semplici senza effetti collaterali — un valore di ritorno normale è più affidabile e facile da leggere. Either è anche eccessivo quando il fallimento è una situazione eccezionale e non uno scenario previsto.
Either è efficace anche quando si lavora con le coroutine in Kotlin. Una funzione che restituisce Either può essere chiamata all'interno di una coroutine con gestione degli errori tramite fold o mapLeft senza bloccare il thread. Ciò è particolarmente utile nelle app Android con architettura MVVM, dove ogni repository restituisce Either e la ViewModel trasforma il risultato in UiState.
Gli sviluppatori alle prime armi con Either commettono spesso errori simili. Esaminiamo i più comuni e come evitarli.
Domande frequenti
Optional rappresenta la presenza o l'assenza di un valore (Some/None), ma non comunica la causa dell'assenza. Either fornisce due tipi concreti — sinistro per l'errore e destro per il successo — consentendo di trasmettere il contesto del fallimento.
In Java non esiste un Either integrato, ma le librerie Vavr e functionaljava forniscono implementazioni. Nello sviluppo Android, Either di Vavr è un'alternativa popolare per uno stile funzionale con lambda.
Sealed class in Kotlin è più comoda quando ci sono più di due varianti o hanno strutture diverse. Per un risultato binario (errore/successo), Either è più compatto e fornisce combinatori funzionali pronti all'uso.
Either è immutabile e thread-safe per impostazione predefinita. In Kotlin con le coroutine, Either si combina perfettamente: flatMap funziona all'interno di un ambito di coroutine e la gestione degli errori non richiede blocchi.
No. Either è adatto per operazioni con guasti previsti (rete, validazione, logica di business). Per getter semplici e calcoli senza effetti collaterali, un tipo normale si legge più facilmente e non aggiunge complessità inutile.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche