haskell syntax lazy-evaluation

¿Qué significa el signo de exclamación en una declaración de Haskell?



syntax lazy-evaluation (3)

Creo que es una anotación de rigor.

Haskell es un lenguaje funcional puro y perezoso , pero a veces la sobrecarga de la pereza puede ser demasiado o un desperdicio. Entonces, para lidiar con eso, puede pedirle al compilador que evalúe completamente los argumentos de una función en lugar de analizar los thunks.

Hay más información en esta página: Performance/Strictness .

Encontré la siguiente definición cuando trato de aprender Haskell usando un proyecto real para manejarlo. No entiendo lo que significa el signo de exclamación frente a cada argumento y mis libros no parecen mencionarlo.

data MidiMessage = MidiMessage !Int !MidiMessage


Es una declaración de rigor. Básicamente, significa que debe evaluarse a lo que se denomina "forma de cabeza normal débil" cuando se crea el valor de la estructura de datos. Veamos un ejemplo, para que podamos ver lo que esto significa:

data Foo = Foo Int Int !Int !(Maybe Int) f = Foo (2+2) (3+3) (4+4) (Just (5+5))

La función f anterior, cuando se evalúa, devolverá un "procesador": es decir, el código que se ejecutará para determinar su valor. En ese punto, un Foo ni siquiera existe todavía, solo el código.

Pero en algún momento alguien puede intentar mirar dentro, probablemente a través de una coincidencia de patrón:

case f of Foo 0 _ _ _ -> "first arg is zero" _ -> "first arge is something else"

Esto va a ejecutar suficiente código para hacer lo que necesita, y no más. Así que creará un Foo con cuatro parámetros (porque no puedes mirar dentro de él sin que exista). El primero, ya que lo estamos probando, necesitamos evaluar todo el camino hasta 4 , donde nos damos cuenta de que no coincide.

El segundo no necesita ser evaluado, porque no lo estamos probando. Por lo tanto, en lugar de que 6 se almacenen en esa ubicación de memoria, solo almacenaremos el código para una posible evaluación posterior, (3+3) . Eso se convertirá en un 6 solo si alguien lo mira.

El tercer parámetro, sin embargo, tiene un ! delante de él, se evalúa estrictamente: (4+4) se ejecuta y 8 se almacena en esa ubicación de memoria.

El cuarto parámetro también es estrictamente evaluado. Pero aquí es donde se pone un poco complicado: no estamos evaluando completamente, sino solo en forma de cabeza normal débil. Esto significa que averiguamos si es Nothing o Just algo, y almacenamos eso, pero no vamos más allá. Eso significa que almacenamos no Just 10 sino en realidad Just (5+5) , dejando el thunk sin evaluar. Es importante saber esto, aunque creo que todas las implicaciones de esto van más allá del alcance de esta pregunta.

Puede anotar los argumentos de la función de la misma manera, si habilita la extensión de lenguaje BangPatterns :

f x !y = x*y

f (1+1) (2+2) devolverá el procesador (1+1)*4 .


Una forma sencilla de ver la diferencia entre argumentos de constructor estrictos y no estrictos es cómo se comportan cuando no están definidos. Dado

data Foo = Foo Int !Int first (Foo x _) = x second (Foo _ y) = y

Dado que el argumento no estricto no se evalúa por second , el pasar undefined no causa un problema:

> second (Foo undefined 1) 1

Pero el argumento estricto no puede ser undefined , incluso si no usamos el valor:

> first (Foo 1 undefined) *** Exception: Prelude.undefined