Có gì ồn ào về Haskell? [đóng cửa]


109

Tôi biết một vài lập trình viên luôn nói về Haskell khi họ ở trong số họ, và ở đây mọi người dường như yêu thích ngôn ngữ đó. Giỏi Haskell có vẻ giống như dấu hiệu của một lập trình viên thiên tài.

Ai đó có thể đưa ra một vài ví dụ về Haskell cho thấy tại sao nó lại thanh lịch / cao cấp như vậy không?

Câu trả lời:


134

Cách nó được thuyết trình với tôi, và điều tôi nghĩ là đúng sau khi học trên Haskell trong một tháng nay, thực tế là lập trình chức năng xoay chuyển bộ não của bạn theo những cách thú vị: nó buộc bạn phải suy nghĩ về những vấn đề quen thuộc theo những cách khác nhau : thay vì các vòng lặp, hãy suy nghĩ trong bản đồ và các nếp gấp và bộ lọc, v.v. Nói chung, nếu bạn có nhiều quan điểm về một vấn đề, điều đó giúp bạn có khả năng lập luận tốt hơn về vấn đề này và chuyển đổi quan điểm nếu cần.

Điều thực sự gọn gàng khác về Haskell là hệ thống loại của nó. Nó được nhập đúng cách, nhưng công cụ suy luận kiểu khiến nó giống như một chương trình Python cho bạn biết một cách kỳ diệu khi bạn đã thực hiện một lỗi ngu ngốc liên quan đến kiểu. Các thông báo lỗi của Haskell về vấn đề này hơi thiếu, nhưng khi bạn làm quen với ngôn ngữ hơn, bạn sẽ tự nói với mình: đây là những gì phải nhập!


47
Cần lưu ý rằng thông báo lỗi của Haskell không thiếu, ghc thì có. Tiêu chuẩn Haskell không chỉ định cách thông báo lỗi được thực hiện.
PyRulez

Đối với những người thích ứng xử như tôi, GHC là viết tắt của Glasgow Haskell Compiler. vi.wikipedia.org/wiki/Glasgow_Haskell_Compiler
Lorem Ipsum

137

Đây là những ví dụ mà thuyết phục tôi để tìm hiểu Haskell (và cậu bé là tôi vui vì tôi đã làm).

-- program to copy a file --
import System.Environment

main = do
         --read command-line arguments
         [file1, file2] <- getArgs

         --copy file contents
         str <- readFile file1
         writeFile file2 str

OK, đây là một chương trình ngắn, dễ đọc. Theo nghĩa đó, nó tốt hơn một chương trình C. Nhưng điều này rất khác với (giả sử) một chương trình Python có cấu trúc rất giống nhau như thế nào?

Câu trả lời là lười đánh giá. Trong hầu hết các ngôn ngữ (thậm chí một số ngôn ngữ chức năng), một chương trình có cấu trúc như chương trình ở trên sẽ dẫn đến việc toàn bộ tệp được tải vào bộ nhớ, và sau đó được viết lại dưới một tên mới.

Haskell là "lười biếng". Nó không tính toán mọi thứ cho đến khi nó cần và mở rộng không tính toán những thứ nó không bao giờ cần. Ví dụ, nếu bạn xóa writeFiledòng, Haskell sẽ không bận tâm đọc bất cứ thứ gì từ tệp ngay từ đầu.

Như vậy, Haskell nhận ra rằng writeFilephụ thuộc vào readFile, và do đó có thể tối ưu hóa đường dẫn dữ liệu này.

Mặc dù kết quả phụ thuộc vào trình biên dịch, nhưng điều thường xảy ra khi bạn chạy chương trình trên là điều này: chương trình đọc một khối (ví dụ 8KB) của tệp đầu tiên, sau đó ghi nó vào tệp thứ hai, sau đó đọc một khối khác từ tệp đầu tiên và ghi nó vào tệp thứ hai, v.v. (Hãy thử chạy stracetrên nó!)

... trông rất giống những gì việc triển khai C hiệu quả của một bản sao tệp sẽ làm.

Vì vậy, Haskell cho phép bạn viết các chương trình nhỏ gọn, có thể đọc được - thường mà không làm giảm nhiều hiệu suất.

