Ngôn ngữ lập trình chức năng thuần túy nhất? [đóng cửa]


20

Tôi quan tâm đến việc học lập trình chức năng tốt hơn. Để làm như vậy, có vẻ hiển nhiên rằng tôi nên ép bản thân sử dụng ngôn ngữ lập trình chức năng thuần túy nhất có thể. Do đó, tôi ở đây yêu cầu, ít nhiều, cho một thứ tự các ngôn ngữ lập trình chức năng theo độ tinh khiết của chúng.

Đối với tôi, có vẻ thực tế hơn khi học Lisp hoặc Clojure (hoặc Scheme, hoặc Scala, v.v.), nhưng đối với những gì tôi đã nghe gần đây, Haskell sẽ rất khó để đánh bại việc dạy các nguyên tắc lập trình chức năng cho ai đó. Tôi chưa chắc về điều này, vì vậy tôi đang hỏi bạn: ngôn ngữ lập trình chức năng thuần túy nhất là gì? Một đơn đặt hàng sẽ là tuyệt vời nếu một số người đang cạnh tranh cho danh hiệu tuyệt vời của ngôn ngữ lập trình chức năng thuần túy nhất.


2
Tôi đã học được Miranda tại trường đại học vì vậy tôi biassed, nhưng tôi sẽ đề nghị Haskel cho bất cứ ai muốn đắm mình trong một ngôn ngữ chức năng mà không có sự phiền nhiễu của tạp chất . * 8 ')
Đánh dấu gian hàng

2
Sau khi bạn học xong lập trình chức năng, bạn cũng nên học lập trình gõ tĩnh với một hệ thống kiểu biểu cảm. Trong danh mục kết hợp (cả chức năng và đánh máy), tôi đề xuất: Coq> Haskell> OCaml> Scala> khác. Có một số lựa chọn thay thế ít phổ biến hơn phù hợp giữa Coq và Haskell (như Epigram và Agda). Haskell nhớ hệ thống mô-đun biểu cảm của OCaml.
lukstafi

Câu trả lời:


28

Không có thang đo để đánh giá mức độ tinh khiết của các ngôn ngữ chức năng. Nếu ngôn ngữ cho phép tác dụng phụ thì nó không trong sạch, nếu không thì nó thuần túy. Theo định nghĩa này, Haskell, Mercury, Clean, vv là các ngôn ngữ chức năng thuần túy; trong khi Scala, Clojure, F #, OCaml v.v ... là những thứ không trong sạch.

EDIT: Có lẽ tôi nên đặt câu này là "nếu ngôn ngữ không cho phép tác dụng phụ mà không cho hệ thống loại biết , thì đó là thuần túy. Nếu không thì nó không trong sạch."


6
Không hoàn toàn: Haskell không cho phép tác dụng phụ ( IOđơn nguyên). Đó chỉ là mã gây ra tác dụng phụ được đánh dấu rõ ràng như vậy. Tôi không nghĩ việc nói về các ngôn ngữ thuần túy / không tinh khiết là hữu ích (Haskell cho phép bạn lập trình một cách bắt buộc! Gasp!) Nhưng vẫn có thể hữu ích khi nói về các đoạn mã (chức năng, mô-đun, chương trình, bất cứ điều gì) / không tinh khiết.
Frank Shearar

8
@Frank: Có, Haskell không cho phép tác dụng phụ, nhưng nó không chỉ đánh dấu mã gây hiệu ứng phụ. Nó cũng duy trì tính minh bạch tham chiếu ngay cả khi sử dụng mã gây ra tác dụng phụ và đó là những gì nó làm cho thuần túy - ít nhất là theo định nghĩa về độ tinh khiết của nhiều người. Tất nhiên, điều đó không phù hợp với định nghĩa của "không có tác dụng phụ".
sepp2k

4
@Frank Shearar nhưng thật hữu ích khi nói theo cách như vậy bởi vì IOđơn nguyên thuần túy. Đó là thư viện thời gian chạy không phải, không phải mã của bạn, vì mainchức năng của bạn về cơ bản là một biến áp trạng thái rất lớn. (Một cái gì đó giống như main :: World -> Worldđằng sau hậu trường)
thay thế vào

