/ csharp-programming

Especificação de design do C#, cadê as tails functions?

Pros senhores que participam do desenvolvimento do Roslyn, já devem saber que essa é uma discussão que vem de tempos remotos, um certo período primitivo da linguagem onde sua sintaxe conseguia ser mais verbrosa do que a nossa realidade atual. Mas enfim, vamos entender o custo que essa feature pode vir a causar dentro do .NET compiler.

Naturalmente falando, existem sempre rotinas de execuções na nossa vida, e claro, no código, que necessitam da recursividade da mesma, por mais que muitas vezes essa não seja a razão mais lógica para isso acontecer - igual os humanos cometendo os mesmos erros á mais de 2 mil anos - mas essa approach dentro da programação de sistemas, não é muito bem vista, devido ao consumo exagerado de recursos do host.

E o problema da abordagem genérica com recursividade, especialmente na estrutura de tipos que é consumida o algoritmo, sempre vamos ter um probleminha bem chato, conhecido popularmente como Stack Overflow, que de modo grosseiro faz coisas "estourarem", isso ocorre principalmente pela referência a profundidade da execução da ordem de repetição, gerando assim behaviors completamente diferentes do que se espera.

Voltando a falar agora do conceito história da coisa, na teoria - nessa, tudo vai bem - a implementação de uma tecnologia desse aporte (feature) é viável, e de fato é implementada por algumas propriedades bem especificas da CLR, tem a capacidade de funcionar nativamente nas implementações do framework, todos os recursos desta, porém, com certas limitações performáticas.

O mais importante é que isso só acontece hoje, pela implementação da dynamic programming dentro do Roslyn, esse feito permitiu a reflexão de N' para R', ou no momento de sua atribuição e/ou declaração, assinaturas de tipo sem referência direta a estrutura da linguagem, ou seja, objetos que não contém uma assinatura real e tátil para validar este, dai temos opções muito efetivas para isso com o emprego de out e o casting implícito/explicito providos por is (validação) e as.

Porém, de nada adianta uma CLR capaz de entender os recursos necessários, sem antes conceber uma builder, que pode facilmente ser implementada consumindo Func<> e sua vasta possibilidade de consumo, nesse contexto, seria de fato aceitar como parâmetros recursivos a própria função, sem o risco de um stack overflow direto no tempo de execução.

De uma maneira mais simples, podemos entender o trabalho recursivo como o de um advogado, onde o mesmo começa sua tese de defesa em um papel, e depois disso precisa de outro para continuar seu diálogo, porém, em determinada situação, ele necessita de ir além, chamando uma função no topo de sua pilha de papeis, que retorna exatamente aquilo que ele já está fazendo, porém com um estado diferente, no meio desse caminho, a pilha aumenta, e novamente ele precisa de uma papel, só que agora não mais a alcança, pelo seu tamanho, fica desequilibrada e cai, ai então, temos o estouro da pilha.

Dentro da família .NET temos algumas linguagens que tem uma tratativa bem diferente desta, o melhor exemplo seria o próprio F#, que tendem a trazer a zero a mutabilidade do sistema, e isso ocorre quando a chamada da função no topo desta, retorna imediatamente seu resultado, ou seja o processo de tail. Se a última instrução de uma função recursiva é a chamada recursiva, então chamamos essa tail recursion.

Agora, no lado C# da coisa, temos algumas mudanças no funcionamento, o estado mutável e funções sujas, fazem que a chamado no topo ou no fim da pilha, seja um processo ridiculamente custoso ao JIT, por mais que em situações extremamente especificas, ele decida ajudar, o processo por si só não tem melhorias significativas no que se diz respeito ao tempo de execução.

Para contornar isso, normalmente o emprego de Func<> é realizado, desta forma, o papel que o advogado escreve, ao chamada a nova função refletora, é apagado, para dar espaço ao resultado da mutação da recursão da estrutura, possibilitado pelo enésimo T in e pelo último T out de Func<>.

Por tanto chegamos a conclusão que de fato o C# na sua versão atual (nesse momento 7.2) juntamente com a última build do Roslyn, não implementam nativamente funções de cauda, que sejam recursivas, porém, é possível contornar esse fator, utilizando principalmente a técnica descrita como trampolim, que literalmente se diz respeito a capacidade de uma função saltar, juntamente com seus N' parâmetros. Em uma postagem oportuna, vou falar um pouco mais sobre este.

Especificação de design do C#, cadê as tails functions?
Share this