Một điều nữa tôi phải nói thêm là Haskell chỉ đơn giản là gây khó khăn cho việc viết các chương trình lỗi. Hệ thống loại tuyệt vời, thiếu tác dụng phụ và tất nhiên là tính nhỏ gọn của mã Haskell giúp giảm lỗi vì ít nhất ba lý do:

  1. Thiết kế chương trình tốt hơn. Độ phức tạp giảm dẫn đến ít lỗi logic hơn.

  2. Mã nhỏ gọn. Ít dòng hơn cho lỗi tồn tại trên.

  3. Biên dịch lỗi. Rất nhiều lỗi chỉ là Haskell không hợp lệ .

Haskell không dành cho tất cả mọi người. Nhưng mọi người nên thử.


Chính xác thì bạn sẽ thay đổi hằng số 8KB (hoặc bất cứ điều gì)? Bởi vì tôi muốn đặt cược một thực hiện Haskell sẽ chậm hơn so với một phiên bản C khác, đặc biệt là không tìm nạp trước ...
user541686

1
@Mehrdad Bạn có thể thay đổi kích thước bộ đệm bằng hSetBuffering handle (BlockBuffering (Just bufferSize)).
David

3
Thật ngạc nhiên khi câu trả lời này có tới 116 lượt ủng hộ nhưng điều gì trong đó chỉ là sai. Chương trình này sẽ đọc toàn bộ tệp, trừ khi bạn sử dụng lazy Bytestrings (mà bạn có thể làm với Data.Bytestring.Lazy.readFile), không liên quan gì đến việc Haskell là một ngôn ngữ lười biếng (không nghiêm ngặt). Các đơn nguyên được sắp xếp theo trình tự - điều này có nghĩa gần như "tất cả các tác dụng phụ được thực hiện khi bạn lấy ra kết quả". Đối với phép thuật "lazy Bytestring": điều này rất nguy hiểm và bạn có thể thực hiện với cú pháp tương tự hoặc đơn giản hơn trong hầu hết các ngôn ngữ khác.
Jo So

14
Tiêu chuẩn cũ nhàm chán readFilecũng thực hiện IO lười biếng theo cách tương tự Data.ByteString.Lazy.readFile. Vì vậy, câu trả lời là không sai, và nó không chỉ đơn thuần là tối ưu hóa trình biên dịch. Thật vậy, đây là một phần của thông số kỹ thuật cho Haskell : " readFileHàm đọc một tệp và trả về nội dung của tệp dưới dạng chuỗi. Tệp được đọc một cách lười biếng, theo yêu cầu, như với getContents."
Daniel Wagner

1
Tôi nghĩ các câu trả lời khác chỉ ra những điều đặc biệt hơn về Haskell. Nhiều ngôn ngữ / môi trường có suối, bạn có thể làm điều gì đó tương tự trong Node: const fs = require('fs'); const [file1, file2] = process.argv.slice(2); fs.createReadStream(file1).pipe(fs.createWriteStream(file2)). Bash có một cái gì đó tương tự, quá:cat $1 > $2
Max Heiber

64

Bạn đang hỏi sai câu hỏi.

Haskell không phải là một ngôn ngữ mà bạn đi xem xét một vài ví dụ thú vị và đi "aha, tôi hiểu rồi, đó là những gì làm cho nó tốt!"

Nó giống như là, chúng ta có tất cả các ngôn ngữ lập trình khác, và chúng ít nhiều giống nhau, và sau đó có Haskell hoàn toàn khác biệt và kỳ quặc theo một cách hoàn toàn tuyệt vời khi bạn đã quen với sự kỳ quặc. Nhưng vấn đề là, phải mất khá nhiều thời gian để thích nghi với sự kỳ quặc. Những điều khiến Haskell trở nên khác biệt so với hầu hết mọi ngôn ngữ bán chính thống khác:

  • Đánh giá lười biếng
  • Không có tác dụng phụ (mọi thứ đều tinh khiết, IO / vv xảy ra thông qua monads)
  • Hệ thống kiểu tĩnh cực kỳ biểu cảm

cũng như một số khía cạnh khác khác với nhiều ngôn ngữ chính thống (nhưng được chia sẻ bởi một số):

  • chức năng
  • khoảng trắng đáng kể
  • kiểu suy luận

