Comment définir une fonction dans ghci sur plusieurs lignes?

161

J'essaie de définir n'importe quelle fonction simple qui s'étend sur plusieurs lignes dans ghci, prenons l'exemple suivant:

let abs n | n >= 0 = n
          | otherwise = -n

Jusqu'à présent, j'ai essayé d'appuyer sur Entrée après la première ligne:

Prelude> let abs n | n >= 0 = n
Prelude>           | otherwise = -n
<interactive>:1:0: parse error on input `|'

J'ai également essayé d'utiliser les commandes :{et :}mais je ne vais pas loin:

Prelude> :{
unknown command ':{'
use :? for help.

J'utilise GHC Interactive version 6.6 pour Haskell 98 sous Linux, que me manque-t-il?

Peter McG
la source
20
Veuillez mettre à jour votre installation GHC. GHC 6.6 a presque 5 ans! Les dernières versions de Haskell sont ici: haskell.org/platform
Don Stewart
duplication possible des commandes multilignes dans GHCi
mmmmmm
1
@Mark Cet OP a déjà essayé les solutions à ce problème. Ce problème est dû à un ghci obsolète et non à un manque de connaissances sur ce qu'il faut faire. Solution ici: mise à niveau. Solution là: l' utilisation :{, :}.
AndrewC

Réponses:

124

Pour les gardes (comme votre exemple), vous pouvez simplement les mettre tous sur une seule ligne et cela fonctionne (les gardes ne se soucient pas de l'espacement)

let abs n | n >= 0 = n | otherwise = -n

Si vous vouliez écrire votre fonction avec plusieurs définitions dont le modèle correspond aux arguments, comme ceci:

fact 0 = 1
fact n = n * fact (n-1)

Ensuite, vous utiliseriez des accolades avec des points-virgules séparant les définitions

let { fact 0 = 1 ; fact n = n * fact (n-1) }
newacct
la source
258

GHCi dispose désormais d'un mode d'entrée multiligne, activé avec: set + m. Par exemple,

Prelude> :set +m
Prelude> let fac 0 = 1
Prelude|     fac n = n * fac (n-1)
Prelude|
Prelude> fac 10
3628800
Karakfa
la source
39
La définition du mode multiligne fait ghcise comporter un peu comme l'interpréteur Python à cet égard. Très pratique! Vous pouvez en effet créer un .ghcifichier dans votre répertoire personnel dans lequel vous le mettez :set +met le mode multiligne deviendra la valeur par défaut à chaque démarrage ghci!
kqr
2
C'est vraiment génial. Mais j'ai remarqué que lorsque j'ai défini mon invite en utilisant :set prompt "λ "les lignes continues, dites Preludeau lieu de λ. Un moyen de contourner cela?
abhillman
2
Voir ici le patch pour définir une nouvelle invite de continuation ghc.haskell.org/trac/ghc/ticket/7509#no1
karakfa
4
Pour éviter que Prelude n'apparaisse sur les lignes de continuation, ajoutez également: set prompt2 "|" dans votre .ghci.
Nick le
12
Vous pouvez éviter complètement l' indentation en utilisant une fin let. Tapez simplement a letsuivi d'une nouvelle ligne: let⏎. Puis fac 0 = 1⏎. Puis fac n = n * fac (n-1)⏎ ⏎ et vous avez terminé!
Iceland_jack
62

Dan est correct, mais :{et :}doit chaque apparaître sur leur propre ligne:

> :{ 
> let foo a b = a +
>           b
> :}
> :t foo
foo :: (Num a) => a -> a -> a

Cela interagit également avec la règle de mise en page, donc lorsque vous utilisez la notation do, il peut être plus facile d'utiliser explicitement des accolades et des points-virgules. Par exemple, cette définition échoue:

> :{
| let prRev = do
|   inp <- getLine
|   putStrLn $ reverse inp
| :}
<interactive>:1:18:
    The last statement in a 'do' construct must be an expression

Mais cela fonctionne lorsque des accolades et des points-virgules sont ajoutés:

> :{
| let prRev = do {
|   inp <- getLine;
|   putStrLn $ reverse inp;
| }
| :}
> :t prRev
prRev :: IO ()

Cela n'a vraiment d'importance que lors du collage de définitions à partir d'un fichier, où l'indentation peut changer.

Justin Bailey
la source
Cela ne fonctionne pas si vous avez une ligne se terminant par '=' (avec la définition qui suit sur la ligne suivante), au moins dans la version 7.6.3.
AdamC
1
Peut-être que cela échoue, parce que la deuxième et la troisième ligne du let ne sont pas assez en retrait…? (
Encore
7

Si vous ne souhaitez pas mettre à niveau GHC uniquement pour :{et :}, vous devrez tout écrire sur une seule ligne:

> let abs' n | n >= 0 = n | otherwise = -n

Je ne connais aucune définition unique dans Haskell qui doit être écrite sur plusieurs lignes. Ce qui précède fonctionne en effet dans GHCi:

> :t abs'
abs' :: (Num a, Ord a) => a -> a

Pour les autres expressions, telles que les doblocs, vous devrez utiliser la syntaxe sans mise en page avec des accolades et des points-virgules (eugh).

CA McCann
la source
0

J'utilise GHCi, version 8.2.1 sur macOS Catalina 10.15.2. Voici comment je mets ensemble la déclaration de type de fonction et les gardes. Notez que les barres verticales sur la gauche correspondent à plusieurs lignes GHCi.

λ: let abs' :: (Num a, Ord a) => a -> a
 |     abs' n | n >= 0 = n | otherwise = -n
 | 
λ: abs' 7
7
λ: abs' (-7)
7
Pouce d'or
la source
1
Si vous utilisez :{et que :}vous n'avez pas besoin de spécifier let avant votre déclaration de type, cela signifie que vous n'avez pas besoin d'indenter la deuxième ligne et les suivantes.
davidA
Merci beaucoup davidA. C'est exactement ce que je cherchais mais je n'ai pas réussi à trouver.
Golden Thumb
0

Il semble que le fait de coller les deux lignes à la fois ou d'utiliser le contrôle-entrée pour chaque nouvelle ligne garde tout ensemble, au moins à https://repl.it/languages/haskell . Vous verrez 2 points au début de la deuxième ligne. Ou placez-le dans un fichier et: chargez le fichier (: l main). Comment se fait-il que les abdominaux ne fonctionnent pas avec des nombres négatifs? Oh, vous devez mettre des parenthèses autour du nombre.

   let abs n | n >= 0 = n 
..           | otherwise = -n
   abs (-1)
js2010
la source