Haskell, Lisp và verbosity [đã đóng]


104

Đối với những người trong số các bạn đã có kinh nghiệm về cả Haskell và một số hương vị của Lisp, tôi tò mò rằng việc viết mã trong Haskell so với Lisp sẽ "dễ chịu" như thế nào (sử dụng một thuật ngữ kinh khủng).

Một số thông tin cơ bản: Tôi đang học Haskell, trước đó đã làm việc với Scheme và CL (và một chút đột phá vào Clojure). Theo truyền thống, bạn có thể coi tôi là người yêu thích các ngôn ngữ động vì tính ngắn gọn và nhanh chóng mà chúng cung cấp. Tôi nhanh chóng yêu thích các macro Lisp, vì nó mang lại cho tôi một cách khác để tránh dài dòng và viết sẵn.

Tôi thấy Haskell cực kỳ thú vị, vì nó giới thiệu cho tôi những cách viết mã mà tôi không biết là đã tồn tại. Nó chắc chắn có một số khía cạnh mà dường như chúng sẽ giúp đạt được sự nhanh nhẹn, chẳng hạn như dễ dàng viết các hàm từng phần. Tuy nhiên, tôi hơi lo lắng về việc mất macro Lisp (tôi cho rằng mình mất chúng; sự thật mà nói tôi có thể chưa tìm hiểu về chúng?) Và hệ thống nhập tĩnh.

Liệu bất kỳ ai đã thực hiện một lượng lớn mã hóa ở cả hai thế giới có thể nhận xét về cách trải nghiệm khác nhau, bạn thích cái nào hơn, và nếu nói sở thích là tình huống?

Câu trả lời:


69

Câu trả lời ngắn:

  • hầu hết mọi thứ bạn có thể làm với macro mà bạn có thể làm với một hàm bậc cao hơn (và tôi bao gồm các monads, mũi tên, v.v.), nhưng nó có thể đòi hỏi nhiều suy nghĩ hơn (nhưng chỉ là lần đầu tiên và thật thú vị và bạn sẽ lập trình viên tốt hơn cho nó), và
  • hệ thống tĩnh đủ chung chung để nó không bao giờ cản trở bạn và hơi ngạc nhiên là nó thực sự "hỗ trợ việc đạt được sự nhanh nhẹn" (như bạn đã nói) bởi vì khi chương trình của bạn biên dịch, bạn có thể gần như chắc chắn rằng điều đó đúng, vì vậy sự chắc chắn này cho phép bạn thử những thứ mà bạn có thể e ngại khi thử - lập trình có cảm giác "năng động" mặc dù nó không giống với Lisp.

[Lưu ý: Có một " Template Haskell " cho phép bạn viết macro giống như trong Lisp, nhưng nói đúng ra thì bạn không bao giờ cần nó.]


15
Từ Conor McBride, được trích dẫn bởi Don Stewart: 'Tôi thích nghĩ về các loại làm cong lực hấp dẫn của chúng ta, để hướng chúng ta cần di chuyển [để viết các chương trình chính xác] trở nên "xuống dốc". " Hệ thống loại giúp bạn dễ dàng viết các chương trình chính xác một cách đáng ngạc nhiên… hãy xem bài đăng này và các lượt chia sẻ lại của nó.
ShreevatsaR

3
Các hàm bậc cao không thể thay thế macro, và trên thực tế, CL có cả hai vì lý do nào đó. Sức mạnh thực sự của macro trong CL là chúng cho phép nhà phát triển giới thiệu các tính năng ngôn ngữ mới giúp diễn đạt tốt hơn giải pháp cho một vấn đề mà không cần phải đợi phiên bản mới của ngôn ngữ như trong Haskell hoặc Java. Ví dụ: nếu Haskell có sức mạnh này thì các tác giả Haskell không cần viết các phần mở rộng GHC, chúng có thể được các nhà phát triển tự triển khai dưới dạng macro bất cứ lúc nào.
mljrg 14/09/15

@mljrg Bạn có ví dụ cụ thể không? Xem các nhận xét về câu trả lời của Hibou57 bên dưới, nơi một ví dụ bị cáo buộc hóa ra là đáng ngờ. Tôi muốn biết ý bạn (ví dụ: mã Haskell có và không có macro).
ShreevatsaR