1
Ngôn ngữ của tôi có tính minh bạch tham chiếu, quá. Bạn chỉ cần viết program = "some C code", và sau đó môi trường thời gian chạy xử lý mã C. :-)
Daniel Lubarov

3
Khi in ra màn hình là một hiệu ứng phụ thực sự thuần túy các chương trình chức năng khá nhàm chán.

15

Vì học tập là mục tiêu của bạn và không phải viết chương trình mỗi lần, bạn không thể có được bất kỳ điều gì tinh khiết hơn Lambda Tính .

Lambda Tính đã có mặt trước khi máy tính được phát minh. Phải mất một số nhà logic học lành nghề làm việc trên nó để tìm ra cách thực hiện phép trừ (trong một thời gian, người ta cho rằng chỉ có phép cộng và phép nhân là có thể).

Tìm hiểu làm thế nào booleans và số và ifcó thể được phát minh từ dường như không có gì sẽ không thêm khí vào bể của bạn, nhưng nó sẽ làm cho bể của bạn lớn hơn nhiều.


Cứ cho là, có lẽ không có gì tinh khiết hơn thế, nhưng chúng ta vượt qua rào cản toán học quá nhiều. Tôi vẫn muốn thực hành lập trình và học một ngôn ngữ mới trong khi tiếp thu các nguyên tắc chức năng. Mặc dù vậy, tôi đồng ý rằng việc nghiên cứu tính toán lambda có thể chỉ là để hiểu rõ hơn về nền tảng của mô hình chức năng (và có một bể lớn hơn).
Joanis

2
Viết một bằng chứng là viết một chương trình và viết một chương trình là viết một bằng chứng. Khi bạn tìm hiểu về đẳng cấu Curry Curry Howard, bạn sẽ nhận ra mình đã vượt qua rào cản toán học mười nghìn dòng trước.
Macneil

1
Không có đại diện đơn giản của -1.
Daniel Lubarov

2
@Mason Wheeler:-compus một mình không có số hoặc thực sự là bất kỳ loại dữ liệu nào ngoài trừu tượng lambda. Trừ khi có quy định khác, bất kỳ ai nói về các số trong-compus có thể có nghĩa là mã hóa của Church cho các số tự nhiên , trong đó một số n được biểu thị bằng thành phần hàm n lần. Điều đó nói rằng, tôi nghi ngờ rằng đó là một cuộc đấu tranh để tìm ra phép trừ một khi ai đó thực sự cố gắng; Tôi đã tự mình giải quyết nó vào một buổi chiều chỉ đưa ra định nghĩa về phép cộng và kiến ​​thức rằng phép trừ là có thể.
CA McCann

1
Phép trừ sử dụng chữ số Church dễ dàng một khi bạn đã phân tách theo trường hợp. Xây dựng toàn bộ tính toán (xuất phát) để hỗ trợ các số âm, thay vì nhiều công việc hơn.
Donal Fellows

6

Các ngôn ngữ không tinh khiết không thực sự khác biệt về nguyên tắc với các ngôn ngữ mệnh lệnh quen thuộc hơn, đặc biệt là bây giờ nhiều thủ thuật chức năng đã được sao chép. Điều khác biệt là phong cách - cách bạn giải quyết vấn đề.

Cho dù bạn coi Haskell là thuần túy, hay coi đơn vị IO là tạp chất, phong cách Haskell là một hình thức cực đoan của phong cách này và rất đáng để học hỏi.

Đơn nguyên Haskell IO có nguồn gốc từ lý thuyết toán học của (tất nhiên) các đơn nguyên. Tuy nhiên, đối với các lập trình viên cấp bách, tôi nghĩ rằng một cách lạc hậu để đến với các đơn nguyên có ý nghĩa hơn.

Giai đoạn một - một ngôn ngữ chức năng thuần túy có thể dễ dàng trả về một giá trị chuỗi lớn như kết quả của nó. Chuỗi lớn này có thể là mã nguồn của một chương trình bắt buộc, xuất phát theo cách chức năng thuần túy từ một số tham số xác định yêu cầu. Sau đó, bạn có thể xây dựng trình biên dịch "cấp cao hơn" để chạy trình tạo mã của mình, sau đó tự động cung cấp mã đã tạo vào trình biên dịch ngôn ngữ bắt buộc.

