Mehrzeilige Befehle in GHCi

134

Ich habe Probleme bei der Eingabe mehrzeiliger Befehle in ghci.

Der folgende zweizeilige Code funktioniert aus einer Datei:

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

Aber wenn ich in ghci eingebe, erhalte ich eine Fehlermeldung:

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

Ich habe auch versucht, den Code einzufügen :{ ... :}, aber sie funktionieren auch in diesem Beispiel nicht, da hier nur die Zeilen in eine Zeile angehängt werden, was nicht der Fall sein sollte.

Ich verwende WinGHCi, Version 2011.2.0.1

R71
quelle

Antworten:

183

Meistens können Sie sich auf Typinferenz verlassen, um eine Signatur für Sie zu erstellen. In Ihrem Beispiel ist Folgendes ausreichend:

Prelude> let addTwo x y = x + y

Wenn Sie wirklich eine Definition mit einer Typensignatur wünschen oder Ihre Definition sich über mehrere Zeilen erstreckt, können Sie dies in ghci tun:

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

Beachten Sie, dass Sie dies auch auf eine Zeile drücken können:

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

Weitere Informationen zur Interaktion mit ghci finden Sie in der interaktiven Evaluierung im Eingabeaufforderungsabschnitt der Dokumentation.

Nicolas Wu
quelle
1
Vielen Dank für beide Lösungen. Aber ich habe noch eine andere verwandte Frage: Warum werden die vier führenden Leerzeichen in der zweiten Zeile (vor addTwo) benötigt? Und das muss genau sein, wenn es weniger oder mehr Leerzeichen gibt, dann liegt ein Fehler vor.
R71
9
@ Rog letbeginnt einen Block; Einträge in einem Block werden nach Einrückung gruppiert. und das erste Nicht-Leerzeichen in einem Block legt den Einzug fest, nach dem sie gruppiert sind. Da das erste Nicht-Leerzeichen im letobigen Block das avon ist addTwo, müssen alle Zeilen im Block genau so tief eingerückt sein a.
Daniel Wagner
Vielen Dank. Mir ist auch aufgefallen, dass in anderen let / where-Blöcken. Dies ist ein großer Unterschied zu anderen Sprachen, in denen Leerzeichen ignoriert werden. Daher hatte ich einige Schwierigkeiten, dies zu verstehen.
R71
124

Lösen Sie dieses Problem, indem Sie GHCI starten und Folgendes eingeben :set +m:

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

Boom.


Was hier vor sich geht (und ich spreche hauptsächlich mit Ihnen , einer Person, die um Hilfe googelt, während Sie sich durch Learn You A Haskell arbeiten ), ist, dass GHCI eine interaktive Umgebung ist, in der Sie die Bindung von Funktionsnamen im laufenden Betrieb ändern. Sie müssen Ihre Funktionsdefinitionen in einen letBlock einschließen, damit Haskell weiß, dass Sie etwas definieren werden. Das :set +mZeug ist eine Abkürzung für das mehrzeilige :{ Codekonstrukt :} .

Leerzeichen sind auch in Blöcken von Bedeutung. Daher müssen Sie Ihre Funktionsdefinition nach Ihrer Typdefinition um vier Leerzeichen einrücken, um die vier Leerzeichen in zu berücksichtigen let.

Adrian
quelle
5
So einfach, aber nicht offensichtlich. Ich wollte das Buch anschreien, das ich benutzte, um mir das auf Seite 1 nicht zu sagen!
Tim
2
Von einer Linux-Shell aus, echo ':set +m' >> ~/.ghcium diese Einstellung dauerhaft zu machen.
Truthadjustr
Sie können letfür sich in der ersten Zeile haben, dann muss der Rest überhaupt nicht eingerückt werden. Wenn das Leerzeichen wirklich zählt, dürfen in Ihren Zeilen keine nachgestellten Leerzeichen stehen. Nachfolgendes Leerzeichen zählt als zusätzliche Eingabe und unterbricht den mehrzeiligen Block.
Will Ness
14

Verwendung let:

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

Ab GHCI Version 8.0.1 , letist nicht mehr auf dem REPL definieren Funktionen erforderlich.

Das sollte also gut für Sie funktionieren:

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

Die Typinferenz von Haskell bietet eine allgemeine Typisierung, die auch für Floats funktioniert:

λ: addTwo 2.0 1.0
3.0

Wenn Sie Ihre eigene Eingabe angeben müssen, müssen Sie anscheinend eine letKombination mit einer mehrzeiligen Eingabe verwenden (verwenden Sie :set +mdiese Option, um die mehrzeilige Eingabe in GHCI zu aktivieren):

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

Sie erhalten jedoch Fehler, wenn Sie Intaufgrund Ihrer nicht polymorphen Typisierung versuchen, etwas anderes als ein zu übergeben :

λ: 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
Aaron Hall
quelle
2

Um die Antwort von Aaron Hall zu erweitern , müssen Sie zumindest in Version GHCi 8.4.4 keine letTypdeklarationen verwenden, wenn Sie den :{ :}Stil verwenden. Dies bedeutet, dass Sie sich nicht um das Hinzufügen der 4-Leerzeichen-Einrückung in jeder nachfolgenden Zeile kümmern müssen, um dies zu berücksichtigen let, wodurch längere Funktionen viel einfacher eingegeben oder in vielen Fällen kopiert und eingefügt werden können (da die ursprüngliche Quelle dies wahrscheinlich nicht hat die richtige Einrückung):

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

Aktualisieren

Als Alternative Sie mit auf mehrzeiliges Eingabemodus schalten kann :set +m, geben Sie dann letauf seine eigene, drücken Sie die Eingabetaste, dann fügen Sie Definitionen ohne Vertiefung erforderlich.

Dies scheint jedoch bei einigen Codeblöcken nicht zu funktionieren, z.

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

Aber die :{, :}Technik der Fall ist.

davidA
quelle
1
Tatsächlich könnten Sie sogar vorher :{in die nächste Zeile letselbst eingeben, dann Ihre Definitionen ohne zusätzlichen Einzug einfügen und dann mit schließen :}. :) und mit dem mehrzeiligen Eingabemodus set ( :set +m) brauchten Sie nicht einmal die geschweiften Klammerbefehle, solange die Codezeilen keine nachgestellten Leerzeichen enthielten.
Will Ness
Ah, also mit :set +mdir kann man einfach letauf einer eigenen Leitung verwenden? Also kannst du - das ist cool. Danke dir.
Davida
Hmm, ich habe einige Fälle gefunden, in denen das einfache Tippen und letdann der Zeilenumbruch nicht funktioniert. Siehe meine Bearbeitung.
Davida