4
Lấy cà ri từ Haskell. Bạn có thể thực hiện nó với những gì sẽ còn lại trong Haskell không? Một ví dụ khác: giả sử Haskell không hỗ trợ khớp mẫu, bạn có thể tự thêm nó mà không cần các nhà phát triển của GHC hỗ trợ không? Trong CL, bạn có thể sử dụng macro để mở rộng ngôn ngữ theo ý muốn. Tôi cho rằng đó là lý do tại sao ngôn ngữ CL không thay đổi kể từ tiêu chuẩn của nó vào những năm 90, trong khi Haskell dường như có một loạt các phần mở rộng không bao giờ kết thúc trong GHC.
mljrg 14/09/15

64

Trước hết, đừng lo lắng về việc mất các tính năng cụ thể như nhập động. Vì bạn đã quen thuộc với Common Lisp, một ngôn ngữ được thiết kế rất tốt, tôi cho rằng bạn biết rằng một ngôn ngữ không thể bị giảm xuống thành bộ tính năng của nó. Tất cả là về một tổng thể mạch lạc, phải không?

Về mặt này, Haskell tỏa sáng chẳng kém gì Common Lisp. Các tính năng của nó kết hợp để cung cấp cho bạn một cách lập trình làm cho mã cực kỳ ngắn gọn và thanh lịch. Việc thiếu macro được giảm nhẹ phần nào bằng các khái niệm phức tạp hơn (nhưng tương tự, khó hiểu và khó sử dụng hơn) như monads và mũi tên. Hệ thống kiểu tĩnh tăng thêm sức mạnh của bạn thay vì cản trở bạn như trong hầu hết các ngôn ngữ hướng đối tượng.

Mặt khác, lập trình trong Haskell ít tương tác hơn nhiều so với Lisp, và lượng phản xạ khổng lồ hiện có trong các ngôn ngữ như Lisp không phù hợp với quan điểm tĩnh của thế giới mà Haskell đã giả định trước. Do đó, các bộ công cụ có sẵn cho bạn khá khác biệt giữa hai ngôn ngữ, nhưng khó có thể so sánh với nhau.

Cá nhân tôi thích cách lập trình Lisp nói chung, vì tôi cảm thấy nó phù hợp với cách tôi làm việc hơn. Tuy nhiên, điều này không có nghĩa là bạn cũng phải làm như vậy.


4
Bạn có thể giải thích thêm một chút về "lập trình trong Haskell ít tương tác hơn nhiều". GHCi không thực sự cung cấp mọi thứ bạn cần?
Johannes Gerer

3
@JohannesGerer: Tôi chưa thử, nhưng theo như tôi đã đọc, GHCi không phải là một trình bao vào hình ảnh đang chạy, nơi bạn có thể xác định lại và mở rộng các phần tùy ý của toàn bộ chương trình khi nó đang chạy. Ngoài ra, cú pháp Haskell làm cho việc sao chép các đoạn chương trình giữa repl và trình soạn thảo theo chương trình khó hơn nhiều.
Svante

13

Ít cần lập trình siêu hình trong Haskell hơn trong Common Lisp vì nhiều thứ có thể được cấu trúc xung quanh các monads và cú pháp được thêm vào làm cho các DSL được nhúng trông ít giống cây hơn, nhưng luôn có Template Haskell, như ShreevatsaR đã đề cập và thậm chí là Liskell (Ngữ nghĩa Haskell + Lisp cú pháp) nếu bạn thích dấu ngoặc đơn.


1
liên kết Liskell đã chết, nhưng ngày nay vẫn có Hackett .
Will Ness

10

Liên quan đến macro, đây là một trang nói về nó: Xin chào Haskell, Tạm biệt Lisp . Nó giải thích một quan điểm mà macro không cần thiết trong Haskell. Nó đi kèm với một ví dụ ngắn để so sánh.

Trường hợp ví dụ trong đó macro LISP được yêu cầu để tránh đánh giá cả hai đối số:

(defmacro doif (x y) `(if ,x ,y))

Ví dụ trường hợp Haskell không đánh giá một cách có hệ thống cả hai đối số mà không cần bất kỳ điều gì như định nghĩa macro:

doif x y = if x then (Just y) else Nothing

Và Voila


17
Đó là một quan niệm sai lầm phổ biến. Có, trong Haskell lười biếng có nghĩa là bạn không cần macro khi muốn tránh đánh giá một số phần của biểu thức, nhưng đó chỉ là tập hợp con nhỏ nhất trong tất cả các cách sử dụng macro. Google cho "The Swine Before Perl" cho một cuộc nói chuyện thể hiện một macro không thể thực hiện được với sự lười biếng. Ngoài ra, nếu bạn làm muốn có một số bit để được chặt chẽ, thì bạn không thể làm điều đó như một chức năng - phản ánh một thực tế rằng Đề án của delaykhông thể là một chức năng.
Eli Barzilay

8
@Eli Barzilay: Tôi không thấy ví dụ này thuyết phục lắm. Dưới đây là một hoàn thành, đơn giản dịch Haskell của trượt 40: pastebin.com/8rFYwTrE
Reid Barton

5
@Eli Barzilay: Tôi không hiểu câu trả lời của bạn chút nào. accept (E) DSL. Các acceptchức năng là tương tự của vĩ mô nêu trên các trang trước và định nghĩa về vlà chính xác song song với định nghĩa của vtrong Đề án trên slide 40. Các Haskell và Đề án chức năng tính toán điều tương tự với chiến lược đánh giá tương tự. Tốt nhất, macro cho phép bạn hiển thị nhiều hơn cấu trúc chương trình của bạn cho trình tối ưu hóa. Bạn khó có thể khẳng định đây là một ví dụ khi macro tăng sức mạnh biểu đạt của ngôn ngữ theo cách không được sao chép bởi đánh giá lười biếng.
Reid Barton

5
@Eli Barzilay: Trong một sơ đồ lười biếng giả định, bạn có thể viết như sau: pastebin.com/TN3F8VVE Yêu cầu chung của tôi là macro này mua cho bạn rất ít: cú pháp hơi khác và thời gian dễ dàng hơn cho trình tối ưu hóa (nhưng sẽ không thành vấn đề một "trình biên dịch đủ thông minh"). Đổi lại, bạn đã tự mắc kẹt mình vào một ngôn ngữ khó diễn đạt; làm thế nào để bạn xác định một tự động hóa khớp với bất kỳ chữ cái nào mà không liệt kê tất cả chúng? Ngoài ra, tôi không biết bạn muốn nói gì khi "sử dụng nó trong tất cả các danh sách phụ" hoặc "yêu cầu sử dụng ở đâu với phạm vi riêng của nó".
Reid Barton

15
Được tôi từ bỏ. Rõ ràng định nghĩa của bạn của DSL là "biện minh cho khả macro" và như vậy lười biếng Scheme ví dụ của tôi không phải là một DSL, mặc dù là cú pháp đẳng cấu với bản gốc ( automatontrở thành letrec, :trở thành accept, ->trở thành không có gì trong phiên bản này). Bất cứ điều gì.
Reid Barton

9

Tôi là một lập trình viên Lisp chung.

Đã thử Haskell một thời gian trước, điểm mấu chốt của cá nhân tôi là gắn bó với CL.

Lý do:

  • nhập động (xem Nhập động so với Nhập tĩnh - Phân tích dựa trên mẫu của Pascal Costanza )
  • các đối số tùy chọn và từ khóa
  • cú pháp danh sách đồng nhất thống nhất với macro
  • cú pháp tiền tố (không cần nhớ quy tắc ưu tiên)
  • không tinh khiết và do đó phù hợp hơn để tạo mẫu nhanh
  • hệ thống đối tượng mạnh mẽ với giao thức siêu đối tượng
  • tiêu chuẩn trưởng thành
  • nhiều loại trình biên dịch

Tất nhiên, Haskell có những ưu điểm riêng và thực hiện một số việc theo một cách khác về cơ bản, nhưng nó không cắt giảm nó về lâu dài đối với tôi.


Này, bạn có tình cờ thấy tiêu đề của tờ báo Costanza mà bạn đã liên kết không? Có vẻ như tệp đó đã được di chuyển.
michiakig

2
Lưu ý rằng haskell cũng hỗ trợ cú pháp tiền tố nhưng tôi muốn nói rằng đơn nguyên >> = sẽ rất tệ khi sử dụng nó. Ngoài ra tôi không đồng ý với tạp chất là một phước lành: P
thay thế

2
Tôi thích ghi chú bên lề này: Chúng tôi vẫn chưa thu thập dữ liệu thực nghiệm liệu vấn đề này có gây ra sự cố nghiêm trọng trong các chương trình trong thế giới thực hay không.
Jerome Baum

22
Không có ví dụ nào trong bài báo đó (Pascal Costanza, Nhập động so với Nhập tĩnh - Phân tích dựa trên mẫu ) áp dụng cho Haskell. Tất cả chúng đều dành riêng cho Java (hoặc chính xác hơn, cụ thể cho "lập trình hướng đối tượng" ) và tôi không thể thấy bất kỳ vấn đề nào trong số đó xuất hiện trong Haskell. Tương tự, tất cả các đối số khác của bạn đều gây tranh cãi: người ta cũng có thể nói Haskell là "thuần túy và do đó phù hợp hơn cho việc tạo mẫu nhanh", cú pháp tiền tố đó không bắt buộc, rằng nó không có nhiều trình biên dịch làm những việc khác nhau , v.v.
ShreevatsaR

11
Tờ báo đó thực sự gần như hoàn toàn không liên quan đến Haskell. " dilbert = dogbert.hire(dilbert);" ?? Tôi nghi ngờ nhiều lập trình viên Haskell thậm chí có thể đọc nó mà không bị giật một chút.
bùng binh bên trái

6

Trong Haskell, bạn có thể xác định một hàm if, điều này là không thể trong LISP. Điều này có thể xảy ra vì sự lười biếng, điều này cho phép nhiều mô-đun hơn trong các chương trình. Bài báo kinh điển này: Tại sao FP lại quan trọng của John Hughes, giải thích cách sự lười biếng nâng cao khả năng sáng tác.


5
Đề án (một trong hai phương ngữ chính của LISP) thực sự có đánh giá lười biếng, mặc dù nó không mặc định như trong Haskell.
Randy Voet 09/09/09

7
(defmacro doif (xy) `(nếu, x, y))
Joshua Má

