Vous pouvez imaginer indentation Haskell et Python de même, mais il y a quelques petites différences. Cependant, la plus grande différence est probablement que la syntaxe sensible à l'indentation de Python démarre toujours le bloc aligné sur une nouvelle ligne, alors que Haskell a des constructions syntaxiques avec des blocs alignés qui peuvent commencer partiellement à travers une ligne existante. Ce n'est pas vraiment une différence dans les règles de mise en page, mais cela affecte énormément votre façon de penser (Haskellers n'a pas tendance à simplifier autant les règles que les «niveaux de retrait» dans leur tête).
Voici un exemple de quelques-uns (horribles) syntaxe de mise en page sensible en Python:
if True:
x = 1
while (
not x
):
y = 2
Les constructions if
et while
sont suivies d'une série de déclarations Alignés. La première instruction non-espace de caractère de la prochaine instruction doit être indentée à une position plus éloignée que la position d'alignement du bloc externe, et définit l'alignement pour toutes les instructions suivantes dans le même bloc interne. Le premier caractère de chaque instruction doit s'aligner sur une position d'un bloc englobant (qui détermine le bloc dont il fait partie).
Si nous avons ajouté un z = 3
aligné à la position 0, il fera partie du "bloc" global. Si nous l'ajoutons à la position 4, cela fera partie du bloc if
. Si nous l'ajoutons aligné à la position 5, il fera partie du bloc while
. Commencer une déclaration à n'importe quelle autre position serait une erreur de syntaxe.
Notez également qu'il existe des constructions multi-lignes dont l'alignement est totalement hors de propos. Ci-dessus j'ai écrit la condition du while
sur plusieurs lignes en utilisant des parenthèses, alignant même la ligne avec not x
à la position 0. Peu importe que le colon introduisant le bloc indenté se trouvait sur une ligne "désalignée"; les alignements pertinents pour le bloc indenté sont la position du premier caractère non blanc de l'instruction while
(position 4) et la position du premier caractère non blanc de l'instruction suivante (position 5).
Voici une mise en page sensibles (horribles) Haskell:
x = let
y = head . head $ do
_ <- [1, 2, 3]
pure [10]
z = let a = 2
b = 2
in a * b
in y + z
Ici, nous avons let
(deux fois) et l'introduction do
blocs alignés. La définition de x
lui-même fait partie du « bloc » de définitions formant le module, et doit être à la position 0.
Le premier caractère non-blanc de la première définition dans un bloc let
définit l'alignement de l'ensemble autres définitions dans le bloc. Dans le bloc extérieur c'est le y
à la position 3. Cependant la syntaxe de let
ne nécessite pas de saut de ligne avant de commencer le bloc indenté (comme les constructions indentées de Python le font en terminant l'en-tête par deux points et une nouvelle ligne).Le bloc let
interne a a = 2
immédiatement après le let
, mais la position de a
définit toujours l'alignement requis pour les autres définitions dans le bloc (11).
Encore une fois il y a des choses que vous pouvez diviser sur plusieurs lignes où les lignes ne sont pas nécessaires pour s'aligner. Dans Haskell, vous pouvez le faire avec presque tout ce qui n'est pas une construction particulière sensible à la mise en page, alors qu'en Python vous ne pouvez le faire qu'avec des parenthèses ou en terminant une ligne avec une barre oblique inverse. Mais dans Haskell, toutes les lignes faisant partie d'une construction doivent être indentées plus loin que le bloc dont elles font partie. Par exemple, j'ai été en mesure de mettre la partie in a * b
de la définition de z
sur une ligne distincte. Le in
fait partie de la construction syntaxique let
, mais c'est et non partie du bloc aligné de définitions introduit par let
, donc il n'a pas d'exigences particulières d'alignement. Cependant, toute la définition de z = ...
fait partie du bloc de définitions externelet
, donc je n'ai pas pu démarrer la ligne in a * b
à la position 3 ou plus tôt; il s'agit d'une "ligne de continuation" de la définition z
, il est donc nécessaire d'être indenté plus loin que le début de cette définition. Ceci est différent des lignes de continuation de Python, qui n'ont aucune contrainte sur leur indentation.
Le do
introduit également un bloc aligné (des «instructions» plutôt que des définitions). J'aurais pu suivre la première déclaration immédiatement sur le do
, mais j'ai choisi de commencer une nouvelle ligne. Le bloc ici se comporte beaucoup comme un bloc prévu de style Python; J'ai dû le démarrer à une position plus en retrait que le bloc externe (les définitions du let
en position 3), et une fois que j'ai fait cela, toutes les instructions dans le bloc do
doivent être alignées sur la même position (14 , ici). Puisque la ligne suivante après le pure [10]
est z = ...
à partir de la position 3, il termine implicitement le bloc do
car il est aligné sur les définitions du bloc let
, et non sur les instructions du bloc do
.
Dans votre exemple:
fib :: Integer -> Integer
fib n = fib_help n 0 1
where fib_help n a b
|n<=1 = b
|otherwise fib_help (n-1) b (a+b)
La construction nécessitant un alignement est where
, qui introduit un bloc de définitions comme beaucoup let
. L'utilisation de blocs de style Python où vous commencez toujours une nouvelle ligne avant de commencer un nouveau bloc, votre exemple ressemblerait à ceci:
fib :: Integer -> Integer
fib n = fib_help n 0 1
where
fib_help n a b
|n<=1 = b
|otherwise fib_help (n-1) b (a+b)
Ce qui fait l'erreur vous submergent beaucoup plus. Vous n'avez pas démarré le bloc de définitions dans le where
au prochain "niveau d'indentation" de 4 espaces, vous l'avez démarré à la position 10! Et puis est retourné à la position 8 pour le prochain "niveau de retrait". Si vous êtes plus à l'aise avec les «niveaux d'indentation» de style Python qu'avec l'alignement Haskell, formatez simplement vos blocs Haskell de la façon dont Python vous demande de formater ses blocs; après l'en-tête introduisant un bloc, terminez toujours la ligne et commencez le bloc sur la ligne suivante au prochain "tab stop".
Merci! Maintenant je comprends! – Schytheron