Многострочные команды в GHCi

У меня проблема с вводом многострочных команд в ghci.

Следующий двухстрочный код работает с файлом:

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

Но когда вхожу в ghci, выдает ошибку:

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

Я также попытался поместить код внутрь :{ ... :}, но они также не работают в этом примере, потому что это просто добавление строк в одну строку, чего не должно быть.

Я использую WinGHCi версии 2011.2.0.1


person R71    schedule 09.12.2011    source источник
comment
возможный дубликат Как определить функцию в ghci для нескольких линии?   -  person devnull    schedule 18.05.2014


Ответы (5)


В большинстве случаев вы можете полагаться на вывод типа, чтобы разработать для вас подпись. В вашем примере достаточно:

Prelude> let addTwo x y = x + y

Если вам действительно нужно определение с сигнатурой типа или ваше определение занимает несколько строк, вы можете сделать это в ghci:

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

Обратите внимание, что вы также можете сжать это на одной строке:

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

Вы можете узнать больше о взаимодействии с ghci в интерактивной оценке в командной строке раздел документации.

person Nicolas Wu    schedule 09.12.2011
comment
Большое спасибо за оба решения. Но у меня есть еще один связанный с этим вопрос: почему во второй строке (перед addTwo) требуются четыре ведущих пробела? И это должно быть точно, если пробелов меньше или больше, значит, возникает ошибка. - person R71; 09.12.2011
comment
@Rog let начинает блок; записи в блоке сгруппированы по отступам; а первый непробельный символ в блоке устанавливает отступ, по которому они сгруппированы. Поскольку первый непробельный символ в блоке let выше - это a из addTwo, все строки в блоке должны иметь такой же глубокий отступ, как этот a. - person Daniel Wagner; 09.12.2011
comment
Спасибо. Я также заметил, что в других блоках let / where. Это большое отличие от других языков, где пробелы игнорируются, поэтому мне было трудно это понять. - person R71; 10.12.2011

Решите эту проблему, запустив GHCI и набрав :set +m:

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

Бум.


Что здесь происходит (и я говорю в основном с вами, человеком, ищущим помощи в Google, пока вы работаете над Learn You A Haskell) заключается в том, что GHCI - это интерактивная среда, в которой вы меняете привязки имен функций на лету. Вы должны заключить свои определения функций в блок let, чтобы Haskell знал, что вы собираетесь что-то определить. Материал :set +m - это сокращение для многострочной конструкции :{ code :}.

Пробелы также важны в блоках, поэтому вам нужно сделать отступ в определении функции после определения типа на четыре пробела, чтобы учесть четыре пробела в let.

person adrian    schedule 12.11.2012
comment
Так просто, но не очевидно. Мне хотелось кричать на книгу, которую я использовал, за то, что она не сказала мне об этом на странице 1! - person Tim; 11.08.2016
comment
В оболочке Linux echo ':set +m' >> ~/.ghci, чтобы сделать этот параметр постоянным. - person eigenfield; 02.11.2018
comment
у вас может быть let сам по себе в первой строке, тогда все остальные не нужно делать с отступом. там, где действительно учитываются пробелы, в ваших строках не должно быть конечных пробелов. завершающие пробелы считаются дополнительным вводом и разбивают многострочный блок. - person Will Ness; 12.05.2020

Используйте 1_:

Prelude> :{
Prelude| let addTwo :: Int -> Int -> Int
Prelude|     addTwo x y = x + y
Prelude| :}
Prelude> addTwo 2 3
5
person Stefan Holdermans    schedule 09.12.2011

Начиная с GHCI версии 8.0.1, let больше не требуется для определения функций в REPL.

Так что это должно сработать для вас:

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

Вывод типов в Haskell обеспечивает обобщенную типизацию, которая также работает для чисел с плавающей запятой:

λ: addTwo 2.0 1.0
3.0

Если вы должны предоставить свой собственный набор текста, кажется, вам нужно использовать let в сочетании с многострочным вводом (используйте :set +m, чтобы включить многострочный ввод в GHCI):

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

Но вы получите ошибки, если попытаетесь передать что-нибудь, кроме Int, из-за вашего неполиморфизма типизации:

λ: 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
person Aaron Hall    schedule 30.12.2018

Чтобы расширить ответ Аарона Холла, по крайней мере, в версии GHCi 8.4.4 вам не нужно использовать let с типом объявления, если вы используете стиль :{ :}. Это означает, что вам не нужно беспокоиться о добавлении отступа из 4 пробелов в каждую последующую строку для учета let, что значительно упрощает ввод более длинных функций или, во многих случаях, копипасту (поскольку исходный источник, скорее всего, не иметь правильный отступ):

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

Обновлять

В качестве альтернативы вы можете включить многострочный режим ввода с помощью :set +m, затем ввести let самостоятельно, нажать Enter, а затем вставить определения без необходимости отступа.

Однако это не работает с некоторыми блоками кода, такими как:

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

Но техника :{, :} работает.

person davidA    schedule 12.05.2020
comment
на самом деле, даже раньше вы могли ввести :{, затем в следующей строке let отдельно, затем вставить свои определения без каких-либо дополнительных отступов, а затем закрыть с помощью :}. :) и с установленным режимом многострочного ввода (:set +m) вам даже не нужны были команды фигурных скобок, пока в строках кода не было конечных пробелов. - person Will Ness; 12.05.2020
comment
Ах, так что с :set +m вы можете просто использовать let в отдельной строке? Так что можно - это круто. Спасибо. - person davidA; 14.05.2020
comment
Хм, я обнаружил несколько случаев, когда простой ввод let, а затем новая строка не работает. Смотрите мою правку. - person davidA; 14.05.2020