TL; DR Como outros salientaram: a notação lambda é apenas uma maneira de definir funções sem ser forçada a dar-lhes um nome.
Versão longa
Eu gostaria de elaborar um pouco sobre esse assunto porque acho muito interessante. Disclaimer: Eu fiz o meu curso de cálculo lambda há muito tempo. Se alguém com melhor conhecimento encontrar alguma imprecisão na minha resposta, sinta-se à vontade para me ajudar a melhorá-lo.
Vamos começar com expressões, por exemplo 1 + 2
e x + 2
. Literais como 1
e 2
são chamados de constantes porque estão vinculados a valores fixos específicos.
Um identificador como x
é chamado de variável e, para avaliá-lo, é necessário vinculá-lo a algum valor primeiro. Então, basicamente você não pode avaliar x + 1
desde que você não saiba o que é x
.
A notação lambda fornece um esquema para vincular valores de entrada específicos a variáveis. Uma expressão lambda pode ser formada adicionando λx .
na frente de uma expressão existente, e. %código%. A variável λx . x + 1
é considerada livre em x
e ligada em x + 1
Como isso ajuda na avaliação de expressões? Se você alimentar um valor para a expressão lambda, assim
(λx . x + 1) 2
então você pode avaliar toda a expressão substituindo (ligando) todas as ocorrências da variável λx . x + 1
com o valor 2:
(λx . x + 1) 2
2 + 1
3
Assim, a notação lambda fornece um mecanismo geral para vincular coisas a variáveis que aparecem em um bloco de expressão / programa. Dependendo do contexto, isso cria conceitos visualmente diferentes em linguagens de programação:
- Em uma linguagem puramente funcional como o Haskell, expressões lambda representam funções no sentido matemático: um valor de entrada é injetado no corpo do lambda e um valor de saída é produzido.
- Em muitos idiomas (por exemplo, JavaScript, Python, Scheme), avaliar o corpo de uma expressão lambda pode ter efeitos colaterais. Nesse caso, pode-se usar o termo procedure para marcar a diferença de funções puras.
Além das diferenças, a notação lambda é sobre definir parâmetros formais e vinculá-los a parâmetros reais.
O próximo passo é dar um nome à função / procedimento.
Em vários idiomas, as funções são valores como qualquer outro, portanto, você pode atribuir um nome à função da seguinte forma:
(define f (lambda (x) (+ x 1))) ;; Scheme
f = \x -> x + 1 -- Haskell
val f: (Int => Int) = x => x + 1 // Scala
var f = function(x) { return x + 1 } // JavaScript
f = lambda x: x + 1 # Python
Como Eli Barzilay apontou, essa definição apenas liga o nome x
a um valor, que por acaso é uma função. Então, a esse respeito, funções, números, strings, caracteres são todos valores que podem ser ligados a nomes da mesma maneira:
(define n 42) ;; Scheme
n = 42 -- Haskell
val n: Int = 42 // Scala
var n = 42 // JavaScript
n = 42 # Python
Nessas linguagens, você também pode vincular uma função a um nome usando a notação mais familiar (mas equivalente):
(define (f x) (+ x 1)) ;; Scheme
f x = x + 1 -- Haskell
def f(x: Int): Int = x + 1 // Scala
function f(x) { return x + 1 } // JavaScript
def f(x): return x + 1 # Python
Alguns idiomas, por exemplo C, suporta apenas a última notação para definir funções (nomeadas).
Encerramentos
Algumas observações finais sobre fechamentos . Considere a expressão f
. Isto contém duas variáveis livres. Se você vincular x + y
usando a notação lambda, obterá:
\x -> x + y
Esta não é (ainda) uma função porque ainda contém uma variável livre x
. Você poderia fazer uma função disso ligando y
também:
\x -> \y -> x + y
ou
\x y -> x + y
que é exatamente igual à função y
.
Mas você pode vincular, digamos, +
de outra maneira (*):
incrementBy y = \x -> x + y
O resultado da aplicação da função incrementBy a um número é um encerramento, isto é, uma função / procedimento cujo corpo contém uma variável livre (por exemplo, y
) que foi ligada a um valor do ambiente no qual o fechamento foi definido.
Portanto, y
é a função (encerramento) que aumenta os números em 5.
NOTA (*)
Estou trapaceando um pouco aqui:
incrementBy y = \x -> x + y
é equivalente a
incrementBy = \y -> \x -> x + y
, então o mecanismo de ligação é o mesmo. Intuitivamente, penso em um fechamento como representando um pedaço de uma expressão lambda mais complexa. Quando esta representação é criada, algumas das ligações da expressão mãe já foram definidas e o encerramento as usa mais tarde quando é avaliada / invocada.