Như một số áp phích khác đã trả lời, sự kết hợp của tất cả các tính năng này có nghĩa là bạn nghĩ về lập trình theo một cách hoàn toàn khác. Và vì vậy, thật khó để đưa ra một ví dụ (hoặc một tập hợp các ví dụ) truyền đạt đầy đủ điều này cho Joe-lập trình viên chính thống. Đó là một điều kinh nghiệm. (Để so sánh, tôi có thể cho bạn xem những bức ảnh về chuyến đi Trung Quốc năm 1970 của tôi, nhưng sau khi xem những bức ảnh, bạn vẫn sẽ không biết cuộc sống ở đó trong thời gian đó như thế nào. Tương tự, tôi có thể cho bạn xem một chiếc Haskell 'quicksort', nhưng bạn vẫn sẽ không biết trở thành Haskeller nghĩa là gì.)


17
Tôi không đồng ý với câu đầu tiên của bạn. Tôi thực sự ấn tượng với một vài ví dụ về mã Haskell ban đầu và điều thực sự thuyết phục tôi rằng nó đáng để học là bài báo này: cs.dartmouth.edu/~doug/powser.html Nhưng tất nhiên, điều này thật thú vị đối với một nhà toán học / vật lý. Một lập trình viên nhìn vào những thứ trong thế giới thực sẽ thấy ví dụ này thật nực cười.
Rafael S. Calsaverini

2
@Rafael: Điều đó đặt ra câu hỏi "một lập trình viên khi nhìn vào những thứ trong thế giới thực sẽ bị ấn tượng bởi điều gì"?
JD

Câu hỏi hay! Tôi không phải là một lập trình viên "thế giới thực" nên tôi không biết họ thích gì. hahaha ... Tôi biết các nhà vật lý và toán học thích gì. : P
Rafael S. Calsaverini.

27

Điều thực sự khiến Haskell trở nên khác biệt là nỗ lực mà nó dành cho thiết kế của mình để thực thi lập trình chức năng. Bạn có thể lập trình theo phong cách chức năng bằng khá nhiều ngôn ngữ, nhưng tất cả đều quá dễ dàng để bỏ qua ngay từ đầu. Haskell không cho phép bạn từ bỏ lập trình chức năng, vì vậy bạn phải đưa nó đến kết luận hợp lý của nó, đây là một chương trình cuối cùng dễ lập luận hơn và loại bỏ toàn bộ các loại lỗi gai góc nhất.

Khi nói đến việc viết một chương trình để sử dụng trong thế giới thực, bạn có thể thấy Haskell thiếu một số cách thực tế, nhưng giải pháp cuối cùng của bạn sẽ tốt hơn nếu bạn đã biết Haskell ngay từ đầu. Tôi chắc chắn là chưa đến đó, nhưng cho đến nay việc học Haskell còn thú vị hơn nhiều so với việc Lisp đang học đại học.


1
Vâng, luôn có khả năng xảy ra luôn và chỉ sử dụng ST đơn nguyên và / hoặc unsafePerformIOcho những người chỉ muốn xem các bỏng trên thế giới;)
sara

22

Một phần của rắc rối là độ tinh khiết và tính năng nhập tĩnh cho phép tính song song kết hợp với tối ưu hóa tích cực. Các ngôn ngữ song song đang trở nên phổ biến hiện nay với đa lõi có một chút gián đoạn.

Haskell cung cấp cho bạn nhiều tùy chọn cho song song hơn bất kỳ ngôn ngữ đa dụng nào, cùng với trình biên dịch mã gốc, nhanh. Thực sự không có cạnh tranh với loại hỗ trợ cho các phong cách song song:

Vì vậy, nếu bạn quan tâm đến việc làm cho công việc đa lõi của mình, Haskell có điều gì đó để nói. Một nơi tuyệt vời để bắt đầu là với hướng dẫn của Simon Peyton Jones về lập trình song song và đồng thời trong Haskell .


"cùng với một trình biên dịch mã gốc, nhanh"?
JD

Tôi tin rằng dons đang đề cập đến GHCI.
Gregory Higley

