ref rápida:
El tipo de evaluate
es:
evaluate :: a -> IO a
seq
tiene el tipo a -> b -> b
. Primero evalúa el primer argumento, luego devuelve el segundo argumento.
Evaluar sigue estas tres reglas:
evaluate x `seq` y ==> y
evaluate x `catch` f ==> (return $! x) `catch` f
evaluate x >>= f ==> (return $! x) >>= f
La diferencia entre el return $! x
y (return $! x) >>= return
se hace evidente con esta expresión:
evaluate undefined `seq` 42
por la primera regla, que debe evaluar a 42
Con el return $! x
definición, la expresión anterior causaría una excepción indefinida. Esto tiene el valor ⊥, que no es igual a 42.
Con la definición (return $! x) >>= return
, que es igual a 42.
Básicamente, la forma return $! x
es estricta cuando se calcula el valor IO. La otra forma solo es estricta cuando se ejecuta el valor IO y se usa el valor (usando >>=
).
Ver this mailing list thread para más detalles.
¿Esto no infringe realmente las leyes de mónadas? – leftaroundabout
@leftaroundabout No, no es así. Ambos se comportan exactamente igual si se ejecutan, pero si 'seq' las expresiones,' devuelve $! x' tiene un 'seq' más externo, mientras que' (return $! x) >> = return' tiene un '(>> =)' outermost. –
@leftaroundabout: No, porque ⊥ se ignora a los efectos de las leyes. Las mónadas estándar como 'Reader' se comportan de la misma manera. (No me creo el argumento de Daniel Fischer (que he escuchado antes de otros), porque "comportarse exactamente igual si se ejecuta" no es realmente un concepto bien definido.) – ehird