Biểu thức Lambda không có tham số trong phép tính Haskell và / hoặc lambda


9

Trong các ngôn ngữ háo hức như Scheme và Python, bạn có thể sử dụng biểu thức lambda mà không có tham số để trì hoãn đánh giá, ví dụ như trong Scheme (Lược đồ gà):

#;1> (define (make-thunk x) (lambda () (+ x 1)))
#;2> (define t (make-thunk 1))
#;3> (t)
2

Trong dòng 2, tđược liên kết với biểu thức không (lambda () (+ 1 1))được đánh giá, sau đó được ước tính 2trong dòng 3.

Tương tự, trong Python:

>>> def make_thunk(x): return lambda: x + 1
... 
>>> t = make_thunk(1)
>>> t()
2

Sử dụng kỹ thuật này, người ta có thể thực hiện đánh giá lười biếng trong một ngôn ngữ háo hức.

Vì vậy, tôi đã hy vọng rằng Haskell sẽ không có biểu thức lambda nếu không có tham số vì ngôn ngữ đã lười biếng và không cần phải xây dựng biểu thức bị trì hoãn. Thật ngạc nhiên, tôi phát hiện ra rằng trong Haskell có thể viết biểu thức lambda

\() -> "s"

chỉ có thể được áp dụng cho ()giá trị như vậy:

(\() -> "s") ()

đưa ra kết quả

"s"

Áp dụng chức năng này cho bất kỳ đối số nào khác ngoài việc ()ném một ngoại lệ (ít nhất là theo như tôi có thể thấy trong các thử nghiệm của mình). Điều này có vẻ khác với đánh giá chậm trong Scheme và Python, vì biểu thức vẫn cần một đối số để được đánh giá. Vậy biểu thức lambda không có biến (như \() -> "s") có nghĩa gì trong Haskell và nó có thể hữu ích cho việc gì?

Ngoài ra, tôi sẽ tò mò muốn biết liệu các biểu thức lambda không tham số tương tự có tồn tại trong (một số loại) lambda-tính toán hay không.


"Áp dụng hàm này cho bất kỳ đối số nào khác ngoài ()ném ngoại lệ ..." Nó có khiến chương trình ném ngoại lệ hay khiến trình biên dịch phàn nàn rằng mã không gõ kiểm tra?
Fried Brice

Câu trả lời:


12

Chà, các câu trả lời khác bao gồm những gì \() -> "something"có nghĩa trong Haskell: một hàm unary lấy ()làm đối số.

  • Hàm không có đối số là gì? - Một giá trị. Trên thực tế, đôi khi có thể hữu ích khi nghĩ về các biến là các hàm vô giá trị để đánh giá giá trị của chúng. Các let-syntax cho một hàm không có đối số (mà không thực sự tồn tại) kết thúc đem lại cho bạn một biến ràng buộc:let x = 42 in ...

  • Có tính toán lambda có chức năng nullary? - Không. Mỗi hàm có chính xác một đối số. Tuy nhiên, đối số này có thể là một danh sách hoặc hàm có thể trả về một hàm khác có đối số tiếp theo. Haskell thích giải pháp sau, vì vậy đó a b cthực sự là hai lệnh gọi hàm ((a b) c). Để mô phỏng các hàm null, bạn phải chuyển một số giá trị giữ chỗ không sử dụng.


1
+1 cho "Hàm không có đối số là gì? - Giá trị" và bằng cách mở rộng chế độ xem hàm null. Tôi nghĩ thật hữu ích khi có thể xem các hàm và giá trị có liên quan như thế nào và một hàm có thể được xem như là một khái quát của một giá trị.
KChaloux

@amon: Vì vậy, một hàm nullary chỉ có ý nghĩa trong một ngôn ngữ như Scheme trong đó bạn có thể kích hoạt đánh giá một cách rõ ràng và trong đó các hàm (thủ tục) có thể hữu ích cả về giá trị và tác dụng phụ của chúng.
Giorgio

@Giorgio Lisp là một mã hóa trực tiếp của phép tính lambda với rất nhiều đường cú pháp, nhưng nếu là phép tính lambda, nó không thể có các hàm vô hiệu. Mọi thứ đều là một danh sách, vì vậy biểu thức ứng dụng hàm (f)về mặt khái niệm là một danh sách thành phần đơn, với giá trị đầu 'fvà đuôi không - vì vậy sử dụng conschúng ta có thể viết nó dưới dạng (cons 'f '()). Đuôi này là danh sách (về mặt khái niệm) được sử dụng làm đối số và không có vấn đề gì nếu không đại diện cho danh sách trống. Sự khác biệt giữa Lisp và Haskell là cái sau có cà ri ngầm, vì vậy biểu thức (f)có nghĩa là những thứ khác nhau
amon