3
@Jon: shootout.alioth.debian.org/u32/… Ví dụ : Haskell làm khá tốt trong loạt luân lưu.
Peaker

4
@Jon: Mã loạt đá luân lưu đã rất cũ, và từ một quá khứ xa xôi khi GHC không phải là một trình biên dịch tối ưu hóa. Tuy nhiên, nó chứng minh mã Haskell có thể đi đến mức thấp để mang lại hiệu suất, nếu cần. Các giải pháp mới hơn trong loạt đá luân lưu thường khó hiểu hơn và vẫn nhanh.
Peaker

1
@GregoryHigley Có sự khác biệt giữa GHCI và GHC.
Danh sách Jeremy

18

Bộ nhớ giao dịch phần mềm là một cách khá hay để xử lý đồng thời. Nó linh hoạt hơn nhiều so với việc truyền tin nhắn và không dễ bị bế tắc như mutexes. Việc triển khai STM của GHC được coi là một trong những cách tốt nhất.


18

Tôi đã dành năm ngoái để học Haskell và viết một dự án khá lớn và phức tạp trong đó. (Dự án là một hệ thống giao dịch quyền chọn tự động và mọi thứ từ thuật toán giao dịch đến phân tích cú pháp và xử lý nguồn cấp dữ liệu thị trường tốc độ thấp, cấp thấp đều được thực hiện trong Haskell.) Nó ngắn gọn và dễ hiểu hơn đáng kể (đối với những người có nền thích hợp) so với phiên bản Java cũng như cực kỳ mạnh mẽ.

Có lẽ chiến thắng lớn nhất đối với tôi là khả năng mô-đun hóa dòng điều khiển thông qua những thứ như monoids, monads, v.v. Một ví dụ rất đơn giản sẽ là Đơn đặt hàng; trong một biểu thức chẳng hạn như

c1 `mappend` c2 `mappend` c3

ở đâu c1và như vậy trả về LT, EQhoặc GT, việc c1trả về EQlàm cho biểu thức tiếp tục, đánh giá c2; nếu c2trả về LThoặc GTđó là giá trị của tổng thể và c3không được đánh giá. Loại thứ này trở nên phức tạp và phức tạp hơn đáng kể trong những thứ như trình tạo và phân tích cú pháp tin nhắn đơn nguyên, nơi tôi có thể đang thực hiện các loại trạng thái khác nhau, có các điều kiện hủy bỏ khác nhau hoặc có thể muốn quyết định cho bất kỳ cuộc gọi cụ thể nào liệu hủy bỏ thực sự có ý nghĩa "không xử lý thêm" hoặc có nghĩa là, "trả lại lỗi ở cuối, nhưng tiếp tục xử lý để thu thập thêm thông báo lỗi."

Đây là tất cả những thứ cần một thời gian và có lẽ khá nỗ lực để tìm hiểu, và do đó có thể khó đưa ra một lập luận thuyết phục cho nó đối với những người chưa biết những kỹ thuật này. Tôi nghĩ rằng hướng dẫn All About Monads mang lại một minh chứng khá ấn tượng về một khía cạnh của điều này, nhưng tôi sẽ không mong đợi rằng bất kỳ ai không quen thuộc với tài liệu đã có thể "hiểu nó" trong lần đầu tiên hoặc thậm chí là thứ ba, đọc cẩn thận.

Dù sao, cũng có rất nhiều thứ hay ho khác trong Haskell, nhưng đây là một thứ chính mà tôi không thấy được đề cập thường xuyên, có lẽ vì nó khá phức tạp.


2
Rất thú vị! Tổng cộng có bao nhiêu dòng mã Haskell đi vào hệ thống giao dịch tự động của bạn? Bạn đã xử lý khả năng chịu lỗi như thế nào và bạn nhận được những loại kết quả hiệu suất nào? Gần đây tôi đã nghĩ rằng Haskell có tiềm năng tốt cho lập trình có độ trễ thấp ...
JD

12

Để có một ví dụ thú vị, bạn có thể xem tại: http://en.literateprograms.org/Quicksort_(Haskell)

Điều thú vị là xem việc triển khai bằng nhiều ngôn ngữ khác nhau.