Giai đoạn hai - thay vì tạo mã nguồn văn bản, bạn tạo một cây cú pháp trừu tượng được gõ mạnh. Trình biên dịch ngôn ngữ bắt buộc của bạn được hấp thụ vào trình biên dịch "cấp cao hơn" của bạn và chấp nhận AST trực tiếp làm mã nguồn. Điều này gần với những gì Haskell làm.

Điều này vẫn còn vụng về, mặc dù. Ví dụ, bạn có hai loại chức năng riêng biệt - những loại được đánh giá trong giai đoạn tạo mã và những loại được thực thi khi chương trình được tạo được chạy. Nó hơi giống sự khác biệt giữa các chức năng và mẫu trong C ++.

Vì vậy, đối với giai đoạn 3, làm cho hai cái giống nhau - cùng một hàm với cùng một cú pháp có thể được đánh giá một phần trong quá trình "tạo mã", hoặc được đánh giá đầy đủ hoặc hoàn toàn không được đánh giá. Hơn nữa, loại bỏ tất cả các vòng lặp xây dựng các nút AST có lợi cho đệ quy. Trong thực tế, loại bỏ ý tưởng về các nút AST là một loại dữ liệu đặc biệt hoàn toàn - không có các nút AST "giá trị theo nghĩa đen", chỉ có các giá trị, v.v.

Đây là khá nhiều những gì đơn vị IO làm - toán tử liên kết là một cách để soạn "hành động" để tạo thành các chương trình. Nó không có gì đặc biệt - chỉ là một chức năng. Nhiều biểu thức và hàm có thể được đánh giá trong quá trình "tạo mã", nhưng các biểu thức phụ thuộc vào tác dụng phụ I / O phải được đánh giá bị trì hoãn cho đến thời gian chạy - không phải bởi bất kỳ quy tắc đặc biệt nào, mà là hậu quả tự nhiên của sự phụ thuộc dữ liệu trong biểu thức.

Các đơn nguyên nói chung chỉ là khái quát hóa - chúng có cùng giao diện, nhưng thực hiện các hoạt động trừu tượng khác nhau, vì vậy thay vì đánh giá một mô tả về mã mệnh lệnh mà chúng đánh giá sang một thứ khác thay thế. Có cùng một giao diện có nghĩa là có một số điều bạn có thể làm với các đơn nguyên mà không quan tâm đến đơn nguyên nào, hóa ra là hữu ích.

Mô tả này chắc chắn sẽ làm cho những người theo chủ nghĩa thuần túy bùng nổ, nhưng với tôi nó giải thích một số lý do thực sự tại sao Haskell lại thú vị. Nó xóa nhòa ranh giới giữa lập trình và siêu lập trình, và sử dụng các công cụ lập trình chức năng để phát minh lại lập trình mệnh lệnh mà không cần cú pháp đặc biệt.

Một lời chỉ trích mà tôi có về các mẫu C ++ là chúng là một loại ngôn ngữ chức năng thuần túy bị hỏng trong một ngôn ngữ bắt buộc - để đánh giá cùng một chức năng cơ bản tại thời gian biên dịch thay vì thời gian chạy bạn phải triển khai lại bằng cách sử dụng một kiểu hoàn toàn khác mã hóa. Trong Haskell, trong khi tạp chất phải được dán nhãn như vậy trong loại của nó, chức năng chính xác tương tự có thể được đánh giá cả theo nghĩa lập trình meta và theo nghĩa không lập trình siêu thời gian chạy trong cùng một chương trình - không có đường cứng giữa lập trình và siêu lập trình.

Điều đó nói rằng, có một số điều siêu lập trình mà Haskell tiêu chuẩn không thể làm được, về cơ bản vì các loại (và có thể một vài thứ khác) không phải là giá trị hạng nhất. Có những biến thể ngôn ngữ cố gắng giải quyết điều này, mặc dù.

