Either<TLeft, TRight>
Esta classe representa valores "ou um ou outro".
Instâncias de Either<TLeft, TRight>
encapsulam um valor que pode pertencer a um dos dois possíveis tipos: TLeft
ou TRight
. Este valor só pode ser acessado através dos métodos Match
e Match2
.
Uma utilização comum deste tipo é uma alternativa ao tipo Option<T>
para lidar com possíveis erros, mas neste caso o estado "Não contendo nada" é substituído por um valor de um tipo diferente.
Por convenção o tipo TLeft
representa o tipo problemático enquanto o TRight
representa o tipo resultante no caminho feliz.
Os valores either possuem dois estados:
- Esquerda (
IsLeft
) - Quando contém um valor do tipoTLeft
; - Direita (
IsRight
) - Quando contém um valor do tipoTRight
.
Propriedades
Nome | Tipo | Descrição |
---|---|---|
IsLeft | bool | Retorna true quando o valor contido no tipo either é do tipo definido por TLeft. |
IsRight | bool | Retorna true quando o valor contido no tipo either é do tipo definido por TRight. |
Construtores
Parâmetros | Retorno | Descrição |
---|---|---|
TLeft left | Either<TLeft, TRight> | Inicializa uma nova instância de um valor Either com o estado IsLeft encapsulando o valor informado no parâmetro. |
TRight right | Either<TLeft, TRight> | Inicializa uma nova instância de um valor Either com o estado IsRight encapsulando o valor informado no parâmetro. |
Métodos
Nome | Parâmetros | Retorno | Descrição |
---|---|---|---|
Match |
Func<TRight,TResult> methodWhenRight Func<TLeft, TResult> methodWhenLeft |
TResult | Permite uma maneira de aplicar um método à um valor either sem necessidade de checar o estado do valor. Para isso são passados dois métodos por parâmetro. Estes métodos precisam retornar o mesmo tipo e cada um deles deve esperar um parâmetro diferente, dos tipos TLeft e TRight. Eles serão chamados de acordo com o estado do valor Either (IsLeft ou IsRight). |
Match2 |
Either<TLeft2, TRight2> either2 Func<TRight, TRight2, T> methodWhenBothRight Func<TRight, TLeft2, T> methodWhenRightLeft Func<TLeft, TRight2, T> methodWhenLeftRight Func<TLeft, TLeft2, T> methodWhenBothLeft |
T | Permite uma maneira de aplicar um método à dois valores either sem necessidade de checar os estados de cada valor. Para isso são passados quatro métodos contendo o mesmo tipo de retorno por parâmetro, onde cada um deles será chamado de acordo com o estado dos dois valores either. |
Match |
Action<TRight> methodWhenRight Action<TLeft> methodWhenLeft |
Unit | Permite uma maneira de aplicar um método à um valor either sem necessidade de checar o estado do valor. Para isso são passados dois métodos por parâmetro. Estes métodos precisam não conter retorno (void) e cada um deles deve esperar um parâmetro diferente, dos tipos TLeft e TRight. Eles serão chamados de acordo com o estado do valor Either (IsLeft ou IsRight). |
Match2 |
Either<TLeft2, TRight2> either2 Action<TRight, TRight2> methodWhenBothRight Action<TRight, TLeft2> methodWhenRightLeft Action<TLeft, TRight2> methodWhenLeftRight Action<TLeft, TLeft2> methodWhenBothLeft |
T | Permite uma maneira de aplicar um método à dois valores either sem necessidade de checar os estados de cada valor. Para isso são passados quatro métodos por parâmetro, onde eles precisam não retornar nenhum valor (void) e cada um será chamado de acordo com o estado dos dois valores either. |
Sobrecargas de operadores
Operador | Parâmetros | Retorno | Descrição |
---|---|---|---|
Cast implícito |
TLeft left |
Either<TLeft, TRight> | Permite a criação de um valor Either através de um cast implícito de qualquer valor TLeft para seu respectivo valor Either. |
Cast implícito |
TRight right |
Either<TLeft, TRight> | Permite a criação de um valor Either através de um cast implícito de qualquer valor TRight para seu respectivo valor Either. |
Cast implícito |
Either<TLeft, TRight> |
Option<TLeft> | Permite a criação de um valor Option<TLeft> através de um cast implícito de um valor Either.
Caso o valor Either esteja no estado IsRight será criado um valor opcional no estado IsNone. |
Cast implícito |
Either<TLeft, TRight> |
Option<TRight> | Permite a criação de um valor Option<TRight> através de um cast implícito de um valor Either.
Caso o valor Either esteja no estado IsLeft será criado um valor opcional no estado IsNone. |
Cast implícito |
Continuation<TLeft, TRight> |
Either<TLeft, TRight> | Permite a criação de um valor Either<TLeft, TRight> através de um cast implícito de um valor Continuation.
Caso o valor Continuation esteja no estado IsFail será criado um valor Either no estado IsLeft, caso contrário, no estado IsRight. |
Como Usar
Você pode criar um valor either de várias formas diferentes, inclusive para atribuir mais informações à um valor Option<T>
.
Criando valores Either
Você pode utilizar o construtor para criar valores contendo o estado IsLeft
ou IsRight
de acordo com o parâmetro, conforme código:
Utilizando o construtor
Either<bool, int> valueWithRight = new Either<bool, int>(10); //-> IsRight
Either<bool, int> valueWithLeft = new Either<bool, int>(false); //-> IsLeft
Devido às sobrecargas de cast implícito você não precisa se preocupar com nenhum tipo de sintaxe e nem utilizar o construtor, basta criar o valor de um dos dois tipos TLeft
ou TRight
declarado e a linguagem fará todo o trabalho.
Utilizando cast implícito
Either<bool, int> valueWithRight = 10; //-> IsRight
Either<bool, int> valueWithLeft = false; //-> IsLeft
Através deste cast implícito, você poderá gerar novos métodos para retornar o tipo Either em sua aplicação sem alterar nada no corpo especial da função, apenas indicando que a função retorna um valor Either e informando duas instruções de return com tipos diferentes.
Veja este exemplo:
private Either<string, int> GetSquareIfEven(int value)
{
if (value % 2 == 0)
return value * value;
else
return "Odd";
}
Pode parecer estranho ter duas instruções de retorno com valores de tipos diferentes, mas na verdade ambos retornam um valor do tipo Either
, mas estamos deixando esta responsabilidade com o C#.
Obtendo informação de um valor Either
Assim como nos valores opcionais é necessário utilizar os métodos Match
ou Match2
para obter a informação de um valor Either, no entanto, existe a possibilidade de extrair a informação de um valor Either realizando um cast implícito para um valor opcional.
Neste caso o valor opcional terá que identificar um dos dois tipos TLeft
ou TRight
. Caso o tipo identificado pelo Option
seja o tipo referente ao estado atual do valor Either
será gerado um valor opcional no estado IsSome
, caso contrário será gerado no estado IsNone
.
Com isso, é necessário tratar adequadamente todas as possibilidades para obter um valor de um Either
.
Utilizando o cast implícito para Option
Either<bool, int> eitherValue = 10;
Option<int> optionValue = eitherValue;
//optionValue.IsSome = true
Either<bool, int> eitherValue = 10;
Option<bool> optionValue = eitherValue;
//optionValue.IsSome = false
//optionValue.IsNone = true
Atenção
Mesmo realizando o cast implícito para o tipo correto, podem haver casos onde o valor opcional esteja no estado IsNone.
Esta situação ocorre quando o valor armazenado no tipo Either é igual ao valor
default
ou igual ànull
.
Either<bool, int> eitherValue = 0;
Option<int> optionValue = eitherValue;
//optionValue.IsSome = false
//optionValue.IsNone = true
Os métodos Match
esperam dois métodos por parâmetro, estes métodos podem realizar transformações no valor Either, ou apenas retorná-los, conforme exemplos:
Utilizando Match com parâmetros nomeados
Either<bool, int> eitherValue = 10;
int value = eitherValue.Match(
methodWhenRight: number => number,
methodWhenLeft: boolean => 0);
//value = 10
O primeiro método será executado apenas se o valor either estiver no estado IsRight
, logo, este método recebe um valor do tipo à direita por parâmetro, int
, no exemplo.
O segundo método recebe um valor do tipo à esquerda, bool
, mas note que ambos precisam retornar valores do mesmo tipo. Portanto, foi utilizado o valor zero (0) para seguir o fluxo da aplicação caso o Either esteja no estado IsLeft
.
Não há necessidade de nomear os métodos, basta utilizá-los na ordem correta.
Utilizando Match
Either<bool, int> eitherValue = 10;
int value = eitherValue.Match(
number => number,
boolean => 0);
//value = 10
Além disso, você também pode aplicar algum tipo de transformação no valor no momento de obtê-lo, como por exemplo, elevá-lo ao quadrado.
Either<bool, int> eitherValue = 10;
int value = eitherValue.Match(
number => number * number,
boolean => 0);
//value = 100
Você também pode retornar o valor que precisar para o caso do estado ser IsLeft
, nos exemplos anteriores foi utilizado o valor zero, mas não há nenhuma obrigatoriedade nisso.
O método Match também não precisa retornar nenhum dos dois tipos do valor Either.
Utilizando Match para retornar um novo valor
Either<bool, int> eitherValue = 10;
string value = eitherValue.Match(
number => number.ToString(),
boolean => boolean.ToString());
//value = "10"
Either<bool, int> eitherValue = false;
string value = eitherValue.Match(
number => number.ToString(),
boolean => boolean.ToString());
//value = "false"
Nos casos onde é necessário realizar uma comparação com dois valores Either de cada vez é necessário utilizar o método Match2
, este método é consideravelmente mais complexo, pois você terá de lidar com todas as possíveis combinações de resultado.
Utilizando Match2 com dois Either no estado IsRight
Either<bool, int> either = 15;
Either<bool, int> either2 = 10;
int result =
either.Match2(
either2,
(value1, value2) => value1 + value2,
(value1, value2) => value1,
(value1, value2) => value2,
(value1, value2) => 0
);
//result = 25
Neste exemplo está sendo realizada a soma de dois valores Either diferentes, no caso, ambos com o estado IsRight
, fazendo com que o primeiro método seja executado:
(value1, value2) => value1 + value2
Neste método anônimo o parâmetro value1
contém o valor armazenado pela variável either
(10) e o parâmetro value2
da variável either2
(15).
Neste exemplo a variável result
receberá o valor 25.
Para este tipo de comparação existem outros três resultados possíveis dependendo do estado de cada um dos valores Either:
Utilizando Match2 com os estados IsRight e IsLeft, respectivamente
Either<bool, int> either = 15;
Either<bool, int> either2 = true;
int result =
either.Match2(
either2,
(value1, value2) => value1 + value2,
(value1, value2) => value1, // -> Selecionado
(value1, value2) => value2,
(value1, value2) => 0
);
//result = 15
Utilizando Match2 com os estados IsLeft e IsRight, respectivamente
Either<bool, int> either = false;
Either<bool, int> either2 = 10;
int result =
either.Match2(
either2,
(value1, value2) => value1 + value2,
(value1, value2) => value1,
(value1, value2) => value2, // -> Selecionado
(value1, value2) => 0
);
//result = 10
Utilizando Match2 com dois Either no estado IsLeft
Either<bool, int> either = false;
Either<bool, int> either2 = true;
int result =
either.Match2(
either2,
(value1, value2) => value1 + value2,
(value1, value2) => value1,
(value1, value2) => value2,
(value1, value2) => 0 // -> Selecionado
);
//result = 0
Tanto para o método Match
quanto para o método Match2
há uma sobrecarga onde os métodos informados não precisam retornar nenhum tipo de valor (void
).
O conceito dos valores "Ou um ou outro" pode ser encontrado na seção Conceitos > Valores "Ou um ou outro".