Em linguagens ansiosas como Scheme e Python, você pode usar uma expressão lambda sem parâmetros para atrasar a avaliação, por exemplo, em Scheme (Chicken Scheme):
#;1> (define (make-thunk x) (lambda () (+ x 1)))
#;2> (define t (make-thunk 1))
#;3> (t)
2
Na linha 2, t
é vinculado à expressão não avaliada (lambda () (+ 1 1))
, que é avaliada 2
na linha 3.
Da mesma forma, em Python:
>>> def make_thunk(x): return lambda: x + 1
...
>>> t = make_thunk(1)
>>> t()
2
Usando essa técnica, é possível implementar uma avaliação lenta em um idioma ansioso.
Então, eu esperava que Haskell não tivesse expressões lambda sem parâmetros, porque a linguagem já é preguiçosa e não há necessidade de criar expressões atrasadas. Para minha surpresa, descobri que em Haskell é possível escrever a expressão lambda
\() -> "s"
que só pode ser aplicado ao ()
valor da seguinte forma:
(\() -> "s") ()
dando o resultado
"s"
A aplicação desta função a qualquer argumento que não seja ()
lança uma exceção (pelo menos até onde pude ver durante meus testes). Isso parece diferente da avaliação atrasada em Scheme e Python, porque a expressão ainda precisa de um argumento para ser avaliado. Então, o que uma expressão lambda sem variáveis (como \() -> "s"
) significa em Haskell e para que ela pode ser útil?
Além disso, eu ficaria curioso para saber se existem expressões lambda sem parâmetros semelhantes em (alguma variedade de) cálculo lambda.
()
gera uma exceção ..." Faz com que o programa lance uma exceção ou faz com que o compilador reclame que o código não digita check?Respostas:
Bem, as outras respostas cobrem o que
\() -> "something"
significa em Haskell: uma função unária que toma()
como argumento.O que é uma função sem argumentos? - Um valor. Na verdade, ocasionalmente pode ser útil pensar em variáveis como funções nulas que avaliam seu valor. A
let
sintaxe-para uma função sem argumentos (que na verdade não existe) acaba fornecendo uma associação de variável:let x = 42 in ...
O cálculo lambda tem funções nulas? - Não. Toda função usa exatamente um argumento. No entanto, esse argumento pode ser uma lista ou a função pode retornar outra função que aceita o próximo argumento. Haskell prefere a última solução, de modo que
a b c
na verdade são duas chamadas de função((a b) c)
. Para simular funções nulas, você deve passar algum valor de espaço reservado não utilizado.fonte
(f)
é conceitualmente uma lista de elemento único, com um valor inicial'f
e uma cauda nula - paracons
que possamos escrever como(cons 'f '())
. Essa cauda é a lista que é (conceitualmente) usada como argumento, e não importa que nil represente a lista vazia. A diferença entre Lisp e Haskell é que o último tem curtimenta implícita, de modo que a expressão(f)
significa coisas diferentes()
(como é o caso nos MLs) ou se não o faz conscientemente (como é o caso nos idiomas Lisps ou C), não importa.Você está interpretando mal o que
()
significa em Haskell. Não é a falta de um valor, é o único valor do tipo Unit (o tipo em si é referido por um conjunto vazio de parênteses()
).Como os lambdas podem ser construídos para usar a correspondência de padrões, a expressão lambda
\() -> "s"
está dizendo explicitamente "crie uma função anônima, esperando uma entrada que corresponda ao()
padrão". Não há muito sentido em fazê-lo, mas certamente é permitido.Também é possível usar a correspondência de padrões com lambdas de outras maneiras, por exemplo:
fonte
Unit
também está escrito()
em Haskell.const
que recebe qualquer entrada e transforma tudo no mesmo valor).() -> "s"
é uma função lambda que usa um argumento:()
, a tupla vazia (às vezes conhecida como Unidade), é um tipo completo em Haskell. Ele possui apenas um membro (ignorando_|_
*), que também é escrito como()
. Veja a definição de()
:Isso significa que o formulário no lado esquerdo da sua expressão lambda é sintaticamente uma correspondência de padrão que corresponde
()
ao único membro do tipo()
. Há apenas uma maneira válida de chamá-lo (que é fornecer()
como argumento) porque há apenas um membro válido do tipo()
.Essa função não é muito útil no Haskell, porque, por padrão, os termos são preguiçosamente avaliados de qualquer maneira, como você observou na sua pergunta.
Para uma explicação muito mais detalhada, consulte esta resposta .
*
_|_
é chamado "inferior" ou "indefinido". Sempre verifica o tipo, mas trava o seu programa. O exemplo clássico de como obter um_|_
élet x = x in x
.fonte