Commandes multilignes dans GHCi

135

J'ai des problèmes pour entrer des commandes multilignes dans ghci.

Le code de 2 lignes suivant fonctionne à partir d'un fichier:

addTwo :: Int -> Int -> Int
addTwo x y = x + y

Mais quand j'entre dans ghci, j'obtiens une erreur:

<interactive>:1:1: error:
    Variable not in scope: addTwo :: Int -> Int -> Int

J'ai également essayé de mettre le code à l'intérieur :{ ... :}, mais ils ne fonctionnent pas non plus pour cet exemple, car il s'agit simplement d'ajouter les lignes sur une seule ligne, ce qui ne devrait pas être le cas.

J'utilise WinGHCi, version 2011.2.0.1

R71
la source

Réponses:

184

La plupart du temps, vous pouvez vous fier à l'inférence de type pour élaborer une signature à votre place. Dans votre exemple, ce qui suit est suffisant:

Prelude> let addTwo x y = x + y

Si vous voulez vraiment une définition avec une signature de type, ou si votre définition s'étend sur plusieurs lignes, vous pouvez le faire dans ghci:

Prelude> :{
Prelude| let addTwo :: Int -> Int -> Int
Prelude|     addTwo x y = x + y 
Prelude| :}
Prelude> addTwo 4 7
11

Notez que vous pouvez également presser ceci sur une ligne:

Prelude> let addTwo :: Int -> Int -> Int ; addTwo x y = x + y

Vous pouvez en savoir plus sur l'interaction avec ghci dans l' évaluation interactive dans la section invite de la documentation.

Nicolas Wu
la source
1
Merci beaucoup pour les deux solutions. Mais j'ai une autre question connexe: pourquoi les quatre blancs de tête sont-ils requis dans la deuxième ligne (avant addTwo)? Et cela doit être exact, s'il y a moins ou plus de blancs, alors il y a une erreur.
R71
9
@Rog letcommence un bloc; les entrées d'un bloc sont regroupées par indentation; et le premier caractère non blanc d'un bloc définit l'indentation par laquelle ils sont regroupés. Étant donné que le premier caractère non blanc dans le letbloc ci-dessus est le ade addTwo, toutes les lignes du bloc doivent être indentées exactement aussi profondément que cela a.
Daniel Wagner
Merci. J'ai également remarqué que dans d'autres blocs let / where. C'est une grande différence par rapport aux autres langues où les espaces sont ignorés, j'ai donc eu du mal à comprendre cela.
R71
125

Résolvez ce problème en lançant GHCI et en tapant :set +m:

Prelude> :set +m
Prelude> let addTwo :: Int -> Int -> Int
Prelude|     addTwo x y = x + y
Prelude| 
Prelude> addTwo 1 3
4

Boom.


Ce qui se passe ici (et je parle principalement à vous , personne cherchant de l'aide sur Google tout en travaillant sur Learn You A Haskell ), c'est que GHCI est un environnement interactif dans lequel vous modifiez les liaisons des noms de fonctions à la volée. Vous devez envelopper vos définitions de fonction dans un letbloc, pour que Haskell sache que vous êtes sur le point de définir quelque chose. Le :set +mtruc est un raccourci pour le code multiligne:{ :} construction de .

Les espaces blancs sont également importants dans les blocs, vous devez donc indenter votre définition de fonction après votre définition de type de quatre espaces pour tenir compte des quatre espaces dans let.

Adrian
la source
5
Si simple, mais pas évident. Je voulais crier au livre que j'utilisais pour ne pas me dire ça à la page 1!
Tim
2
Depuis un shell Linux, echo ':set +m' >> ~/.ghcipour rendre ce paramètre persistant.
truthadjustr
vous pouvez avoir tout letseul sur la première ligne, alors tout le reste n'a pas du tout besoin d'être en retrait. là où l'espace blanc compte vraiment, il ne doit pas y avoir d'espaces de fin sur vos lignes. Les espaces de fin comptent comme une entrée supplémentaire et coupent le bloc multiligne.
Will Ness
14

Utilisez let:

Prelude> :{
Prelude| let addTwo :: Int -> Int -> Int
Prelude|     addTwo x y = x + y
Prelude| :}
Prelude> addTwo 2 3
5
Stefan Holdermans
la source
5

À partir de la version 8.0.1 de GHCI , il letn'est plus nécessaire de définir des fonctions sur le REPL.

Cela devrait donc bien fonctionner pour vous:

λ: addTwo x y = x + y
λ: addTwo 1 2
3
λ: :t addTwo
addTwo :: Num a => a -> a -> a

L'inférence de type de Haskell fournit un typage généralisé qui fonctionne également pour les flottants:

λ: addTwo 2.0 1.0
3.0

Si vous devez fournir votre propre typage, il semble que vous devrez utiliser letcombiné avec une entrée multiligne (à utiliser :set +mpour activer l'entrée multiligne dans GHCI):

λ: let addTwo :: Int -> Int -> Int
 |     addTwo x y = x + y
 | 
λ: addTwo 1 2
3

Mais vous obtiendrez des erreurs si vous essayez de passer autre chose que un à Intcause de votre typage non polymorphe:

λ: addTwo 2.0 1.0

<interactive>:34:8: error:
     No instance for (Fractional Int) arising from the literal 2.0
     In the first argument of addTwo’, namely 2.0
      In the expression: addTwo 2.0 1.0
      In an equation for it’: it = addTwo 2.0 1.0
Salle Aaron
la source
2

Pour développer la réponse d'Aaron Hall , dans la version GHCi 8.4.4 au moins, vous n'avez pas besoin d'utiliser letavec les déclarations de type si vous utilisez le :{ :}style. Cela signifie que vous n'avez pas à vous soucier d'ajouter l'indentation de 4 espaces sur chaque ligne suivante à prendre en compte let, ce qui rend les fonctions plus longues beaucoup plus faciles à taper ou, dans de nombreux cas, à copier-coller (car la source d'origine n'aura probablement pas l'indentation correcte):

λ: :{
 | addTwo :: Int -> Int -> Int
 | addTwo x y = x + y
 | :}
λ: addTwo 1 2
3

Mettre à jour

Comme alternative, vous pouvez activer le mode d'entrée multiligne avec :set +m, puis tapezlet seul, appuyer sur Entrée, puis coller les définitions sans indentation requise.

Cependant, cela ne semble pas fonctionner avec certains blocs de code, tels que:

class Box a where
  mkBox :: a -> Boxes.Box

Mais la :{, :}fait technique.

davidA
la source
1
en fait, même avant, vous pouvez taper :{, puis sur la ligne suivante letseule, puis coller vos définitions sans aucune indentation supplémentaire, puis fermer avec :}. :) et avec le mode d'entrée multiligne set ( :set +m), vous n'avez même pas besoin des commandes d'accolades tant qu'il n'y a pas d'espaces de fin sur les lignes de code.
Will Ness
Ah, alors avec :set +mvous pouvez simplement utiliser letsur sa propre ligne? Donc vous pouvez - c'est cool. Je vous remercie.
davidA
Hmm, j'ai trouvé des cas où la simple saisie letpuis la nouvelle ligne ne fonctionne pas. Voir ma modification.
davidA