Các lệnh nhiều dòng trong GHCi


134

Tôi gặp vấn đề trong việc nhập các lệnh đa dòng trong ghci.

Mã 2 dòng sau hoạt động từ một tệp:

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

Nhưng khi tôi nhập vào ghci, tôi gặp lỗi:

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

Tôi cũng đã thử đặt mã bên trong :{ ... :}, nhưng chúng cũng không hoạt động cho ví dụ này, bởi vì đây chỉ là nối các dòng thành một dòng, điều này không nên xảy ra.

Tôi đang sử dụng WinGHCi, phiên bản 2011.2.0.1


Câu trả lời:


183

Hầu hết thời gian, bạn có thể dựa vào loại suy luận để tìm ra chữ ký cho bạn. Trong ví dụ của bạn, những điều sau đây là đủ:

Prelude> let addTwo x y = x + y

Nếu bạn thực sự muốn một định nghĩa với chữ ký loại hoặc định nghĩa của bạn trải dài trên nhiều dòng, bạn có thể thực hiện điều này trong ghci:

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

Lưu ý rằng bạn cũng có thể ép nó lên một dòng:

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

Bạn có thể tìm hiểu thêm về việc tương tác với ghci trên phần đánh giá Tương tác tại phần nhắc nhở của tài liệu.


1
Cảm ơn rất nhiều cho cả hai giải pháp. Nhưng tôi có một câu hỏi liên quan khác: tại sao bốn khoảng trống hàng đầu được yêu cầu trong dòng thứ hai (trước addTwo)? Và điều này phải chính xác, nếu có ít hoặc nhiều khoảng trống, thì có lỗi.
R71

9
@Rog letbắt đầu một khối; các mục trong một khối được nhóm theo cách thụt lề; và ký tự không phải khoảng trắng đầu tiên trong một khối sẽ đặt vết lõm mà chúng được nhóm lại. Vì ký tự không phải khoảng trắng đầu tiên trong letkhối ở trên là acủa addTwo, nên tất cả các dòng trong khối phải được thụt vào chính xác như sâu a.
Daniel Wagner

Cảm ơn. Tôi cũng nhận thấy rằng trong các khối let / where khác. Đây là một sự khác biệt lớn so với các ngôn ngữ khác, nơi khoảng trắng bị bỏ qua, vì vậy tôi gặp một số khó khăn trong việc nắm bắt điều đó.
R71

124

Giải quyết vấn đề này bằng cách kích hoạt GHCI và gõ :set +m:

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

Bùng nổ.


Điều gì đang xảy ra ở đây (và tôi đang nói chuyện chủ yếu với bạn , người đang tìm kiếm sự giúp đỡ trong khi tìm hiểu thông qua Learn You A Haskell ) là GHCI là một môi trường tương tác khi bạn thay đổi liên kết tên hàm một cách nhanh chóng. Bạn phải bọc các định nghĩa hàm của bạn trong một letkhối, để Haskell biết rằng bạn sắp xác định một cái gì đó. Các :set +mcông cụ là tốc ký cho cấu trúc :{ đa dòng :}.

Khoảng trắng cũng có ý nghĩa trong các khối, vì vậy bạn phải thụt lề định nghĩa hàm sau định nghĩa kiểu của bạn bằng bốn khoảng trắng để tính cho bốn khoảng trắng trong let.


5
Rất đơn giản, nhưng không rõ ràng. Tôi muốn hét lên với cuốn sách tôi đang sử dụng vì đã không nói với tôi điều đó ở trang 1!
Tim

2
Từ trình bao Linux, echo ':set +m' >> ~/.ghciđể thực hiện cài đặt này liên tục.
Truthadjustr

bạn có thể lettự nó ở dòng đầu tiên sau đó tất cả phần còn lại không cần phải thụt lề. trong đó khoảng trắng thực sự được tính là không được có dấu cách trên các dòng của bạn. khoảng trắng theo sau được tính là một Enter thêm và phá vỡ khối nhiều dòng.
Will Ness

14

Sử dụng let:

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

4

Kể từ phiên bản GHCI 8.0.1 , letkhông còn cần thiết để xác định các chức năng trên REPL.

Vì vậy, điều này sẽ làm việc tốt cho bạn:

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

Kiểu suy luận của Haskell cung cấp kiểu gõ tổng quát cũng hoạt động cho phao:

λ: addTwo 2.0 1.0
3.0

Nếu bạn phải cung cấp cách gõ của riêng mình, có vẻ như bạn sẽ cần sử dụng letkết hợp với đầu vào đa dòng (sử dụng :set +mđể bật đầu vào đa dòng trong GHCI):

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

Nhưng bạn sẽ gặp lỗi nếu bạn cố gắng vượt qua bất cứ điều gì ngoại trừ Intvì cách gõ không đa hình của bạn:

λ: 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

Để mở rộng câu trả lời của Aaron Hall , ít nhất là trong phiên bản GHCi 8.4.4, bạn không cần sử dụng letvới khai báo kiểu nếu bạn sử dụng :{ :}kiểu này. Điều này có nghĩa là bạn không phải lo lắng về việc thêm thụt lề 4 khoảng trống trên mỗi dòng tiếp theo vào tài khoản let, làm cho các chức năng dài hơn dễ dàng hơn để nhập hoặc trong nhiều trường hợp, sao chép-dán (vì nguồn ban đầu có thể sẽ không có thụt lề chính xác):

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

Cập nhật

Để thay thế, bạn có thể bật chế độ nhập nhiều dòng với :set +m, sau đó tự nhập let, nhấn Enter, sau đó dán định nghĩa mà không cần thụt lề.

Tuy nhiên, điều này dường như không hoạt động với một số khối mã, chẳng hạn như:

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

Nhưng :{, :}kỹ thuật nào.


1
trên thực tế, ngay cả trước đó, bạn có thể tự nhập :{, sau đó vào dòng tiếp theo let, sau đó dán định nghĩa của bạn mà không cần thụt dòng nào, sau đó đóng lại :}. :) và với chế độ nhập nhiều dòng được đặt ( :set +m), bạn thậm chí không cần các lệnh niềng răng miễn là không có dấu cách trên các dòng mã.
Will Ness

Ah, vậy với :set +mbạn chỉ có thể sử dụng lettrên dòng riêng của mình? Vì vậy, bạn có thể - đó là mát mẻ. Cảm ơn bạn.
davidA

Hmm, tôi đã tìm thấy một số trường hợp chỉ cần gõ letthì dòng mới không hoạt động. Xem chỉnh sửa của tôi.
davidA
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.