Continuation<TFail, TSuccess>
Esta classe representa valores que permitem encadeamento através de pipeline.
Instâncias de Continuation<TFail, TSuccess>
encapsulam um valor que pode pertencer a um dos dois possíveis tipos: TFail
ou TSuccess
.
Neste ponto o tipo Continuation
é muito semelhante ao tipo Either
, mas as semelhanças acabam por aí. O propósito dos dois tipos é bastante distinto.
Enquanto o tipo Either
encarrega-se de armazenar um dos dois valores possíveis, o tipo Continuation
, além de também fazer isso, expõe uma série de métodos e operadores para criar um fluxo idiomático, sofisticado e poderoso para composição de seus métodos.
Devido à natureza deste tipo, não há uma demanda para compará-lo em conjunto com outro Continuation
, por conta disso, este tipo não possui nenhuma implementação para o método Match2
.
Ao invés disso, existem os métodos Then
e Catch
, que devem ser utilizados respectivamente para dar continuídade à composição quando a operação anterior foi bem sucedida e mal sucedida.
Através destes dois métodos a estrutura Continuation
gera um pipeline entre duas ou mais funções, conectando sempre a saída de uma função como parâmetro da função seguinte.
Caso você tenha uma função f definida por A -> B
e uma função g definida por B -> C
, será possível criar uma nova função fg A -> C
conectando o retorno de f como parâmetro de entrada para a função g.
Os valores Continuation
possuem dois estados:
Sucesso - Quando contém um valor do tipo
TSuccess
;Falha - Quando contém um valor do tipo
TFail
.
Propriedades
Nome
Tipo
Descrição
IsSuccess
bool
Retorna true quando o valor contido no tipo Continuation é do tipo definido por TSuccess. Representa o estado "Sucesso".
IsFail
bool
Retorna true quando o valor contido no tipo Continuation é do tipo definido por TFail. Representa o estado "Falha".
Construtores
Parâmetros
Retorno
Descrição
TSuccess success
Continuation<TFail, TSuccess>
Inicializa uma nova instância de um valor Continuation com o estado IsSuccess encapsulando o valor informado no parâmetro.
TFail fail
Continuation<TFail, TSuccess>
Inicializa uma nova instância de um valor Continuation com o estado IsFail encapsulando o valor informado no parâmetro.
Métodos
Nome
Parâmetros
Retorno
Descrição
Return
TSuccess success
Continuation<TFail, TSuccess>
Inicializa uma nova instância de um valor Continuation com o estado IsSuccess encapsulando o valor informado no parâmetro.
Return
TFail fail
Continuation<TFail, TSuccess>
Inicializa uma nova instância de um valor Continuation com o estado IsFail encapsulando o valor informado no parâmetro.
Match
Func<TSuccess,TResult> methodWhenSuccess
Func<TFail, TResult> methodWhenFail
TSuccess
Permite uma maneira de aplicar um método à um valor Continuation 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 TFail e TSuccess. Eles serão chamados de acordo com o estado do valor Continuation (IsFail ou IsSuccess).
Match
Action<TSuccess> methodWhenSuccess
Action<TFail> methodWhenFail
Unit
Permite uma maneira de aplicar um método à um valor Continuation 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 TFail e TSuccess. Eles serão chamados de acordo com o estado do valor Either (IsFail ou IsSuccess).
Then
Func<TSuccess, Continuation<TFail, TSuccess>> thenMethod
Continuation<TFail, TSuccess>
Permite uma maneira sofisticada e poderosa de compor um método através de diferentes funções. Quando o valor armazenado em Continuation estiver no estado IsSuccess o método informado no parâmetro é executado utilizando o valor armazenado no Continuation como parâmetro, caso contrário ocorre apenas um bypass até um método Catch ser encontrado.
Then
Func<TSuccess, Continuation<TFail, TNewSuccess>> thenMethod
Continuation<TFail, TNewSuccess>
Permite uma maneira sofisticada e poderosa de compor um método através de diferentes funções. Quando o valor armazenado em Continuation estiver no estado IsSuccess o método informado no parâmetro é executado utilizando o valor armazenado no Continuation como parâmetro, caso contrário ocorre apenas um bypass até um método Catch ser encontrado.
Then
Func<TParameter, TSuccess, Continuation<TFail, TNewSuccess>> thenMethod
TParameter parameter
Continuation<TFail, TNewSuccess>
Permite uma maneira sofisticada e poderosa de compor um método que contém através de diferentes funções. Quando o valor armazenado em Continuation estiver no estado IsSuccess o método informado no parâmetro é executado utilizando o valor armazenado no Continuation e o valor informado em parameter, como os dois parâmetros. Caso contrário ocorre apenas um bypass até um método Catch ser encontrado.
Catch
Func<TFail, Continuation<TFail, TSuccess>> catchMethod
Continuation<TFail, TSuccess>
Permite uma maneira sofisticada e poderosa de compor um método que contém através de diferentes funções. Quando o valor armazenado em Continuation estiver no estado IsFail o método informado no parâmetro é executado utilizando o valor armazenado no Continuation. Caso contrário ocorre apenas um bypass até um método Then ser encontrado.
Catch
Func<TFail, Continuation<TNewFail, TSuccess>> catchMethod
Continuation<TNewFail, TSuccess>
Permite uma maneira sofisticada e poderosa de compor um método que contém através de diferentes funções. Quando o valor armazenado em Continuation estiver no estado IsFail o método informado no parâmetro é executado utilizando o valor armazenado no Continuation. Caso contrário ocorre apenas um bypass até um método Then ser encontrado.
Finally
Action finallyMethod
Continuation<TFail, TSuccess>
Permite uma maneira de criar um código que deve ser executado no Continuation<TFail, TSuccess> independente de seu estado, com isso, evitando duplicar código nos métodos Then e Catch.
Finally
Action< Either<TFail,TSuccess> > finallyMethod
Continuation<TFail, TSuccess>
Permite uma maneira de criar um código que deve ser executado no Continuation<TFail, TSuccess> independente de seu estado, com isso, evitando duplicar código nos métodos Then e Catch.
Merge
Func<TSuccess, Continuation<TNewFail,TNewSuccess>> mergeMethod
Continuation<( Option<TFail>, Option<TNewFail>), (TSuccess, TNewSuccess)>
Permite uma maneira de unir dois diferentes Continuations em um único, onde os valores são agrupados em tuplas: Continuation<(TFail, TNewFail), (TSuccess, TNewSuccess)>.
Sobrecargas de operadores
Operador
Parâmetros
Retorno
Descrição
Cast implícito
TSuccess success
Continuation<TFail, TSuccess>
Inicializa uma nova instância de um valor Continuation com o estado IsSuccess encapsulando o valor informado no parâmetro.
Cast implícito
TFail fail
Continuation<TFail, TSuccess>
Inicializa uma nova instância de um valor Continuation com o estado IsFail encapsulando o valor informado no parâmetro.
Cast implícito
Continuation<TFail, TSuccess>
Option<TFail>
Permite a criação de um valor Option<TFail> através de um cast implícito de um valor Continuation.
Caso o valor Continuation esteja no estado IsFail será criado um valor opcional no estado IsSome.
Cast implícito
Continuation<TFail, TSuccess>
Option<TSuccess>
Permite a criação de um valor Option<TSuccess> através de um cast implícito de um valor Continuation.
Caso o valor Continuation esteja no estado IsSuccess será criado um valor opcional no estado IsSome.
Cast implícito
Either<TFail, TSuccess>
Continuation<TFail, TSuccess>
Permite a criação de um valor Continuation<TRight> através de um cast implícito de um valor Either.
Caso o valor Either esteja no estado IsLeft será criado um valor Continuation no estado IsFail, caso contrário, no estado IsSuccess.
Operador maior (>)
Continuation<TFail, TSuccess>
Func<TSuccess, Continuation<TFail, TSuccess>> thenMethod
Continuation<TFail, TSuccess>
Executa o método Then para realizar o pipeline através do operador.
Operador maior ou igual (>=)
Continuation<TFail, TSuccess>
Func<TSuccess, Continuation<TFail, TSuccess>> catchMethod
Continuation<TFail, TSuccess>
Executa o método Catch para realizar o pipeline através do operador.
Atenção
Por conta de obrigações da linguagem este tipo também implementa os operadores menor (
<
) e menor ou igual (<=
). Mas caso qualquer um dos dois seja utilizado, a biblioteca irá lançar a exceçãoNotSupportedException
.
Como Usar
Você pode criar um valor do tipo Continuation
de várias formas diferentes, geralmente a criação de um Continuation
indica que uma série de funções serão executadas a seguir.
Os modos de criação de um Continuation
são bastante semelhantes à criação de um valor Either
:
Criando valores Continuation
Você pode utilizar o construtor para criar valores contendo o estado IsFail
ou IsSuccess
de acordo com o parâmetro, conforme código:
Utilizando o construtor
Você também pode utilizar o método estático Return
.
Utilizando o método estático Return
E como os tipos anteriores descritos nesta seção, também há as sobrecargas de cast implícito onde não é necessário se preocupar com nenhum tipo de sintaxe, basta criar o valor de um dos dois tipos TFail
ou TSuccess
declarado e a linguagem fará todo o trabalho.
Utilizando cast implícito
Através deste cast implícito, você poderá gerar novos métodos para retornar o tipo Continuation
em sua aplicação sem alterar nada no corpo especial da função, apenas indicando que a função retorna um valor deste tipo. Assim como no Either
é possível informar duas instruções de return com tipos diferentes.
Veja este exemplo:
Obtendo informação de um valor Continuation
Assim como nos valores Either
e Option
, a informação armazenada em um Continuation
está encapsulada. Para obter a informação de um Continuation
é necessário realizar um cast implícito para um valor opcional para TFail
ou TSuccess
ou utilizarmos o método Match
.
Caso o tipo identificado pelo Option
seja o tipo referente ao estado atual do valor Continuation
será gerado um valor opcional no estado IsSome
, caso contrário será gerado no estado IsNone
.
Utilizando o cast implícito para Option
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 Continuation é igual ao valor
default
ou igual ànull
.
O método Match
disponível nesta estrutura funciona da mesma maneira que os métodos Match
disponíveis em Option
e Either
.
Os métodos Match
esperam dois métodos por parâmetro, estes métodos podem realizar transformações no valor Continuation
, ou apenas retorná-los, conforme exemplos a seguir.
Utilizando Match com parâmetros nomeados
O primeiro método será executado apenas se o valor Continuation
estiver no estado IsSuccess
, logo, este método recebe um valor do tipo que representa sucesso por parâmetro, int
, no exemplo.
O segundo método recebe um valor do tipo que representa uma falha, 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 Continuation
esteja no estado IsFail
.
Não há necessidade de nomear os métodos, basta utilizá-los na ordem correta.
Utilizando Match
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.
Você também pode retornar o valor que precisar para o caso do estado ser IsFail
, 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 Continuation
.
Utilizando Match para retornar um novo valor
Não há a possibilidade de comparar dois valores Continuation
ao mesmo tempo, este tipo não implementa o método Match2
por não fazer parte de seu contexto.
Criando Pipelines de Execução
O real valor do tipo Continuation
está em sua capacidade de gerar métodos sofisticados e limpos através de operações em pipeline com os métodos Then
e Catch
.
Neste primeiro exemplo iremos apenas alterar um valor inteiro somando-o com cinco e depois com dez.
Utilizando o Then para alterar o valor
O objeto continuation
inicialmente continha o valor 5, após executarmos o primeiro método Then
o processamento é feito e o valor armazenado (5) é passado como parâmetro para a função anônima: value => value + 5
.
Por fim, o resultado desta operação (10) é passado como parâmetro para a segunda função anônima: value => value + 10
, produzindo o resultado final (20).
Quando o Then não é executado
Caso o continuation
esteja no estado IsFail
nenhum dos métodos Then
é executado. Ocorre apenas um bypass do valor.
Neste caso, podemos obter o resultado utilizando um Option<bool
para mapear a falha.
Utilizando cast implícito para obter a falha
Um ponto importante a ser ressaltado é a possibilidade de uma das funções utilizadas em pipeline retornar o tipo referente à falha. Caso isso ocorra todos os métodos Then
após ela serão ignorados.
Quando a falha ocorre no meio do caminho
No exemplo anterior, a falha ocorre somente no segundo Then
, isso significa que o Then
que o antecede executa normalmente, mas o Then
que vem depois não será executado.
Para criar métodos que lidem com os erros, é necessário utilizar o método Catch
.
Utilizando o Catch para tratar erros
Quando um Continuation
está no valor IsFail
os métodos Then
não serão executados, eles apenas passarão o valor para o próximo método, até encontrar um método Catch
.
Assim como o método Then
também é possível executar diversos métodos Catch
de forma encadeada.
Catches encadeados
Evite repetição de código com o Finally
Em alguns casos é necessário executar uma determinada ação independente do resultado contido no Continuation
. Esta é a função principal do método Finally
, funciona de maneira similar ao Then
e ao Catch
, mas neste caso, a função sempre é executada.
Outra diferença clara entre o Finally
e os dois anteriores é a função recebida por parâmetro. Neste caso, você pode utilizar uma função que não recebe nenhum parâmetro.
Ela será executada e depois disso, o Continuation
será retornado novamente para manter o fluxo contínuo.
Apesar de não ser obrigatório, o Finally
geralmente é a última chamada do fluxo de um Continuation
, além disso, é comum este método causar algum efeito colateral, portanto, seja cuidadoso.
Outra particularidade deste método é o fato de que ele não é capaz de modificar o valor armazenado no Continuation
. Na verdade, é possível acessar o valor armazenado usando a sobrecarga que recebe um Action<Either<TFail, TSuccess>>
como parâmetro.
Assim como o Then
e o Catch
, o método Finally
também permite encadeamentos.
Unindo dois Continuations diferentes em um novo
Esta não é uma operação comum, mas existem casos onde você precisa unir duas pipelines de valores Continuation
diferentes para completar alguma determinada tarefa. Para facilitar este processo, você pode utilizar o método Merge
.
A função recebida por parâmetro por este método recebe o próprio Continuation<TFail, TSuccess>
por parâmetro e deve retornar um novo Continuation<TNewFail, TNewSuccess>
. Depois disso, o retorno do Merge
é criado agrupando os dois em um novo: Continuation<(Option<TFail>, Option<TNewFail>), (TSuccess, TNewSuccess)>
O continuation gerado pelo Merge
estará no estado de Success
somente quando os dois anteriores também estiverem, nos outros casos, o novo Continuation
estará no estado Fail
.
Você pode continuar seu fluxo normalmente, mas depois de chamar este método será necessário acessar as propriedades Item
, afinal os valores foram agrupados em tuplas.
Note que os tipos de falha do novo Continuation
são tratados como valores opcionais, porque não há garantia de qual dos erros ocorreu.
Encadeando métodos com o operador de pipeline
A linguagem funcional da plataforma .NET, o F#, possui um operador para realizar pipelines, este operador é definido por: |>
para pipe-foward e <|
para reverse pipe ou pipe-backward.
Com eles podemos realizar operações na linguagem F# como estas:
Infelizmente não há como criar novos operadores no C# até a versão atual. No entanto, é possível sobrescrever os operadores existentes.
Pensando nisso, realizei a sobrescrita dos operadores >
e >=
para funcionarem de forma similar ao pipe-foward do F#.
Ao invés de realizarem as comparações de maior e maior ou igual, os operadores atuam recebendo como parâmetro um delegate Func
idêntico aos utilizados nos métodos Then
e Catch
.
Por tanto é possível realizar as operações em pipeline substituindo as chamadas ao método Then
pelo operador >
e as chamadas ao método Catch
pelo operador >=
.
Utilizando o operador >
para Then
Utilizando os operadores >
e >=
para Then e Catch
Atenção
Há uma limitação na utilização dos operadores. 1. Não há uma versão possível do pipe-backward 2. É possível utilizar o operador somente nos métodos que retornam um valor do mesmo tipo que seu parâmetro, diferente dos métodos
Then
eCatch
não há como sobrecarregar os parâmetros com generics.
Quando o tipo do valor é alterado durante os métodos
Os dois métodos para realizar pipelines possuem sobrecargas para alterar o tipo do valor armazenado no Continuation
, desta forma é possível transformar o valor ao longo da execução.
Alterando o tipo do Continuation durante as execuções
Note que para alterar o tipo não é necessário utilizar a notação de generics Then<tipoDestino>
, mas você pode explicitá-lo se quiser. O mesmo ocorre com o método Catch
.
Alterando diversas vezes os tipos
Quando é necessário unir os resultados em um único tipo
Quando é necessário unificar as duas possibilidades de valores em um Continuation
é sugerido utilizar o método Match
após todas as execuções.
Assim como nos outros tipos você pode utilizar o Match
para retornar qualquer tipo, desde que ambos os métodos o retornem.
Last updated