2
Nhưng vâng, các hàm không có đối số thường không hữu ích trong Haskell, vì nó lười biếng và thuần túy. Các ngôn ngữ nghiêm ngặt có thể sử dụng các hàm không có đối số để mô phỏng sự lười biếng hoặc cho các cuộc gọi lại cho mục đích chung. Cho dù bạn phải cung cấp một đối số giả như ()(như trường hợp trong ML) hoặc liệu bạn không cố ý làm như vậy (như trường hợp trong các ngôn ngữ giống Lisps hoặc C) không thực sự quan trọng.
amon

9

Bạn đang hiểu sai ý ()nghĩa của Haskell. Đó không phải là thiếu một giá trị, nó đúng hơn là giá trị duy nhất của loại Đơn vị (chính loại được gọi bằng một bộ dấu ngoặc rỗng ()).

Vì lambdas có thể được xây dựng để sử dụng khớp mẫu, nên biểu thức lambda \() -> "s"nói rõ ràng là "tạo một hàm ẩn danh, mong đợi một đầu vào khớp với ()mẫu". Không có nhiều điểm để làm điều đó, nhưng nó chắc chắn được cho phép.

Bạn cũng có thể sử dụng khớp mẫu với lambdas theo những cách khác:

map (\(a, b) -> a + b) [(1,2), (3,4), (5,6)] -- uses pattern matching to destructured tuples

map (\(Name first _) -> first) [Name "John" "Smith", Name "Jane" "Doe"] -- matches a "Name" data type and its first field

map (\(x:_) -> x) [[1,2,3], [4,5,6]] -- matches the head of a list

Ngoại trừ đó Unitcũng được đánh vần ()trong Haskell.

@delnan Tôi thành thạo Scala hơn = P Tôi sẽ cập nhật câu trả lời để cụ thể hơn một chút về điểm đó.
KChaloux

Vì vậy, tôi tưởng tượng cơ chế khớp mẫu này là một tính năng Haskell không có trong lambda-compus (?)
Giorgio

@Giorgio Tôi không quá tin vào tính toán lambda cơ bản của mình, nhưng tôi hiểu là bạn đã đúng; khớp mẫu là một tính năng ngôn ngữ và không phải là tính toán của lambda.
KChaloux

1
@Giorgio Âm thanh về đúng. Tôi nghĩ bạn có thể xác định điều này về cách mỗi ngôn ngữ xử lý các tác dụng phụ. Hàm không tham số là A) giá trị không thay đổi hoặc B) một cái gì đó có tác dụng phụ. Haskell cố gắng tránh các tác dụng phụ nhiều hơn Python (hoặc Scheme ở mức độ thấp hơn), do đó, nó giả định rằng một hàm không tham số là một giá trị. Sẽ không có ý nghĩa gì khi cung cấp một hàm ẩn danh không có đầu vào và trả về cùng một giá trị (không giống như const, nó lấy bất kỳ đầu vào nào và biến tất cả thành cùng một giá trị).
KChaloux

1

() -> "s"là một hàm lambda có một đối số:

ghci> :t (\() -> "s")
(\() -> "s") :: () -> [Char]

(), tuple trống (đôi khi được gọi là Đơn vị), là một loại hoàn chỉnh trong Haskell. Nó chỉ có một thành viên (bỏ qua _|_*), cũng được viết là (). Nhìn vào định nghĩa của ():

data () = ()

Điều này có nghĩa là biểu mẫu ở phía bên trái của biểu thức lambda của bạn về mặt cú pháp là khớp mẫu, khớp với ()thành viên duy nhất của loại (). Chỉ có một cách hợp lệ để gọi nó (đó là cung cấp ()dưới dạng đối số) vì chỉ có một thành viên hợp lệ của loại ().

Một chức năng như vậy không hữu ích lắm trong Haskell, bởi vì theo mặc định các thuật ngữ được đánh giá một cách lười biếng, như bạn đã quan sát trong câu hỏi của mình.

Đối với một lời giải thích chi tiết hơn nhiều, xem câu trả lời này .

* _|_được gọi là "dưới cùng" hoặc "không xác định". Nó luôn kiểm tra kiểu, nhưng nó làm hỏng chương trình của bạn. Ví dụ kinh điển về cách lấy a _|_let x = x in x.


"Hàm như vậy không hữu ích lắm trong Haskell, vì theo mặc định, các thuật ngữ mặc định được đánh giá một cách lười biếng, như bạn đã quan sát trong câu hỏi của mình.": Không chỉ vậy: Tôi cần cung cấp một đối số, mà tôi không cần phải làm như trong Scheme .
Giorgio
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.