Polecenia wieloliniowe w GHCi

139

Mam problem z wprowadzaniem poleceń wielowierszowych w ghci.

Poniższy dwuwierszowy kod działa z pliku:

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

Ale kiedy wchodzę w ghci, pojawia się błąd:

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

Próbowałem też umieścić kod w środku :{ ... :}, ale one również nie działają w tym przykładzie, ponieważ jest to po prostu dołączanie wierszy do jednej linii, co nie powinno mieć miejsca.

Używam WinGHCi w wersji 2011.2.0.1

R71
źródło

Odpowiedzi:

189

W większości przypadków możesz polegać na wnioskowaniu o typie, aby opracować podpis za Ciebie. W Twoim przykładzie wystarczy:

Prelude> let addTwo x y = x + y

Jeśli naprawdę potrzebujesz definicji z podpisem typu lub jeśli twoja definicja obejmuje wiele linii, możesz to zrobić w ghci:

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

Zauważ, że możesz również wcisnąć to w jedną linię:

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

Więcej informacji na temat interakcji z ghci można znaleźć w interaktywnej ocenie w sekcji instrukcji w dokumentacji.

Nicolas Wu
źródło
1
Bardzo dziękuję za oba rozwiązania. Ale mam inne powiązane pytanie: dlaczego cztery wiodące spacje są wymagane w drugiej linii (przed addTwo)? I to musi być dokładne, jeśli jest mniej lub więcej pustych znaków, to jest błąd.
R71
9
@Rog letrozpoczyna blok; wpisy w bloku są grupowane według wcięć; a pierwszy niebielony znak w bloku ustawia wcięcie, według którego są one zgrupowane. Ponieważ pierwszym niebiałym znakiem w letpowyższym bloku jest znak aof addTwo, wszystkie wiersze w bloku muszą być wcięte dokładnie tak głęboko, jak to a.
Daniel Wagner,
Dzięki. Zauważyłem też, że w innych blokach let / where. Jest to duża różnica w porównaniu z innymi językami, w których białe spacje są ignorowane, więc miałem pewne trudności z zrozumieniem tego.
R71
130

Rozwiąż ten problem, uruchamiając GHCI i wpisując :set +m:

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

Bum.


To, co się tutaj dzieje (i mówię głównie do ciebie , osoby szukającej pomocy podczas pracy w Learn You A Haskell ), to fakt, że GHCI to interaktywne środowisko, w którym zmieniasz wiązania nazw funkcji w locie. Musisz zawinąć definicje funkcji w letblok, aby Haskell wiedział, że masz zamiar coś zdefiniować. To :set +mjest skrótem dla konstrukcji :{ kodu wielowierszowego :}.

Białe znaki są również istotne w blokach, więc musisz dodać wcięcie definicji funkcji po definicji typu o cztery spacje, aby uwzględnić cztery spacje w let.

Adrian
źródło
5
Tak proste, ale nieoczywiste. Chciałem krzyczeć na książkę, której używałem, ponieważ nie powiedział mi tego na stronie 1!
Tim
2
Z powłoki systemu Linux, echo ':set +m' >> ~/.ghciaby to ustawienie było trwałe.
eigenfield
możesz mieć letsamego siebie w pierwszej linii, wtedy cała reszta nie musi być w ogóle wcięta. gdzie białe znaki naprawdę się liczą, to nie może być końcowych spacji w liniach. końcowe białe znaki liczą się jako dodatkowe Enter i przerywają wieloliniowy blok.
Will Ness
14

Zastosowanie let:

Prelude> :{
Prelude| let addTwo :: Int -> Int -> Int
Prelude|     addTwo x y = x + y
Prelude| :}
Prelude> addTwo 2 3
5
Stefan Holdermans
źródło
5

Począwszy od GHCI wersji 8.0.1 , letnie jest już wymagane do zdefiniowania funkcji na REPL.

Więc to powinno działać dobrze dla Ciebie:

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

Wnioskowanie o typie Haskella zapewnia uogólnione typowanie, które działa również dla pływaków:

λ: addTwo 2.0 1.0
3.0

Jeśli musisz podać własne wpisywanie, wydaje się, że będziesz musiał użyć letpołączenia z wejściem wielowierszowym (użyj, :set +maby włączyć wprowadzanie wielowierszowe w GHCI):

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

Ale pojawią się błędy, jeśli spróbujesz przekazać coś innego niż z Intpowodu niepolimorficznego pisania:

λ: 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.0In the expression: addTwo 2.0 1.0
      In an equation for ‘it’: it = addTwo 2.0 1.0
Aaron Hall
źródło
2

Aby rozwinąć odpowiedź Aarona Halla , przynajmniej w wersji GHCi 8.4.4 nie musisz używać letz deklaracjami typu, jeśli używasz :{ :}stylu. Oznacza to, że nie musisz się martwić dodawaniem wcięcia z 4 spacjami w każdym kolejnym wierszu w celu uwzględnienia let, co znacznie ułatwi wpisywanie dłuższych funkcji lub w wielu przypadkach kopiowanie i wklejanie (ponieważ oryginalne źródło prawdopodobnie nie będzie miało prawidłowe wcięcie):

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

Aktualizacja

Alternatywnie możesz włączyć wielowierszowy tryb wprowadzania za pomocą :set +m, a następnie wpisać letsamodzielnie, nacisnąć Enter, a następnie wkleić definicje bez konieczności wcięcia.

Jednak wydaje się, że nie działa to z niektórymi blokami kodu, takimi jak:

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

Ale :{, :}technika robi.

davidA
źródło
1
w rzeczywistości, nawet wcześniej, można było wpisać :{, a następnie w następnym wierszu letsamodzielnie, a następnie wkleić definicje bez żadnego dodanego wcięcia, a następnie zakończyć za pomocą :}. :) a przy wielowierszowym trybie wprowadzania set ( :set +m) nie potrzebowałeś nawet poleceń nawiasów klamrowych, o ile nie było końcowych spacji w liniach kodu.
Will Ness
Ach, więc :set +mmożesz po prostu użyć letna własnej linii? Więc możesz - to super. Dziękuję Ci.
davidA
Hmm, znalazłem kilka przypadków, w których zwykłe pisanie, leta następnie znak nowej linii nie działa. Zobacz moją edycję.
davidA