4
Macro không giống với một hàm - macro không hoạt động tốt với các hàm bậc cao hơn như fold, trong khi các hàm không nghiêm ngặt thì làm .
Tikhon Jelvis

5

Có những điều thực sự thú vị mà bạn có thể đạt được trong Lisp với các macro rườm rà (nếu có thể) trong Haskell. Lấy ví dụ macro `` ghi nhớ '' (xem Chương 9 của PAIP của Peter Norvig). Với nó, bạn có thể xác định một hàm, nói foo, và sau đó chỉ cần đánh giá (memoize 'foo), thay thế định nghĩa toàn cục của foo bằng một phiên bản đã ghi nhớ. Bạn có thể đạt được hiệu quả tương tự trong Haskell với các chức năng bậc cao hơn không?


3
Không hoàn toàn (AFAIK), nhưng bạn có thể làm điều gì đó tương tự bằng cách sửa đổi hàm (giả sử nó đệ quy) để hàm gọi đệ quy dưới dạng tham số (!) Thay vì chỉ đơn giản gọi chính nó bằng tên: haskell.org/haskellwiki/Memoization
j_random_hacker

6
Bạn có thể thêm foo vào cấu trúc dữ liệu lười biếng, nơi giá trị sẽ được lưu trữ sau khi được tính toán. Điều này sẽ có hiệu quả như nhau.
Theo Belaire

Mọi thứ trong Haskell đều được ghi nhớ và có thể được nội tuyến khi cần theo mặc định bởi trình biên dịch Haskell.
aoeu256

4

Khi tôi tiếp tục hành trình học Haskell của mình, có vẻ như một điều giúp "thay thế" macro là khả năng xác định các toán tử infix của riêng bạn và tùy chỉnh mức độ ưu tiên và tính liên kết của chúng. Kinda phức tạp, nhưng là một hệ thống thú vị!

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.