Điều làm cho Haskell trở nên thú vị, cùng với các ngôn ngữ chức năng khác, là bạn phải suy nghĩ khác về cách lập trình. Ví dụ, bạn thường sẽ không sử dụng vòng lặp for hoặc while, mà sẽ sử dụng đệ quy.

Như đã đề cập ở trên, Haskell và các ngôn ngữ chức năng khác vượt trội với khả năng xử lý song song và viết ứng dụng để hoạt động trên đa lõi.


2
đệ quy là quả bom. đó và đối sánh mẫu.
Người mới đến từ Ellery,

1
Thoát khỏi vòng lặp for và while là phần khó nhất đối với tôi khi viết bằng ngôn ngữ chức năng. :)
James Black

4
Học cách suy nghĩ trong đệ quy thay vì lặp lại là phần khó nhất đối với tôi. Cuối cùng khi nó chìm vào trong, đó là một trong những sự kiện tuyệt vời nhất về lập trình mà tôi từng có.
Chris Connett

8
Ngoại trừ việc lập trình viên Haskell đang làm việc hiếm khi sử dụng đệ quy nguyên thủy; chủ yếu là bạn sử dụng các chức năng thư viện như bản đồ và gấp.
Paul Johnson

18
Tôi thấy thú vị hơn là thuật toán quicksort ban đầu của Hoare đã bị biến thành dạng dựa trên danh sách không đúng chỗ này rõ ràng để các triển khai không hiệu quả vô ích có thể được viết "thanh lịch" trong Haskell. Nếu bạn cố gắng viết một câu chuyện nhanh thực sự (tại chỗ) trong Haskell, bạn sẽ thấy nó xấu như địa ngục. Nếu bạn cố gắng viết một quicksort chung chung có hiệu suất cạnh tranh trong Haskell, bạn sẽ thấy nó thực sự không thể thực hiện được do các lỗi lâu dài trong bộ thu gom rác của GHC. Ca ngợi nhanh chóng như một ví dụ điển hình cho niềm tin của người ăn xin Haskell, IMHO.
JD

8

Tôi không thể cho bạn một ví dụ, tôi là một chàng trai OCaml, nhưng khi tôi ở trong tình huống như bạn, sự tò mò chỉ diễn ra và tôi phải tải xuống trình biên dịch / thông dịch viên và thử. Bạn có thể sẽ học nhiều hơn theo cách đó về điểm mạnh và điểm yếu của một ngôn ngữ chức năng nhất định.


1
Đừng quên đọc mã nguồn của trình biên dịch. Điều đó cũng sẽ cung cấp cho bạn nhiều thông tin giá trị.
JD

7

Một điều tôi thấy rất thú vị khi giải quyết các thuật toán hoặc các vấn đề toán học là tính lười biếng cố hữu của Haskell đối với các phép tính, điều này chỉ có thể thực hiện được do tính chất chức năng nghiêm ngặt của nó.

Ví dụ: nếu bạn muốn tính toán tất cả các số nguyên tố, bạn có thể sử dụng

primes = sieve [2..]
    where sieve (p:xs) = p : sieve [x | x<-xs, x `mod` p /= 0]

và kết quả thực sự là một danh sách vô hạn. Nhưng Haskell sẽ đánh giá nó từ trái từ phải, vì vậy, miễn là bạn không cố gắng làm điều gì đó yêu cầu toàn bộ danh sách, bạn vẫn có thể sử dụng nó mà chương trình không bị kẹt ở vô cực, chẳng hạn như:

foo = sum $ takeWhile (<100) primes

mà tổng tất cả các số nguyên tố nhỏ hơn 100. Điều này là tốt vì một số lý do. Trước hết, tôi chỉ cần viết một hàm số nguyên tố tạo ra tất cả các số nguyên tố và sau đó tôi đã sẵn sàng làm việc với các số nguyên tố. Trong ngôn ngữ lập trình hướng đối tượng, tôi sẽ cần một số cách để cho hàm biết có bao nhiêu số nguyên tố mà nó phải tính trước khi trả về hoặc mô phỏng hành vi danh sách vô hạn với một đối tượng. Một điều khác là nói chung, bạn sẽ viết mã thể hiện những gì bạn muốn tính toán chứ không phải theo thứ tự để đánh giá mọi thứ - thay vào đó, trình biên dịch sẽ làm điều đó cho bạn.

