la función seq y el rigor


Me he estado preguntando mucho sobre esto, pero no he podido encontrar nada al respecto.

Cuando se utiliza la función seq, ¿cómo funciona entonces realmente? En todas partes, solo se explica diciendo que seq a b evalúa a, descarta el resultado y devuelve b.

Pero ¿qué significa eso realmente? Sería el siguiente resultado en una evaluación estricta:

foo s t = seq q (bar q t) where
      q = s*t

Lo que quiero decir es, es q evaluado estrictamente antes de ser utilizado en bar? Y sería la siguiente ser equivalente:

foo s t = seq (s*t) (bar (s*t) t)

Me resulta un poco difícil obtener detalles sobre la funcionalidad de esta función.

Author: Undreren, 2012-06-15

2 answers

No estás solo. seq es probablemente una de las funciones de Haskell más difíciles de usar correctamente, por varias razones diferentes. En su primer ejemplo:

foo s t = seq q (bar q t) where
      q = s*t

q se evalúa antes de que se evalúe bar q t. Si bar q t nunca se evalúa, q tampoco lo será. Así que si tienes

main = do
    let val = foo 10 20
    return ()

Como val nunca se usa, no se evaluará. Así que q tampoco será evaluado. Si en su lugar tiene

main = print (foo 10 20)

El resultado de foo 10 20 se evalúa (por print), por lo que dentro foo q se evalúa antes del resultado de bar.

Esta es también la razón por la que esto no funciona:

myseq x = seq x x

Semánticamente, esto significa que el primer x será evaluado antes de que el segundo x sea evaluado. Pero si el segundo x nunca es evaluado, el primero tampoco necesita ser evaluado. Así que seq x x es exactamente equivalente a x.

Su segundo ejemplo puede o no ser lo mismo. Aquí, la expresión s*t será evaluada antes que bar salida, pero puede no ser el mismo s*t que el primer parámetro de bar. Si el compilador realiza la eliminación de subexpresión común, puede compartir las dos expresiones idénticas. Sin embargo, GHC puede ser bastante conservador sobre dónde hace CSE, por lo que no puede confiar en esto. Si defino bar q t = q*t realiza el CSE y evalúa s*t antes de usar ese valor en bar. Puede que no lo haga para expresiones más complejas.

Es posible que también desee saber lo que se entiende por estricto evaluación. seq evalúa el primer argumento a weak head normal form (WHNF), que para los tipos de datos significa desempaquetar el constructor más externo. Considere esto:

baz xs y = seq xs (map (*y) xs)

xs debe ser una lista, debido a map. Cuando seq lo evalúa, esencialmente transformará el código en

case xs of
  [] -> map (*y) xs
  (_:_) -> map (*y) xs

Esto significa que determinará si la lista está vacía o no, luego devolverá el segundo argumento. Tenga en cuenta que ninguno de los valores de la lista se evalúa. Así que usted puede hacer esto:

Prelude> seq [undefined] 4
4

Pero no esto{[41]]}

Prelude> seq undefined 5
*** Exception: Prelude.undefined

Sea cual sea el tipo de datos que use para el primer argumento de seq, evaluar a WHNF irá lo suficientemente lejos como para averiguar el constructor y no más. A menos que el tipo de datos tenga componentes marcados como estrictos con un patrón bang. Entonces todos los campos estrictos también serán evaluados a WHNF.

Editar: (gracias a Daniel Wagner por sugerencia en comentarios)

Para las funciones, seq evaluará la expresión hasta que la función "tiene una presentación lambda", lo que significa que está listo para su aplicación. Aquí hay algunos ejemplos que podrían demostrar lo que esto significa:

-- ok, lambda is outermost
Prelude> seq (\x -> undefined) 'a'
'a'

-- not ok.  Because of the inner seq, `undefined` must be evaluated before
-- the lambda is showing
Prelude> seq (seq undefined (\x -> x)) 'b'
*** Exception: Prelude.undefined

Si piensa en un enlace lambda como un constructor de datos (incorporado), seq on functions es perfectamente consistente con usarlo en datos.

También, "enlace lambda" subsume todos los tipos de definiciones de función, ya sea definida por notación lambda o como una función normal.

La Controversia sección de los HaskellWiki seq page tiene un poco sobre algunas de las consecuencias de seq en relación con las funciones.

 30
Author: John L,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-17 13:16:51

Puedes pensar en seq como:

seq a b = case a of
            _ -> b

Esto evaluará a a la forma normal de la cabeza (WHNF) y luego continuará con la evaluación b.

Editar después del comentario de augustss: este case ... of es el strict, GHC Core one, que siempre fuerza su argumento.

 4
Author: gfour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-15 13:49:55