Rất nhiều điều tôi đã nói về Haskell có thể được áp dụng trong các ngôn ngữ chức năng không tinh khiết - và đôi khi cả các ngôn ngữ bắt buộc. Haskell khác biệt bởi vì bạn không có lựa chọn nào khác ngoài cách tiếp cận này - về cơ bản nó buộc bạn phải học phong cách làm việc này. Bạn có thể "viết C bằng ML", nhưng bạn không thể "viết C bằng Haskell" - ít nhất là không phải không học những gì đang diễn ra dưới mui xe.


Cảm ơn bạn! Tôi đã tự hỏi nếu tính tổng quát của các mẫu C ++ có thể được hợp nhất thành các chức năng. Có vẻ như Haskell thực hiện điều này, nhưng phần quan trọng là liệu điều này có thể được thực hiện bằng ngôn ngữ được biên dịch hay không , để cùng một công cụ tạo các hàm chung từ các mẫu trong thời gian biên dịch, cũng có thể đánh giá chúng trong thời gian chạy ...
Milind R

5

Cá nhân tôi phân loại các ngôn ngữ theo ba cấp độ tinh khiết chức năng:

  • Các ngôn ngữ chức năng thuần túy - tức là những ngôn ngữ coi toàn bộ chương trình của bạn là một hàm thuần túy và xử lý khả năng biến đổi chỉ thông qua tương tác với thời gian chạy - Haskell có lẽ là ví dụ điển hình

  • Ngôn ngữ chức năng không tinh khiết - tức là những ngôn ngữ nhấn mạnh một phong cách chức năng nhưng cho phép tác dụng phụ. Clojure rõ ràng thuộc loại này (nó cho phép đột biến theo kiểu được kiểm soát như là một phần của khung STM của nó), cũng là OCaml hoặc F #

  • Các ngôn ngữ đa mô hình - đây không phải là ngôn ngữ chức năng trước hết nhưng có thể hỗ trợ kiểu chức năng bằng cách sử dụng các hàm hạng nhất, v.v. Scala là một ví dụ điển hình ở đây, tôi cũng đưa Common Lisp vào danh mục này và thậm chí bạn có thể bao gồm các ngôn ngữ như JavaScript .

Trong tình huống của bạn, tôi khuyên bạn nên học Haskell trước, sau đó là Clojure. Đây là những gì tôi đã làm và nó làm việc rất tốt cho tôi! Haskell rất đẹp và dạy cho bạn những nguyên tắc chức năng thuần túy nhất, Clojure thực dụng hơn nhiều và giúp bạn hoàn thành công việc trong khi vẫn còn rất nhiều chức năng.

Tôi thực sự không coi loại thứ ba là ngôn ngữ chức năng (mặc dù sau khi học Haskell và Clojure, tôi thường thấy mình tận dụng các kỹ thuật chức năng khi sử dụng chúng!)


Trong giới hạn rất nghiêm ngặt , thậm chí C có khả năng chức năng. (Hạn chế chính là về tổng hợp chức năng thời gian chạy, điều này thực sự khủng khiếp ở C, đến mức hầu hết mọi người đều cho rằng không thể thực hiện được.)
Donal Fellows

2
@Donal Fellows: Trong giới hạn khi độ silliness đến vô cùng, mọi ngôn ngữ Turing-perfect đều hoạt động: người ta chỉ cần thực hiện một trình thông dịch Lisp trong ngôn ngữ, và sau đó sử dụng ngôn ngữ đó. :]
CA McCann

Các mô hình là vô nghĩa nếu bạn đưa ra quan điểm rằng mọi thứ đều hoàn chỉnh và do đó có thể mô phỏng mọi thứ khác. Khi bạn nghĩ về các mô hình, bạn phải tập trung vào những gì ngôn ngữ tạo nên thành ngữ và những gì nó làm nản lòng / ngăn chặn. Để được tính là một ngôn ngữ chức năng theo quan điểm của tôi, đột biến và tác dụng phụ phải không phổ biến (đối với FP không tinh khiết) hoặc bị cấm (đối với FP thuần túy). Điều đó có nghĩa là C không hoạt động. Không phải là ngôn ngữ đa mô hình như Scala.
mikera

