Design: Mônadas
Tags: csharp, functional, softwaredevelopment, braziliandevs
Published: 2024-04-15 12:28:39

Olá! Este é mais um post da seção **Design** e nele trataremos de um recurso excelente oriundo das linguagens funcionais e que tem sido cada vez mais incentivado nas linguagens orientadas a objetos: mônadas!

Não pretendo explicar mônadas a partir da [teoria das categorias](https://pt.wikipedia.org/wiki/M%C3%B3nade_(teoria_das_categorias)#:~:text=Na%20teoria%20das%20categorias%2C%20uma,an%C3%A1logas%20%C3%A0s%20de%20um%20monoide.), apenas explorar sua natureza conforme vemos na programação funcional.

## O que é uma mônada?

Uma mônada é um invólucro (*wrapper*) para um tipo genérico que permite transformações neste tipo sempre retornando o invólucro.

Confuso? Explico.

No .NET temos os *nullable types*. Quando queremos admitir o valor `null` em um *value type* lançamos mão de algo como o que vemos abaixo:

```c#
int? x = null;
```

Simples. Certo?

Agora, quando vamos a fundo no que significa o código acima, descobrimos um tipo chamado `Nullable<T>`, que representa o seguinte:

```c#
Nullable<int> x = null;
Console.WriteLine(x.HasValue);
//false
...
int? x = 10;
int? y = 20;
Console.WriteLine(x + y); //Equivale a x.Value + y.Value
//30
```

Ou seja, temos um tipo que carrega, no nosso caso, um valor do tipo  `int` e permite que sejam feitas operações com ele como se com um `int` fosse, admitindo `null` no processo, sempre retornando um `Nullable`.

Isso significa que `Nullable<T>` é uma mônada? Na verdade não. Vamos explorar um tanto mais este ponto.
### Mais que um invólucro

Apesar de ser uma característica fundamental para uma mônada, ser um invólucro não a descreve totalmente. Além de permitir que um dado tipo seja envolvido uma mônada precisa ser capaz de realizar transformações e, para isso, é necessário uma função (método) de transformação (`Bind`).

Vamos usar como exemplo o tipo mais conhecido e utilizado, o `Option` (conhecido como `Maybe` em outras linguagens). Veja o código abaixo:

```c#
var value = obj.Method() //retorna 50
			   .ToOption<int>()
			   .Bind((v) => r > 0 ? "Maior que zero!" : "Menor ou igual a zero!")
			   .Contains("Maior que zero!");
Console.WriteLine(value);
//true
```

Repare que aqui temos uma função `Bind` que opera sobre `Option<int>`, o transforma em um `Option<string>` e, por fim, ao ser encadeado com `Contains` (que verifica se o valor envolvido é o passado como parâmetro) nos retorna um `bool`.

Embora a função `Bind` seja um requisito para definir uma mônada é possível ter diversas outras funções que manipulem o valor envolvido por ela para simplificar seu uso.

### *Flattening* (achatamento)

[Mark Seeman](https://blog.ploeh.dk/2022/03/28/monads/) , em sua série sobre mônadas (e functors, mas estes estão fora do escopo deste post) argumenta que uma mônada é um tipo "achatável".

Isso significa que toda mônada deveria ser capaz de retornar uma mônada envolvida por ela por meio de uma função de achatamento (`Flatten`).

Vamos a mais um exemplo, simples para ilustração, também com `Option<T>`:

```c#
var option = Option.Some<Option<int>>(obj.Method()); //retorna Option<int> (10)
Console.Write(option.Flatten());
//10
```

O código acima representa um `Option<T>` que pode receber outro a partir da execução de um método e, caso isso ocorra, pode obter a `Option<T>` envolvida via `Flatten`, permitindo acesso ao valor por este envolvido, neste caso 10.
## Bons exemplos

Agora que entendemos um tanto sobre a natureza das mônadas, vamos conhecer as que já estão disponíveis no F#. *Spoiler*: as portei para o C# por meio do projeto [Moonad](moonad.net), e será dele o código que veremos a seguir.

### Choice

Este tipo, também conhecido como `Either` em outras linguagens, nos permite escolher um entre dois (ou mais) tipos para retornar de um método. Talvez soe estranho em um primeiro momento mas considere o seguinte cenário: um usuário pode escolher fazer seu login utilizando email ou CPF. Qualquer que seja sua escolha, uma vez verificada sua existência na base de usuário, ele será direcionado para o pedido de senha.

Vejamos como o `Choice<T1, T2, T3>` pode nos ajudar nisso.

```c#
public Option<User> GetUser(string input)
{
	Choice<Email, Cpf, InvalidKey> key = ExtractKey(input);
	return key switch
	{
		Email => users.GetByEmail(key),
		Cpf => users.GetByCpf(key),
		InvalidKey => Option.None<User>()
	}
}
```

Veja que o método `ExtractKey` vai tentar converter a entrada para um dos dois tipos válidos de identificador e, caso não consiga, retorna um tipo que expressa uma chave inválida. E, a partir da chave, tenta obter um usuário correspondente. Você pode ter notado que, além de usar `Choice` para retornar a chave, também é usado `Option` para retornar o usuário.

> **Nota:** ao contrários das demais mônadas, que possuem uma função de achatamento, `Choice` não o tem. Isso significa que `Choice` não é uma mônada? De forma alguma. A função `Flatten` é substituída pela conversão implícita do valor envolvido por `Choice`. Ou seja, caso o valor envolvido por `Choice<int, string>` seja um `Choice<int>`, haverá uma conversão implícita de `Choice<int>` para `int`, tendo o exato mesmo efeito esperado da função `Flatten`.

### Result e Result&lt;TResult, TError&gt;

Este tipo representa o resultado de uma operação. É usualmente confundido com `Choice<T1, T2>` pelo fato de ambos poderem retornar um de dois tipos, mas `Result` possui indicadores próprios que indicam se uma operação ocorreu ou não com sucesso (`IsOk`), algo que, com `Choice` teria de ser verificado via *[pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching)*.

`Result` é indicado para métodos que envolvem processamentos, normalmente operações de cálculo, transformação de valores ou escrita em mecanismo de persistência/mensageria. Caso a operação ocorra sem intercorrências e haja algum tipo específico a se retornar (como, por exemplo, o número de registros afetados por uma instrução SQL), usa-se `Result<TResult, TError>`, do contrário apenas `Result` é necessário.

Um detalhe interessante é que `Result` pode ser usado para evitar que exceções sejam lançadas em métodos que normalmente seriam do tipo `void`. Ou seja, caso uma exceção seja lançada em um dado método como, por exemplo, uma tentativa de escrita em banco de dados, esta pode ser substituída por uma instância de `Result` que represente apenas que a operação não pôde ser concluída, deixando os detalhes da manipulação da exceção apenas a cargo do objeto de acesso a este banco.
Já `Result<TResult,TError>` por sua vez pode ser usado para retornar tipos de erro que sejam semanticamente significativos para o domínio da aplicação, evitando assim as chamadas *[business exceptions](https://dev.to/wsantosdev/desempenho-toda-regra-tem-excecao-313a)*.

Vamos aos exemplos para deixar mais claro.

Exemplo 1 - tentativa de criação de um usuário na base de dados:

```c#
public Result PersistUser(User)
{
	try
	{
		//SQL...
		return true;
	}
	catch(Exception exc)
	{
		//Logs...
		return false;
	}
}
```

Exemplo 2 - tentativa de criação de um usuário:

```c#
public interface IUserError {}
public struct AlreadyExistingEmailError : IUserError {}
public struct AleradyExistingCpfError : IUserError {}
...
public Result<User, IUserError> Create(Email email, Cpf cpf, Password password)
{
	//Email já existe
	return new AlreadyExistingEmailError();
	//Cpf já existe
	return new AlreadyExistingCpfError();
}
...
public IActionResult Create(CreateUserRequest request)
{
	var result = UserService.Create(email, cpf, password);
	if(result)
		return Created(string.Empty, result);
	
	var conflict = result switch
	{
		AlreadyExistingEmailError => "E-mail já cadastrado.",
		AlreadyExistingCpfError => "Cpf já cadastrado."
	}
	return Conflict(conflict);
}

```

> **Nota:** veja que interessante: no segundo exemplo utilizamos structs cujos tipos bastam para compreender a natureza do erro, deixando a mensagem amigável ao usuário na borda do sistema, onde a comunicação de fato acontece.
> Essa é uma ótima prática para evitar que mensagens ao usuário sejam escritas em objetos que pertençam ao domínio da aplicação, onde preocupações com a apresentação ao usuário não deveriam existir, o que acabaria por criar um acoplamento desnecessário.

### Option 

`Option<T>` é um tipo bem peculiar de mônada por representar a existência ou ausência de um valor. Quando há um valor envolvido por `Option<T>` ele se manifesta como um `Some` e, quando não há, como um `None`. 
Isso permite a `Option<T>` previnir o velho problema do *NullReferenceException*, e isso é algo que considero essencial para evitar que nosso próprio código gere essas exceções. Isso é tão importante que a Microsoft nos brindou com o *[Nullable Reference Types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-reference-types)* para facilitar a identificação, restrição e permissão de potenciais valores nulos em nossas aplicações.

Vamos ver na prática como isso funciona. Imagine que você esteja lidando com uma biblioteca de terceiros onde um dado objeto pode retornar nulo em um de seus métodos, um `int?` por exemplo. Para eliminar a preocupação com este valor nulo, e poder retornar sem preocupações, o seguinte código pode ser implementado:

```c#
public Option<T> MyMethod(Whatever obj)
{
	Option<int> value = obj.Method().ToOption(); //retorna null
	//value é Option<int>.None
	return value;
}
```

```c#
public Option<T> MyMethod(Whatever obj)
{
	Option<int> value = obj.Method().ToOption(); //retorna 10
	//value é Option<int>.Some (10)
	return value;
}
```

É exatamente essa característica que permite, como visto no começo do post, realizarmos diversas operações. Muitas delas, constantes do próprio tipo `Option<T>`, que verificam por si a presença/ausência de valor e retornam de acordo, dispensando assim a necessidade de verificar a todo momento se um dado valor é ou não nulo.

>**Nota:** existe uma versão de `Option<T>` específica para *value types*, chamada `ValueOption<T>`. Ela é, também, um *value type* e deve ser usada em cenários onde performance seja crítica para a aplicação.

## Considerações Finais

Este foi um post introdutório sobre mônadas, com um resumo do seu conceito e aplicação. Caso queira se aprofundar no assunto, recomendo este [ótimo livro](https://www.amazon.com.br/Functional-Programming-Second-Enrico-Buonanno/dp/1617299820) do Enrico Buonanno, que serviu de referência para este post.

### Alguém disse "código"?

Aqui temos o repositório do Moonad no [GitHub](https://github.com/moonad-dotnet/moonad). Fique à vontade para estudá-lo e, para testá-lo, pode baixar o pacote [Nuget](https://www.nuget.org/packages/Moonad). Lembrando que a documentação está disponível em [moonad.net](https://moonad.net/).

Gostou? Me deixe saber pelos indicadores. Fique à vontade para deixar um comentário ou me procurar em minhas redes sociais.

Ah! E caso queira mais conteúdo sobre mônadas, outros tipos (*functors* e *aplications*) e programação funcional em geral,me deixe saber que posto assim que possível.

Até a próxima!