Considérons une fonction simple de la question récente :
myButLast :: [a] -> a
myButLast [x, y] = x
myButLast (x : xs) = myButLast xs
myButLast _ = error "List too short"
Nous pouvons demander à GHC de nous donner la sortie du simplifieur avec ghc -ddump-simpl
. (Éventuellement avec un peu de drapeaux supplémentaires comme -dsuppress-module-prefixes
-dsuppress-uniques
.) Si je comprends bien, c'est c'est la dernière étape de la compilation où le résultat ressemble encore au code original de haut niveau. d'origine. Voici donc ce qui est écrit :
-- RHS size: {terms: 21, types: 22, coercions: 0, joins: 0/0}
myButLast :: forall a. [a] -> a
[GblId,
Arity=1,
Str=<S,1*U>,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [30] 100 0}]
myButLast
= \ (@ a) (ds :: [a]) ->
case ds of {
[] -> myButLast1 @ a;
: x ds1 ->
case ds1 of {
[] -> myButLast1 @ a;
: y ds2 ->
case ds2 of {
[] -> x;
: ipv ipv1 -> myButLast_$smyButLast1 @ a y ipv ipv1
}
}
}
Qu'est-ce qui se passe ici ? Voyons voir.
-
À la signature de type, maintenant avec un quantificateur explicite, des sortes d'annotations sont attachées. Je peux deviner qu'elles disent "identifiant global, unaire, de premier niveau". ce qui est tout à fait vrai pour cette fonction. Les autres annotations, comme
WorkFree=True
,Str=<S,1*U>
sont pour moi énigmatiques. -
El "valeur" est maintenant un lambda qui accepte, en plus d'une liste, une variable de type en argument, et procède à l'étude de la liste par analyse de cas.
[] -> myButLast1 @ a
est un glorifié glorifié, alors ignorons-le pour l'instant. La partie intéressante est l'appel àmyButLast_$smyButLast1
(Quel genre de nom est-ce ? J'ai pensé$
ne pouvait pas faire partie d'un identifiant). qui s'avère être une fonction récursive de queue qui parcourt réellement la liste. -
Et le voici, un seul membre de ce que nous reconnaissons comme un bloc mutuellement récursif :
Rec { -- RHS size: {terms: 13, types: 12, coercions: 0, joins: 0/0} myButLast_$smyButLast1 [Occ=LoopBreaker] :: forall a. a -> a -> [a] -> a [GblId, Arity=3, Caf=NoCafRefs, Str=<L,1*U><L,1*U><S,1*U>, Unf=OtherCon []] myButLast_$smyButLast1 = \ (@ a) (sc :: a) (sc1 :: a) (sc2 :: [a]) -> case sc2 of { [] -> sc; : ipv ipv1 -> myButLast_$smyButLast1 @ a sc1 ipv ipv1 } end Rec }
Il est assez lucide, mais il comporte quelques fonctionnalités nouvelles pour nous, comme le bloc récursif délimiteur
Rec ... end Rec
et une remarque énigmatique[Occ=LoopBreaker]
. Les annotations sont également différentes : lesUnf
est vide, et unCaf
apparaît à la place. Je peux seulement en déduire que le siteUnf
le champ intéressant est une qualité de noms définie par le programmeur, alors quemyButLast_$smyButLast1
est créé par le compilateur.
Ainsi, je peux comprendre environ la moitié de ce que le simplificateur me donne, par nombre de lignes, mais de certaines parties, je ne peux même pas commencer à deviner le sens.
-
La prémisse est-elle que la sortie simplifiée est généralement l'intermédiaire le plus utile ? la plus utile, est-elle correcte ?
-
Ma lecture est-elle correcte jusqu'à présent ?
-
Y a-t-il un manuel pour toutes ces remarques cryptiques ? Que signifient-elles ?