Điều này không chỉ hữu ích cho danh sách vô hạn, trên thực tế, nó được sử dụng mà bạn không hề hay biết khi không cần đánh giá nhiều hơn mức cần thiết.


2
Điều này không hoàn toàn đúng; với hành vi trả về lợi nhuận của C # (một ngôn ngữ hướng đối tượng), bạn cũng có thể khai báo danh sách vô hạn được đánh giá theo yêu cầu.
Jeff Yates

2
Điểm tốt. Bạn đúng và tôi nên tránh nêu những gì có thể và không thể làm bằng các ngôn ngữ khác một cách phân loại. Tôi nghĩ rằng ví dụ của tôi là thiếu sót, nhưng tôi vẫn nghĩ bạn đạt được điều gì đó từ cách đánh giá lười biếng của Haskell: nó thực sự ở đó theo mặc định và không có bất kỳ nỗ lực nào từ lập trình viên. Và điều này, tôi tin rằng, là do bản chất chức năng của nó và không có tác dụng phụ.
waxwing

8
Bạn có thể quan tâm để tìm hiểu tại sao "sàng" là không Sieve of Eratosthenes: lambda-the-ultimate.org/node/3127
Chris Conway

@Chris: Cảm ơn, đó thực sự là một bài báo khá thú vị! Hàm số nguyên tố ở trên không phải là hàm mà tôi đang sử dụng cho các tính toán của riêng mình vì nó rất chậm. Tuy nhiên, bài báo đưa ra một điểm tốt rằng việc kiểm tra tất cả các số để tìm mod thực sự là một thuật toán khác.
waxwing

6

Tôi đồng ý với những người khác rằng xem một vài ví dụ nhỏ không phải là cách tốt nhất để thể hiện Haskell. Nhưng dù sao tôi cũng sẽ cho một ít. Đây là một giải pháp nhanh như chớp cho các bài toán 18 và 67 của Dự án Euler , các bài toán này yêu cầu bạn tìm đường đi có tổng lớn nhất từ ​​đáy đến đỉnh của một tam giác:

bottomUp :: (Ord a, Num a) => [[a]] -> a
bottomUp = head . bu
  where bu [bottom]     = bottom
        bu (row : base) = merge row $ bu base
        merge [] [_] = []
        merge (x:xs) (y1:y2:ys) = x + max y1 y2 : merge xs (y2:ys)

Đây là cách triển khai hoàn chỉnh, có thể tái sử dụng thuật toán BubbleSearch của Lesh và Mitzenmacher. Tôi đã sử dụng nó để đóng gói các tệp phương tiện lớn để lưu trữ trên DVD mà không lãng phí:

data BubbleResult i o = BubbleResult { bestResult :: o
                                     , result :: o
                                     , leftoverRandoms :: [Double]
                                     }
bubbleSearch :: (Ord result) =>
                ([a] -> result) ->       -- greedy search algorithm
                Double ->                -- probability
                [a] ->                   -- list of items to be searched
                [Double] ->              -- list of random numbers
                [BubbleResult a result]  -- monotone list of results
bubbleSearch search p startOrder rs = bubble startOrder rs
    where bubble order rs = BubbleResult answer answer rs : walk tries
            where answer = search order
                  tries  = perturbations p order rs
                  walk ((order, rs) : rest) =
                      if result > answer then bubble order rs
                      else BubbleResult answer result rs : walk rest
                    where result = search order

perturbations :: Double -> [a] -> [Double] -> [([a], [Double])]
perturbations p xs rs = xr' : perturbations p xs (snd xr')
    where xr' = perturb xs rs
          perturb :: [a] -> [Double] -> ([a], [Double])
          perturb xs rs = shift_all p [] xs rs

shift_all p new' [] rs = (reverse new', rs)
shift_all p new' old rs = shift_one new' old rs (shift_all p)
  where shift_one :: [a] -> [a] -> [Double] -> ([a]->[a]->[Double]->b) -> b
        shift_one new' xs rs k = shift new' [] xs rs
          where shift new' prev' [x] rs = k (x:new') (reverse prev') rs
                shift new' prev' (x:xs) (r:rs) 
                    | r <= p    = k (x:new') (prev' `revApp` xs) rs
                    | otherwise = shift new' (x:prev') xs rs
                revApp xs ys = foldl (flip (:)) ys xs

