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

135

У меня проблема с вводом многострочных команд в 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

R71
источник

Ответы:

184

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

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 в интерактивной оценке в разделе подсказок документации.

Николас Ву
источник
1
Большое спасибо за оба решения. Но у меня есть еще один связанный с этим вопрос: почему во второй строке (перед addTwo) требуются четыре ведущих пробела? И это должно быть точно, если пробелов меньше или больше, значит ошибка.
R71
9
@Rog letначинает блок; записи в блоке сгруппированы по отступам; а первый непробельный символ в блоке устанавливает отступ, по которому они сгруппированы. Поскольку первый непробельный символ в letблоке выше - это aof addTwo, все строки в блоке должны иметь такой же глубокий отступ a.
Даниэль Вагнер,
Спасибо. Я также заметил, что в других блоках let / where. Это большое отличие от других языков, в которых пробелы игнорируются, поэтому мне было трудно это понять.
R71
125

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

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

Boom.


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

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

Адриан
источник
5
Так просто, но не очевидно. Мне хотелось кричать на книгу, которую я использовал, за то, что она не сказала мне об этом на странице 1!
Тим
2
Из оболочки Linux, echo ':set +m' >> ~/.ghciчтобы сделать этот параметр постоянным.
truthadjustr 01
у вас может быть letодна строка в первой строке, тогда все остальные не нужно делать с отступом. там, где действительно учитываются пробелы, в ваших строках не должно быть конечных пробелов. завершающий пробел считается дополнительным вводом и разбивает многострочный блок.
Уилл Несс
14

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

Prelude> :{
Prelude| let addTwo :: Int -> Int -> Int
Prelude|     addTwo x y = x + y
Prelude| :}
Prelude> addTwo 2 3
5
Стефан Холдерманс
источник
5

Начиная с версии 8.0.1 GHCI , 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

Но вы получите ошибки, если попытаетесь передать что-либо, кроме a 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
Аарон Холл
источник
2

Чтобы расширить ответ Аарона Холла , по крайней мере, в версии 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

Но :{, :}техник делает.

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