Vous n'êtes pas seul. seq
est probablement l'un des plus difficiles Haskell fonctions de l'utiliser correctement, pour quelques raisons différentes. Dans votre premier exemple:
foo s t = seq q (bar q t) where
q = s*t
q
est évaluée avant d' bar q t
est évaluée. Si bar q t
n'est jamais évalué, q
ne sera pas non plus. Donc, si vous avez
main = do
let val = foo 10 20
return ()
en tant que val
n'est jamais utilisé, il ne sera pas évalué. Donc, q
ne seront pas évaluées. Si, au contraire, vous avez
main = print (foo 10 20)
le résultat de l' foo 10 20
est évaluée (en print
), de sorte que dans foo
q
est évalué avant le résultat de l' bar
.
C'est aussi pourquoi cela ne fonctionne pas:
myseq x = seq x x
Du point de vue sémantique, cela signifie que le premier x
sera évalué avant le deuxième x
est évaluée. Mais si le second x
n'est jamais évalué, le premier n'a pas besoin de l'être. Donc, seq x x
est exactement équivalent à x
.
Votre deuxième exemple peut ou peut ne pas être la même chose. Ici, l'expression s*t
seront évalués avant d' bar
's de sortie, mais il ne peut pas être le même s*t
comme premier paramètre bar
. Si le compilateur effectue commune de la sous-expression de l'élimination, il peut commun-les deux expressions identiques. GHC peut être assez conservateur sur l'endroit où il n'CST bien, vous ne pouvez pas compter sur cela. Si je définis bar q t = q*t
il ne effectuer le CST et d'évaluer s*t
avant d'utiliser cette valeur dans la barre. Il ne peut pas le faire pour des expressions plus complexes.
Vous pouvez également connaître ce que l'on entend par évaluation stricte. seq
évalue le premier argument de la faiblesse de la tête forme normale (WHNF), qui, pour des types de données signifie déballage le constructeur le plus externe. Réfléchissez à ceci:
baz xs y = seq xs (map (*y) xs)
xs
doit être une liste, en raison de l' map
. Lors de l' seq
évalue, il servira essentiellement à transformer le code en
case xs of
[] -> map (*y) xs
(_:_) -> map (*y) xs
Cela signifie qu'il permettra de déterminer si la liste est vide ou pas, puis retourner le deuxième argument. Notez qu' aucune des valeurs de la liste sont évalués. Ainsi, vous pouvez faire ceci:
Prelude> seq [undefined] 4
4
mais pas cette
Prelude> seq undefined 5
*** Exception: Prelude.undefined
Quel que soit le type de données que vous utilisez pour l' seq
s premier argument, l'évaluation de WHNF va aller assez loin pour comprendre le constructeur et rien de plus. À moins que le type de données possède des composants qui sont marqués comme strict avec un bang modèle. Ensuite, tous les strictes domaines seront également évalués à WHNF.
Edit: (merci à Daniel Wagner pour la suggestion dans les commentaires)
Pour les fonctions, seq
permettra d'évaluer l'expression jusqu'à ce que la fonction "a un lambda de montrer", ce qui signifie qu'il est prêt pour l'application. Voici quelques exemples qui pourraient démontrer ce que cela signifie:
-- 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 vous pensez à un lambda de liaison comme un (built-in) données constructeur, seq
sur des fonctions qui est parfaitement cohérent avec l'utilisant sur des données.
Aussi, "lambda de liaison" englobe tous les types de définitions de fonction, qu'il s'agisse d'lambda ou de notation comme une fonction normale.
La Controverse de la section de la HaskellWiki du seq page a un peu de certaines des conséquences de l' seq
par rapport aux fonctions.