Tôi chắc chắn rằng mã này trông giống như vô nghĩa ngẫu nhiên. Nhưng nếu bạn đọc mục blog của Mitzenmacher và hiểu thuật toán, bạn sẽ ngạc nhiên rằng có thể đóng gói thuật toán thành mã mà không cần nói gì về những gì bạn đang tìm kiếm.

Sau khi cung cấp cho bạn một số ví dụ như bạn yêu cầu, tôi sẽ nói rằng cách tốt nhất để bắt đầu đánh giá cao Haskell là đọc bài báo đã cho tôi những ý tưởng tôi cần để viết bộ đóng gói DVD: Tại sao các vấn đề lập trình chức năng của John Hughes. Bài báo này thực sự có trước Haskell, nhưng nó giải thích một cách xuất sắc một số ý tưởng khiến mọi người thích Haskell.


5

Đối với tôi, điểm hấp dẫn của Haskell là lời hứa của trình biên dịch đảm bảo tính đúng đắn. Ngay cả khi nó dành cho các phần thuần túy của mã.

Tôi đã viết rất nhiều mã mô phỏng khoa học và đã rất nhiều lần tự hỏi liệu có lỗi nào trong các mã trước đây của tôi, có thể làm mất hiệu lực của rất nhiều công việc hiện tại hay không.


6
Làm thế nào để nó đảm bảo tính đúng đắn?
Jonathan Fischoff

Các phần thuần túy của mã an toàn hơn nhiều so với các phần không tinh khiết. Mức độ tin cậy / nỗ lực đầu tư cao hơn nhiều.
rpg

1
Điều gì đã cho bạn ấn tượng đó?
JD

5

Tôi thấy rằng đối với một số công việc nhất định, tôi làm việc vô cùng hiệu quả với Haskell.

Lý do là vì cú pháp cô đọng và dễ kiểm tra.

Đây là cú pháp khai báo hàm như sau:

foo a = a + 5

Đó là cách đơn giản nhất tôi có thể nghĩ để xác định một hàm.

Nếu tôi viết ngược lại

nghịch đảoFoo a = a - 5

Tôi có thể kiểm tra xem nó có phải là nghịch đảo đối với bất kỳ đầu vào ngẫu nhiên nào không bằng cách viết

prop_IsInverse :: Double -> Bool
prop_IsInverse a = a == (inverseFoo $ foo a)

Và gọi từ dòng lệnh

jonny @ ubuntu: runhaskell quickCheck + tên fooFileName.hs

Điều này sẽ kiểm tra xem tất cả các thuộc tính trong tệp của tôi có được giữ hay không, bằng cách kiểm tra ngẫu nhiên các đầu vào hàng trăm lần như vậy.

Tôi không nghĩ Haskell là ngôn ngữ hoàn hảo cho mọi thứ, nhưng khi nói đến việc viết các hàm nhỏ và thử nghiệm, tôi chưa thấy điều gì tốt hơn. Nếu chương trình của bạn có một thành phần toán học thì điều này rất quan trọng.


Bạn đang giải quyết vấn đề gì và bạn đã thử những ngôn ngữ nào khác?
JD

1
Đồ họa 3D thời gian thực cho điện thoại di động và iPad.
Jonathan Fischoff

3

Nếu bạn có thể quan tâm đến hệ thống loại trong Haskell, tôi nghĩ rằng bản thân nó đã là một thành tựu.


1
Có gì để có được? Nếu bạn phải, hãy nghĩ "data" == "class" và "typeclass" = "interface" / "role" / "trait". Nó không thể đơn giản hơn. (Thậm chí không có "vô giá trị" để mess bạn lên Null là một khái niệm bạn có thể xây dựng vào loại của chính mình..)
jrockway

