C'est en effet le résultat de la paresse. Paresse signifie que la simple définition d'une valeur ne signifie pas qu'elle sera évaluée; Cela n'arrivera que si c'est nécessaire pour quelque chose. Si ce n'est pas nécessaire, le code qui le produirait ne "fait rien". Si une valeur particulière est nécessaire le code est exécuté, mais seulement la première fois il serait nécessaire; S'il existe d'autres références à la même valeur et qu'elle est utilisée à nouveau, ces utilisations utiliseront directement la valeur qui a été produite la première fois.
Vous devez vous rappeler que fonctions sont les valeurs dans tous les sens du terme; tout ce qui s'applique aux valeurs ordinaires s'applique également aux fonctions. Donc votre définition de f
écrit simplement une expression pour une valeur, l'évaluation de l'expression sera différée jusqu'à ce que la valeur de f
soit réellement nécessaire, et comme il faut deux fois la valeur (fonction) l'expression calculée sera sauvegardée et réutilisée la deuxième fois .
permet de regarder plus en détail:
f=trace("f was called")$(+1)
vous définissez une valeur f
avec une équation simple (ne pas utiliser de sucre syntaxique pour les arguments d'écriture sur le côté gauche de l'équation, ou de fournir cas par le biais de plusieurs équations). Donc, nous pouvons simplement prendre le côté droit comme une seule expression qui définit la valeur f
. Il suffit de le définir ne fait rien, il est assis là jusqu'à ce que vous appelez:
print $ f 1
maintenant imprimer a besoin son argument évalué, donc cela force l'expression f 1
. Mais nous ne pouvons pas appliquer f
à 1
sans forcer d'abord f
. Nous devons donc déterminer quelle fonction l'expression trace "f was called" $ (+1)
évalue. Donc trace
est effectivement appelé, son impression IO dangereuse et f was called
apparaît sur le terminal, puis trace
renvoie son deuxième argument: (+1)
. Nous connaissons maintenant la fonction f
: (+1)
. f
sera maintenant une référence directe à cette fonction, sans avoir besoin d'évaluer le code original trace("f was called")$(+1)
si f
est appelée à nouveau. C'est pourquoi la seconde print
ne fait rien.
Cette affaire est tout à fait différente, même si elle pourrait ressembler:
f n=trace("f was called:"++show n)$n+1
Ici, nous sont en utilisant le sucre syntaxique pour définir des fonctions en écrivant des arguments sur le côté gauche.Nous allons desugar que la notation lambda pour voir plus clairement ce que la valeur réelle étant liée à f
est:
f = \n -> trace ("f was called:" ++ show n) $ n + 1
Ici, nous avons écrit une valeur de fonction directement, plutôt que d'une expression qui peut être évaluée pour entraîner une fonction. Donc, quand f
doit être évalué avant qu'il puisse être appelé 1
, la valeur de f
est cette fonction entière; l'trace
appel est à l'intérieur la fonction au lieu d'être la chose qui est appelée à résultat dans une fonction. Donc, trace
n'est pas appelé dans le cadre de l'évaluation f
, il est appelé dans le cadre de l'évaluation de l'application f 1
. Si vous avez sauvegardé le résultat de cette opération (disons en faisant let x = f 1
) et que vous l'avez imprimé plusieurs fois, vous ne verrez que la trace. Mais quand nous arrivons à évaluer f 2
, l'appel trace
est toujours là à l'intérieur de la fonction qui est la valeur de f
, donc quand f
est appelé à nouveau, c'est trace
.
Notez que 'trace' a le type' String -> a -> a'. Le type qui est substitué à 'a' dans le cas' f = 'est différent de celui dans le cas' f n = '. Cela devrait également aider à expliquer le comportement différent. –