Recherche…


Modèles de bang

Les motifs annotés par un bang ( ! ) Sont évalués strictement plutôt que paresseusement.

foo (!x, y) !z = [x, y, z] 

Dans cet exemple, x et z seront tous deux évalués à la forme normale de la tête faible avant de renvoyer la liste. C'est équivalent à:

foo (x, y) z = x `seq` z `seq` [x, y, z]

Les patterns Bang sont activés en utilisant l'extension de langage Haskell 2010 BangPatterns .

Formes normales

Cet exemple fournit un bref aperçu - pour une explication plus détaillée des formes et des exemples normaux , consultez cette question .

Forme normale réduite

La forme normale réduite (ou simplement la forme normale, lorsque le contexte est clair) d'une expression est le résultat de l'évaluation de toutes les sous-expressions réductibles dans l'expression donnée. En raison de la sémantique non stricte de Haskell (généralement appelée la paresse ), une sous-expression n'est pas réductible si elle est sous un classeur (c'est-à-dire une abstraction lambda - \x -> .. ). La forme normale d'une expression a la propriété que si elle existe, elle est unique.

En d'autres termes, l'ordre de réduction des sous-expressions n'est pas important (en termes de sémantique dénotationnelle). Cependant, la clé de l'écriture de programmes Haskell performants consiste souvent à s'assurer que la bonne expression est évaluée au bon moment, c'est-à-dire à comprendre la sémantique opérationnelle.

Une expression dont la forme normale est elle-même est dite sous forme normale .

Certaines expressions, par exemple, let x = 1:x in x , n'ont pas de forme normale, mais sont toujours productives. L'expression d'exemple a toujours une valeur , si l'on admet des valeurs infinies, qui est la liste [1,1, ...] . D'autres expressions, telles que let y = 1+y in y , n'ont aucune valeur ou leur valeur est undefined .

Faible tête forme normale

Le RNF correspond à une évaluation complète d'une expression - de même, la forme normale de la tête faible (WHNF) correspond à l'évaluation de la tête de l'expression. La tête d'une expression e est entièrement évaluée si e est une application Con e1 e2 .. en et Con est un constructeur; ou une abstraction \x -> e1 ; ou une application partielle f e1 e2 .. en , où une application partielle signifie que f prend plus de n arguments (ou, de manière équivalente, le type de e est un type de fonction). Dans tous les cas, les sous-expressions e1..en peuvent être évaluées ou non pour que l'expression soit dans WHNF - elles peuvent même être undefined .

La sémantique d'évaluation de Haskell peut être décrite en termes de WHNF - pour évaluer une expression e , évaluez-la d'abord à WHNF, puis évaluez récursivement toutes ses sous-expressions de gauche à droite.

La fonction seq primitive est utilisée pour évaluer une expression en WHNF. seq xy est dénotationnellement égal à y (la valeur de seq xy est précisément y ); de plus, x est évalué à WHNF lorsque y est évalué à WHNF. Une expression peut également être évaluée avec WHNF avec un motif bang (activé par l’extension -XBangPatterns ), dont la syntaxe est la suivante:

f !x y = ... 

Dans lequel x sera évalué à WHNF lorsque f est évalué, alors que y n'est pas (nécessairement) évalué. Un motif de bang peut également apparaître dans un constructeur, par exemple

data X = Con A !B C .. N

Dans ce cas, le constructeur Con est dit strict dans le champ B , ce qui signifie que le champ B est évalué à WHNF lorsque le constructeur est appliqué à des arguments suffisants (ici deux).

Motifs paresseux

Les patterns paresseux ou irréfutables (désignés par la syntaxe ~pat ) sont des patterns qui correspondent toujours, sans même regarder la valeur correspondante. Cela signifie que les motifs paresseux correspondent aux valeurs les plus basses. Cependant, les utilisations ultérieures de variables liées à des sous-modèles d'un modèle irréfutable forceront la correspondance du modèle, en évaluant vers le bas à moins que la correspondance ne réussisse.

La fonction suivante est paresseuse dans son argument:

f1 :: Either e Int -> Int
f1 ~(Right 1) = 42

et ainsi nous obtenons

λ» f1 (Right 1)
42
λ» f1 (Right 2)
42
λ» f1 (Left "foo")
42
λ» f1 (error "oops!")
42
λ» f1 "oops!"
*** type mismatch ***

La fonction suivante est écrite avec un pattern paresseux mais utilise en fait la variable du pattern qui force la correspondance, donc échouera pour les arguments Left :

f2 :: Either e Int -> Int
f2 ~(Right x) = x + 1

λ» f2 (Right 1)
2
λ» f2 (Right 2)
3
λ» f2 (Right (error "oops!"))
*** Exception: oops!
λ» f2 (Left "foo")
*** Exception: lazypat.hs:5:1-21: Irrefutable pattern failed for pattern (Right x)
λ» f2 (error "oops!")
*** Exception: oops! 

let liaisons soient paresseuses, se comportent comme des modèles irréfutables:

act1 :: IO ()
act1 = do
    ss <- readLn
    let [s1, s2] = ss :: [String]
    putStrLn "Done"

act2 :: IO ()
act2 = do
    ss <- readLn
    let [s1, s2] = ss
    putStrLn s1

Ici, act1 travaille sur des entrées qui analysent n'importe quelle liste de chaînes, alors que dans act2 putStrLn s1 besoin de la valeur de s1 qui force la correspondance de modèle pour [s1, s2] .

λ» act1
> ["foo"]
Done
λ» act2
> ["foo"]
*** readIO: no parse ***

Champs stricts

Dans une déclaration de data , le fait de préfixer un type avec un bang ( ! ) Fait du champ un champ strict . Lorsque le constructeur de données est appliqué, ces champs seront évalués à la forme normale de tête faible, de sorte que les données dans les champs sont garanties pour être toujours sous la forme normale de tête faible.

Les champs stricts peuvent être utilisés dans les types d’enregistrement et de non-enregistrement:

data User = User
    { identifier :: !Int
    , firstName :: !Text
    , lastName :: !Text
    }

data T = MkT !Int !Int


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow