web-dev-qa-db-fra.com

Que signifie le point d'exclamation dans une déclaration Haskell?

Je suis tombé sur la définition suivante en essayant d'apprendre Haskell en utilisant un vrai projet pour le piloter. Je ne comprends pas ce que signifie le point d'exclamation devant chaque argument et mes livres ne semblent pas le mentionner.

data MidiMessage = MidiMessage !Int !MidiMessage
242
David

C'est une déclaration de rigueur. Fondamentalement, cela signifie qu'il doit être évalué par rapport à ce que l'on appelle la "forme de tête normale faible" lorsque la valeur de la structure de données est créée. Regardons un exemple, pour voir ce que cela signifie:

data Foo = Foo Int Int !Int !(Maybe Int)

f = Foo (2+2) (3+3) (4+4) (Just (5+5))

La fonction f ci-dessus, lorsqu'elle est évaluée, renverra un "thunk": c'est-à-dire le code à exécuter pour déterminer sa valeur. À ce stade, un Foo n'existe même pas encore, juste le code.

Mais à un moment donné, quelqu'un peut essayer de regarder à l'intérieur, probablement à travers une correspondance de modèle:

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

Cela va exécuter suffisamment de code pour faire ce dont il a besoin, et pas plus. Il créera donc un Foo avec quatre paramètres (car vous ne pouvez pas regarder à l'intérieur sans qu'il existe). Le premier, puisque nous le testons, nous devons évaluer jusqu'à 4, Où nous réalisons qu'il ne correspond pas.

La seconde n'a pas besoin d'être évaluée, car nous ne la testons pas. Ainsi, au lieu que 6 Soit stocké dans cet emplacement de mémoire, nous allons simplement stocker le code pour une éventuelle évaluation ultérieure, (3+3). Cela ne deviendra un 6 que si quelqu'un le regarde.

Le troisième paramètre, cependant, a un ! Devant lui, donc est strictement évalué: (4+4) Est exécuté, et 8 Est stocké dans cet emplacement de mémoire.

Le quatrième paramètre est également strictement évalué. Mais voici où cela devient un peu délicat: nous n'évaluons pas complètement, mais seulement pour une forme de tête normale faible. Cela signifie que nous déterminons si c'est Nothing ou Just quelque chose, et stockons cela, mais nous n'allons pas plus loin. Cela signifie que nous stockons non pas Just 10 Mais en fait Just (5+5), laissant le thunk à l'intérieur non évalué. Il est important de le savoir, bien que je pense que toutes les implications de cela dépassent la portée de cette question.

Vous pouvez annoter des arguments de fonction de la même manière, si vous activez l'extension de langage BangPatterns:

f x !y = x*y

f (1+1) (2+2) renverra le thunk (1+1)*4.

290
Curt J. Sampson

Un moyen simple de voir la différence entre les arguments de constructeur stricts et non stricts est de savoir comment ils se comportent lorsqu'ils ne sont pas définis. Donné

data Foo = Foo Int !Int

first (Foo x _) = x
second (Foo _ y) = y

Puisque l'argument non strict n'est pas évalué par second, la transmission de undefined ne pose pas de problème:

> second (Foo undefined 1)
1

Mais l'argument strict ne peut pas être undefined, même si nous n'utilisons pas la valeur:

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

Je pense que c'est une annotation de rigueur.

Haskell est un langage fonctionnel pur et paresseux , mais parfois le surcoût de la paresse peut être trop ou gaspiller. Donc, pour y faire face, vous pouvez demander au compilateur d'évaluer pleinement les arguments d'une fonction au lieu d'analyser les thunks.

Il y a plus d'informations sur cette page: Performance/Strictness .

25
Chris Vest