@mikera: Tôi thấy câu trả lời của bạn rất thú vị: nếu Scala không hoạt động mà chỉ đa mô hình, làm thế nào để bạn đánh giá các tính năng lập trình chức năng trong C ++ 11, C # hoặc thậm chí Java (giới thiệu theo kế hoạch của lambdas)?
Giorgio

@Giorgio: Tôi nghĩ các tính năng chức năng trong tất cả các ngôn ngữ bạn đề cập có lẽ là một ý tưởng hay, họ chỉ không biến chúng thành "ngôn ngữ chức năng". Hoặc để nhìn nó từ hướng ngược lại, thực tế bạn có thể thực hiện IO đơn điệu theo kiểu bắt buộc không biến Haskell thành ngôn ngữ bắt buộc :-). Các ngôn ngữ đa mô hình về cơ bản là kết quả khi bạn kết hợp các tính năng từ nhiều mô hình khác nhau lại với nhau và không đặt bất kỳ phong cách cụ thể nào lên trên hết. IMHO C ++, C # và Java đều đang hướng tới việc đa mô hình, nhưng vẫn chưa hoàn toàn vì OOP dựa trên lớp vẫn chiếm ưu thế.
mikera

3

Nếu một ngôn ngữ chức năng thuần túy là như vậy, chỉ có các chức năng thuần túy (các thói quen không có tác dụng phụ), thì nó hơi vô nghĩa, vì nó không thể đọc đầu vào hoặc đầu ra ghi;)

Bởi vì đây là thực sự cho việc học tập, tôi nghĩ cô lập không nhất thiết phải là những con đường để đi. Lập trình chức năng là một mô hình. Điều quan trọng là phải hiểu mô hình nào phù hợp cho vấn đề nào và quan trọng hơn là làm thế nào chúng có thể được kết hợp tốt nhất.

Tôi sẽ nói ngay bây giờ: thời trang lập trình là ngu ngốc và phản tác dụng. Điều duy nhất quan trọng là chương trình của bạn ngắn gọn, dễ viết, dễ bảo trì và hoạt động chính xác. Làm thế nào bạn đạt được điều này không có gì để làm với mốt lập trình. - Richard Jones

Ngoài ra, nếu bạn đang tìm kiếm "sự thuần khiết", bạn có thể muốn có một cái nhìn về Pure . Tuy nhiên, lưu ý rằng việc gọi các thói quen C cực kỳ dễ dàng khiến nó không hoạt động (nhưng cũng rất mạnh mẽ).


3

Không phải là một câu trả lời hoàn toàn nghiêm túc, nhưng Unlambda phải là một ứng cử viên. Bạn không thể nhận được bất kỳ "chức năng thuần túy" nào hơn các tổ hợp SKI.


4
Điên cuồng! Dị giáo! Những loại ngôn ngữ thuần túy có sự vô lý như tổ hợp với tác dụng phụ? Không không. Những gì bạn thực sự muốn ở đây là Lazy K .
CA McCann

2

Erlang, Haskell, Scheme, Scala, Clojure và F #

Câu hỏi này có lẽ tốt nhất bạn sẽ giúp bạn trong việc tìm kiếm của bạn là tốt .


Cảm ơn, câu hỏi thú vị thực sự. Tôi đã bắt đầu với PLT-Scheme / Vợt, nhưng tôi chưa xem SICP ... Real World Haskell trông cũng khá thú vị đối với tôi.
Joanis

Lược đồ khuyến khích một phong cách chức năng, nhưng nó có set!(trong số những thứ khác) ...
Daniel Lubarov

Tôi đề nghị OCaml, vì đó là sở thích của tôi. Và nó có một hệ thống mô-đun, mà Haskell và F # bỏ lỡ.
lukstafi

@lukstafi có thể haskell đã thay đổi trong 3 năm qua (tôi biết nó phát triển như điên), nhưng chắc chắn có các mô-đun trong haskell.
sara

@kai Theo một hệ thống mô-đun, tôi có nghĩa là các mô-đun được tham số hóa bởi các mô-đun khác (một "phép tính lambda" của các mô-đun).
lukstafi
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.