8
Có rất nhiều thứ để có được, nhanh lên. Trong khi bạn và tôi thấy nó tương đối đơn giản, nhiều người - thậm chí nhiều nhà phát triển - thấy một số loại trừu tượng rất khó hiểu. Tôi biết nhiều nhà phát triển vẫn không hoàn toàn nắm bắt được ý tưởng về con trỏ và tham chiếu trong các ngôn ngữ chính thống hơn, mặc dù họ sử dụng chúng hàng ngày.
Gregory Higley

2

nó không có cấu trúc vòng lặp. không nhiều ngôn ngữ có đặc điểm này.


17
ghci>: m + Control.Monad ghci> forM_ [1..3] in 1 2 3
sastanin

1

Tôi đồng ý với những người nói rằng lập trình chức năng vặn não bạn nhìn lập trình từ một góc độ khác. Tôi chỉ sử dụng nó như một người có sở thích, nhưng tôi nghĩ về cơ bản nó đã thay đổi cách tôi tiếp cận một vấn đề. Tôi không nghĩ rằng tôi sẽ đạt được hiệu quả gần như với LINQ nếu không tiếp xúc với Haskell (và sử dụng trình tạo và hiểu danh sách bằng Python).


-1

Để đưa ra một quan điểm trái ngược: Steve Yegge viết rằng các ngôn ngữ Hindely-Milner thiếu tính linh hoạt cần thiết để viết các hệ thống tốt :

HM rất đẹp, theo nghĩa toán học hình thức hoàn toàn vô dụng. Nó xử lý một số cấu trúc tính toán rất độc đáo; công văn khớp mẫu được tìm thấy trong Haskell, SML và OCaml đặc biệt tiện dụng. Không có gì ngạc nhiên khi nó xử lý một số cấu trúc phổ biến và rất mong muốn khác một cách vụng về tốt nhất, nhưng chúng giải thích những tình huống đó bằng cách nói rằng bạn đã nhầm, bạn thực sự không muốn chúng. Bạn biết đấy, những thứ như, ồ, thiết lập các biến.

Haskell rất đáng học hỏi, nhưng nó có những điểm yếu riêng.


5
Mặc dù chắc chắn đúng là các hệ thống loại mạnh thường yêu cầu bạn tuân thủ chúng (đó là điều làm cho sức mạnh của chúng trở nên hữu ích), nhưng đó cũng là trường hợp mà nhiều (hầu hết?) Các hệ thống loại dựa trên HM hiện có làm, trên thực tế, có một số loại ' Escape hatch 'như được mô tả trong liên kết (lấy Obj.magic trong O'Caml làm ví dụ, mặc dù tôi chưa bao giờ sử dụng nó trừ khi bị hack); Tuy nhiên, trong thực tế, đối với nhiều loại chương trình, người ta không bao giờ cần một thiết bị như vậy.
Zach Snow

3
Câu hỏi đặt ra liệu việc thiết lập các biến có "mong muốn" hay không phụ thuộc vào mức độ đau khi sử dụng các cấu trúc thay thế so với mức độ đau do việc sử dụng các biến. Điều đó không phải để bác bỏ toàn bộ lập luận, mà là chỉ ra rằng việc lấy tuyên bố "các biến là một cấu trúc rất mong muốn" làm tiên đề không phải là cơ sở của một lập luận cogent. Nó chỉ tình cờ là cách hầu hết mọi người học cách lập trình.
gtd

5
-1: Những tuyên bố của Steve có phần lỗi thời nhưng phần lớn là hoàn toàn sai sự thật. Hạn chế giá trị thoải mái của OCaml và hệ thống kiểu của .NET là một số ví dụ phản bác rõ ràng cho các tuyên bố của anh ta.
JD

4
Steve Yegge có một con ong không hợp lý trong ca-pô của mình về việc nhập tĩnh và không chỉ hầu hết những gì anh ấy nói về nó đều sai, anh ấy còn tiếp tục đưa ra mọi cơ hội có sẵn (và thậm chí cả một số điều không khả dụng). Bạn nên tin tưởng vào kinh nghiệm của riêng bạn về vấn đề này.
ShreevatsaR

3
Mặc dù tôi không đồng ý với Yegge về kiểu gõ tĩnh và kiểu động, nhưng Haskell có kiểu Data.Dynamic. Nếu bạn muốn nhập động, bạn có thể có nó!
jrockway
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.