Haskell Language
Trous dactylographiés
Recherche…
Remarques
L'un des points forts de Haskell est la possibilité de tirer parti du système de types pour modéliser des parties de votre domaine problématique dans le système de types. Ce faisant, on rencontre souvent des types très complexes. Lorsque vous écrivez des programmes avec ces types (c.-à-d. Avec des valeurs ayant ces types), il devient parfois presque impossible de jongler avec tous les types. À partir du GHC 7.8, il existe une nouvelle fonctionnalité syntaxique appelée trous typés. Les trous typés ne modifient pas la sémantique de la langue principale; ils sont uniquement destinés à aider à écrire des programmes.
Pour une explication détaillée des trous dactylographiés, ainsi qu'une discussion sur la conception des trous tapés, voir le wiki Haskell .
Section du guide de l'utilisateur du GHC sur les trous dactylographiés .
Syntaxe des trous tapés
Un trou dactylographié est un seul trait de soulignement ( _
) ou un identifiant Haskell valide qui n'est pas dans la portée, dans un contexte d'expression. Avant l'existence de trous typés, ces deux éléments déclencheraient une erreur. La nouvelle syntaxe n'interfère donc pas avec une ancienne syntaxe.
Contrôle du comportement des trous tapés
Le comportement par défaut des trous typés consiste à générer une erreur de compilation lorsqu’un trou est détecté. Cependant, il existe plusieurs indicateurs pour affiner leur comportement. Ces indicateurs sont résumés comme suit ( trac GHC ):
Par défaut, GHC a activé les trous de saisie et génère une erreur de compilation lorsqu'il rencontre un trou tapé.
Lorsque l'
-fdefer-type-errors
ou-fdefer-typed-holes
est activée, les erreurs de trous sont converties en avertissements et entraînent des erreurs d'exécution lors de l'évaluation.Le drapeau d'avertissement
-fwarn-typed-holes
est-fwarn-typed-holes
par défaut. Sans-fdefer-type-errors
ou-fdefer-typed-holes
cet indicateur est un no-op, car les trous tapés sont une erreur dans ces conditions. Si l'un des indicateurs de report est activé (la conversion des erreurs de trous typées en avertissements), l'-fno-warn-typed-holes
désactive les avertissements. Cela signifie que la compilation réussit silencieusement et que l'évaluation d'un trou produira une erreur d'exécution.
Sémantique des trous tapés
La valeur d'un trou de type peut simplement être undefined
comme undefined
, bien qu'un trou typé déclenche une erreur de compilation, il n'est donc pas strictement nécessaire de lui attribuer une valeur. Cependant, un trou de frappe (lorsqu'ils sont activés) produit une erreur de compilation (ou un avertissement avec des erreurs de type différées) qui indique le nom du trou typé, son type le plus général inféré et les types de liaisons locales. Par exemple:
Prelude> \x -> _var + length (drop 1 x)
<interactive>:19:7: Warning:
Found hole `_var' with type: Int
Relevant bindings include
x :: [a] (bound at <interactive>:19:2)
it :: [a] -> Int (bound at <interactive>:19:1)
In the first argument of `(+)', namely `_var'
In the expression: _var + length (drop 1 x)
In the expression: \ x -> _var + length (drop 1 x)
Notez que dans le cas des trous typés dans les expressions entrées dans le rempl GHCi (comme ci - dessus), le type de l'expression saisie a également signalé, comme it
(ici de type [a] -> Int
).
Utilisation de trous tapés pour définir une instance de classe
Les trous typés peuvent faciliter la définition des fonctions grâce à un processus interactif.
Supposons que vous souhaitiez définir une instance de classe Foo Bar
(pour votre type de Bar
personnalisé, afin de pouvoir l'utiliser avec une fonction de bibliothèque polymorphe nécessitant une instance Foo
). Vous devriez maintenant rechercher la documentation de Foo
, déterminer quelles méthodes vous devez définir, examiner leurs types, etc. - mais avec les trous tapés, vous pouvez en fait passer outre!
Tout d'abord, définissez une instance factice:
instance Foo Bar where
Le compilateur va maintenant se plaindre
Bar.hs:13:10: Warning:
No explicit implementation for
‘foom’ and ‘quun’
In the instance declaration for ‘Foo Bar’
Ok, nous devons donc définir foom
pour Bar
. Mais qu'est - ce que c'est même supposé être? Encore une fois, nous sommes trop paresseux pour regarder dans la documentation, et il suffit de demander au compilateur:
instance Foo Bar where
foom = _
Ici, nous avons utilisé un trou dactylographié comme une simple «requête de documentation». Les sorties du compilateur
Bar.hs:14:10:
Found hole ‘_’ with type: Bar -> Gronk Bar
Relevant bindings include
foom :: Bar -> Gronk Bar (bound at Foo.hs:4:28)
In the expression: _
In an equation for ‘foom’: foom = _
In the instance declaration for ‘Foo Bar’
Notez comment le compilateur a déjà rempli la variable de type de classe avec le type de béton Bar
lequel nous voulons l'instancier. Cela peut rendre la signature beaucoup plus facile à comprendre que celle polymorphe trouvée dans la documentation de la classe, surtout si vous utilisez une méthode plus compliquée, par exemple une classe de type multi-paramètres.
Mais qu'est-ce que c'est que Gronk
? À ce stade, c'est probablement une bonne idée de demander à Hayoo . Cependant , nous pouvons encore sortir sans cela: comme une supposition aveugle, nous supposons que cela est non seulement un constructeur de type , mais aussi le constructeur de valeur unique, à savoir qu'il peut être utilisé en fonction qui produira en quelque sorte une Gronk a
valeur. Nous essayons donc
instance Foo Bar where
foom bar = _ Gronk
Si nous avons de la chance, Gronk
est en fait une valeur, et le compilateur va maintenant dire
Found hole ‘_’
with type: (Int -> [(Int, b0)] -> Gronk b0) -> Gronk Bar
Where: ‘b0’ is an ambiguous type variable
Ok, c'est moche - tout d'abord notez que Gronk
a deux arguments, donc nous pouvons affiner notre tentative:
instance Foo Bar where
foom bar = Gronk _ _
Et maintenant c'est assez clair:
Found hole ‘_’ with type: [(Int, Bar)]
Relevant bindings include
bar :: Bar (bound at Bar.hs:14:29)
foom :: Bar -> Gronk Bar (bound at Foo.hs:15:24)
In the second argument of ‘Gronk’, namely ‘_’
In the expression: Gronk _ _
In an equation for ‘foom’: foom bar = Gronk _ _
Vous pouvez maintenant continuer à progresser, par exemple en déconstruisant la valeur de la bar
(les composants apparaîtront alors, avec les types, dans la section Relevant bindings
). Souvent, il est tout à fait évident que la définition sera correcte, car vous voyez tous les arguments disponibles et les types s'emboîtent comme un puzzle. Ou bien, vous pouvez voir que la définition est impossible et pourquoi.
Tout cela fonctionne mieux dans un éditeur avec une compilation interactive, par exemple Emacs en mode haskell. Vous pouvez ensuite utiliser des trous tapés de la même manière que les requêtes de valeur de souris dans un IDE pour un langage impératif dynamique interprété, mais sans toutes les limitations.