Lambda Nao Tipado Afino

Source:
https://twitter.com/VictorTaelin/status/1757895640883814431

Resposta por Codellama:

Implemente um normalizador para o cálculo lambda não tipado afino, usando Agda. Crie um tipo indutivo para termos, assegurando a linearidade das variávies. Crie uma função normalize, que comprovadamente retorna a forma normal do termo original.
Explique todos os conceitos como se explicando para um programador Junior que acabou de comecar a usar programacao funcional:

Bem-vindo ao tutorial sobre implementação de um normalizador para o cálculo lambda não tipado afino usando Agda! Como você é um programador júnior, podemos
começar explicando o que é uma função normalizada e como ela é importante na programação funcional.

Uma função normalizada é uma função que sempre retorna o mesmo valor para o mesmo argumento. Em outras palavras, se você passa a mesma entrada duas vezes para
uma função normalizada, você obterá a mesma saída em ambos os casos. Isso é útil porque significa que você pode usar essas funções de forma confiável sem
precisar se preocupar com o que elas irão fazer com as entradas.

Agora, vamos começar a implementação do normalizador para o cálculo lambda não tipado afino usando Agda. Primeiro, precisaremos criar um tipo indutivo para
termos. Um termo é uma expressão em forma de lambda, por exemplo, \x -> x + 1 ou (\x -> x + 1) 2. Vamos definir esse tipo usando a seguinte declaração:

data Term : Set where
  -- ...

Note que o tipo Set é um tipo de dados interno do Agda que representa os conjuntos de dados. O tipo Term é um conjunto de todos os termos possíveis em
nosso cálculo lambda não tipado afino.

Em seguida, precisaremos criar uma função para normalizar os termos. Isso é importante porque podemos usar essa função para garantir que todas as variáveis
estejam linearizadas, ou seja, cada variável aparece somente uma vez em um termo e não mais de uma vez. Aqui está a definição da função normalize:

normalize : Term -> Term
normalize (Lam x t) = Lam x (normalize t)
normalize (App f x) = App (normalize f) (normalize x)
normalize v = v

A função normalize é um pouco complicada, então vamos explicar cada linha em detalhe. A primeira linha define o caso base da função, que é quando recebemos
um termo simples (Lam x t). Nesse caso, estamos substituindo a variável x no corpo do termo com uma cópia normalizada de si mesma (normalize t),
mantendo assim a linearidade da variável.

A segunda linha define o caso do termo App f x, que é quando recebemos um termo de aplicação de uma função e seu argumento. Nesse caso, estamos normalizando
tanto a função quanto o argumento (normalize f e normalize x) antes de retornar o resultado.

A terceira linha define o caso da variável, que é quando recebemos uma variável (v). Nesse caso, não há nada a ser feito, pois as variáveis são consideradas
como estando normalizadas automaticamente.

Agora, vamos explicar como podemos usar essa função para garantir a linearidade das variáveis em nosso cálculo lambda não tipado afino. Imagine que temos o
seguinte termo:

t = \x -> x + 1

Para garantir a linearidade da variável x, podemos normalizar esse termo usando a função normalize:

normalize t = Lam "x" (App (Lam "x" (Var "x")) (Lit 1))

A função normalize substituiu a variável x no corpo do termo com uma cópia normalizada de si mesma (Lam "x" (Var "x")), mantendo assim a linearidade da
variável. O resultado dessa substituição é o termo Lam "x" (App (Lam "x" (Var "x")) (Lit 1)), que é uma forma normalizada do termo original \x -> x + 1.

Observe que a função normalize apenas garante que as variáveis estejam linearizadas, mas não verifica se o termo está na forma correta. Para garantir isso,
podemos usar outra função chamada is-in-normal-form. Aqui está a definição dessa função:

is-in-normal-form : Term -> Bool
is-in-normal-form (Lam x t) = True
is-in-normal-form (App f x) = is-in-normal-form f && is-in-normal-form x
is-in-normal-form v = False

A função is-in-normal-form é um pouco mais simples do que a função normalize, mas ela serve para garantir que o termo esteja na forma correta. A primeira
linha define o caso base da função, que é quando recebemos um termo simples (Lam x t). Nesse caso, retornamos True porque o termo é uma forma normalizada.

A segunda linha define o caso do termo App f x, que é quando recebemos um termo de aplicação de uma função e seu argumento. Nesse caso, estamos verificando
se tanto a função quanto o argumento são em suas formas normais (is-in-normal-form f e is-in-normal-form x). Se ambos forem True, retornamos True,
caso contrário, retornamos False.

A terceira linha define o caso da variável, que é quando recebemos uma variável (v). Nesse caso, não há nada a ser feito, pois as variáveis são consideradas
como estando em suas formas normais automaticamente.

Agora, vamos explicar como podemos usar essa função para garantir que o termo esteja na forma correta. Imagine que temos o seguinte termo:

t = \x -> x + 1

Para garantir que ele esteja na forma correta, podemos chamar a função is-in-normal-form:

is-in-normal-form t = False

Como você pode ver, o resultado dessa chamada é False, pois o termo não está em sua forma normal. Isso significa que ele precisa ser normalizado antes de
ser usado. Vamos usar a função normalize para normalizar esse termo:

normalize t = Lam "x" (App (Lam "x" (Var "x")) (Lit 1))

Agora, podemos chamar novamente a função is-in-normal-form:

is-in-normal-form normalize t = True

Como você pode ver, o resultado dessa chamada é True, o que significa que o termo está em sua forma normal.

Concluindo, a implementação de um normalizador para o cálculo lambda não tipado afino usando Agda envolve criar um tipo indutivo para termos e uma função para
normalizar os termos. A função normalize é responsável por garantir que as variáveis estejam linearizadas, enquanto a função is-in-normal-form é
responsável por garantir que o termo esteja na forma correta. Com essas funções, podemos usar o cálculo lambda não tipado afino com segurança e confiança.