En lenguajes ansiosos como Scheme y Python, puede usar una expresión lambda sin parámetros para retrasar la evaluación, por ejemplo, en Scheme (Chicken Scheme):
#;1> (define (make-thunk x) (lambda () (+ x 1)))
#;2> (define t (make-thunk 1))
#;3> (t)
2
En la línea 2, t
está vinculada a la expresión no evaluada (lambda () (+ 1 1))
, que luego se evalúa 2
en la línea 3.
Del mismo modo, en Python:
>>> def make_thunk(x): return lambda: x + 1
...
>>> t = make_thunk(1)
>>> t()
2
Usando esta técnica, se puede implementar una evaluación perezosa en un lenguaje entusiasta.
Entonces, esperaba que Haskell no tuviera expresiones lambda sin parámetros porque el lenguaje ya es vago y no hay necesidad de construir expresiones retrasadas. Para mi sorpresa, descubrí que en Haskell es posible escribir la expresión lambda
\() -> "s"
que solo se puede aplicar al ()
valor de la siguiente manera:
(\() -> "s") ()
dando el resultado
"s"
La aplicación de esta función a cualquier argumento que no sea ()
arroja una excepción (al menos por lo que pude ver durante mis pruebas). Esto parece diferente de la evaluación retrasada en Scheme y Python, porque la expresión todavía necesita un argumento para ser evaluado. Entonces, ¿qué significa una expresión lambda sin variables (como \() -> "s"
) en Haskell y para qué puede ser útil?
Además, me gustaría saber si existen expresiones lambda sin parámetros similares en (alguna variedad de) cálculo lambda.
()
arrojar una excepción ..." ¿Hace que el programa arroje una excepción o el compilador se queja de que el código no escribe check?Respuestas:
Bueno, las otras respuestas cubren lo que
\() -> "something"
significa en Haskell: una función unaria que toma()
como argumento.¿Qué es una función sin argumentos? - Un valor. En realidad, en ocasiones puede ser útil pensar en las variables como funciones nulares que evalúan su valor. La
let
sintaxis de una función sin argumentos (que en realidad no existe) termina dándole un enlace variable:let x = 42 in ...
¿El cálculo lambda tiene funciones nulares? - No. Cada función toma exactamente un argumento. Sin embargo, este argumento puede ser una lista o la función puede devolver otra función que tome el siguiente argumento. Haskell prefiere la última solución, por lo que en
a b c
realidad son dos llamadas a funciones((a b) c)
. Para simular funciones nulares, debe pasar un valor de marcador de posición no utilizado.fuente
(f)
es conceptualmente una lista de un solo elemento, con un valor de encabezado de'f
y una cola nula, por lo que utilizandocons
podemos escribirlo como(cons 'f '())
. Esta cola es la lista que (conceptualmente) se usa como argumento, y no importa que nil represente la lista vacía. La diferencia entre Lisp y Haskell es que este último tiene un currículum implícito, por lo que la expresión(f)
significa cosas diferentes()
(como es el caso en ML) o si no lo hace a sabiendas (como es el caso en Lisps o lenguajes similares a C) realmente no importa.Estás malinterpretando lo que
()
significa en Haskell. No es la falta de un valor, es más bien el único valor del tipo Unidad (el tipo en sí se refiere a un paréntesis vacío()
).Dado que las lambdas se pueden construir para utilizar la coincidencia de patrones, la expresión lambda
\() -> "s"
dice explícitamente "crear una función anónima, esperando una entrada que coincida con el()
patrón". No tiene mucho sentido hacerlo, pero ciertamente está permitido.También puede usar la coincidencia de patrones con lambdas de otras maneras, por ejemplo:
fuente
Unit
también se deletrea()
en Haskell.const
, que toma cualquier entrada y la transforma en el mismo valor).() -> "s"
es una función lambda que toma un argumento:()
, la tupla vacía (a veces conocida como Unidad), es un tipo completo en Haskell. Solo tiene un miembro (ignorando_|_
*), que también se escribe como()
. Mira la definición de()
:Esto significa que el formulario en el lado izquierdo de su expresión lambda es sintácticamente una coincidencia de patrón que coincide
()
, el único miembro del tipo()
. Solo hay una forma válida de llamarlo (que es proporcionar()
como argumento) porque solo hay un miembro válido del tipo()
.Tal función no es muy útil en Haskell, porque de forma predeterminada los términos se evalúan perezosamente de todos modos, como observó en su pregunta.
Para una explicación mucho más detallada, vea esta respuesta .
*
_|_
se llama "fondo" o "indefinido". Siempre verifica los tipos, pero bloquea su programa. El ejemplo clásico de cómo obtener un_|_
eslet x = x in x
.fuente