Either<TLeft, TRight>

Tango.Types.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 tipo TLeft;

  • Direita (IsRight) - Quando contém um valor do tipo TRight.

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".

Last updated