Tôi đồng ý với Dietrich Epp: đó là sự kết hợp của một số thứ giúp GHC nhanh chóng.
Đầu tiên và quan trọng nhất, Haskell rất cao cấp. Điều này cho phép trình biên dịch thực hiện tối ưu hóa tích cực mà không phá vỡ mã của bạn.
Hãy nghĩ về SQL. Bây giờ, khi tôi viết một SELECT
tuyên bố, nó có thể trông giống như một vòng lặp bắt buộc, nhưng nó không phải là . Có vẻ như nó lặp lại trên tất cả các hàng trong bảng đó khi cố gắng tìm một hàng khớp với các điều kiện đã chỉ định, nhưng thực ra "trình biên dịch" (công cụ DB) có thể thực hiện tra cứu chỉ mục - có đặc điểm hiệu suất hoàn toàn khác. Nhưng vì SQL rất cao cấp, "trình biên dịch" có thể thay thế các thuật toán hoàn toàn khác nhau, áp dụng nhiều bộ xử lý hoặc kênh I / O hoặc toàn bộ máy chủ một cách trong suốt, v.v.
Tôi nghĩ về Haskell là như nhau. Bạn có thể nghĩ rằng bạn vừa yêu cầu Haskell ánh xạ danh sách đầu vào vào danh sách thứ hai, lọc danh sách thứ hai vào danh sách thứ ba và sau đó đếm xem có bao nhiêu mục kết quả. Nhưng bạn đã không thấy GHC áp dụng các quy tắc viết lại hợp nhất luồng phía sau hậu trường, biến toàn bộ thành một vòng mã máy chặt chẽ duy nhất thực hiện toàn bộ công việc trong một lần truyền dữ liệu mà không cần phân bổ - loại điều sẽ xảy ra tẻ nhạt, dễ bị lỗi và không thể bảo trì để viết bằng tay. Điều đó chỉ thực sự khả thi vì thiếu các chi tiết cấp thấp trong mã.
Một cách khác để xem xét nó có thể là lỗi tại sao Haskell không nên nhanh? Nó làm gì mà làm cho nó chậm lại?
Đây không phải là ngôn ngữ được dịch như Perl hay JavaScript. Nó thậm chí không phải là một hệ thống máy ảo như Java hay C #. Nó biên dịch tất cả các mã nguồn gốc, do đó không có chi phí nào ở đó.
Không giống như các ngôn ngữ OO [Java, C #, JavaScript,], Haskell có kiểu xóa hoàn toàn [như C, C ++, Pascal,]. Tất cả các loại kiểm tra chỉ xảy ra tại thời gian biên dịch. Vì vậy, không có kiểm tra loại thời gian chạy để làm chậm bạn. (Không có kiểm tra con trỏ null, đối với vấn đề đó. Trong giả sử, Java, JVM phải kiểm tra các con trỏ null và ném ngoại lệ nếu bạn trì hoãn một.
Bạn nói rằng nghe có vẻ chậm khi "tạo các chức năng một cách nhanh chóng", nhưng nếu bạn xem xét rất kỹ, bạn thực sự không làm điều đó. Nó có thể trông giống như bạn, nhưng bạn thì không. Nếu bạn nói (+5)
, tốt, đó là mã hóa cứng vào mã nguồn của bạn. Nó không thể thay đổi trong thời gian chạy. Vì vậy, nó không thực sự là một chức năng động. Ngay cả các hàm bị cong thực sự chỉ là lưu các tham số vào một khối dữ liệu. Tất cả các mã thực thi thực sự tồn tại tại thời gian biên dịch; không có giải thích thời gian chạy. (Không giống như một số ngôn ngữ khác có "chức năng eval".)
Hãy nghĩ về Pascal. Nó đã cũ và không ai thực sự sử dụng nó nữa, nhưng không ai phàn nàn rằng Pascal chậm . Có rất nhiều điều không thích về nó, nhưng sự chậm chạp không thực sự là một trong số đó. Haskell không thực sự làm được nhiều điều khác với Pascal, ngoài việc có bộ sưu tập rác thay vì quản lý bộ nhớ thủ công. Và dữ liệu bất biến cho phép một số tối ưu hóa cho công cụ GC [việc đánh giá lười biếng sau đó làm phức tạp phần nào].
Tôi nghĩ vấn đề là Haskell có vẻ tiên tiến, tinh vi và cao cấp, và mọi người đều nghĩ "oh wow, điều này thực sự mạnh mẽ, nó phải chậm một cách đáng kinh ngạc! " Nhưng không phải vậy. Hoặc ít nhất, nó không theo cách bạn mong đợi. Vâng, nó có một hệ thống loại tuyệt vời. Nhưng bạn biết gì không? Đó là tất cả xảy ra tại thời gian biên dịch. Đến giờ chạy, nó biến mất. Có, nó cho phép bạn xây dựng các ADT phức tạp với một dòng mã. Nhưng bạn biết gì không? Một ADT chỉ là một C bình thường đơn giản union
của struct
s. Chỉ có bấy nhiêu thôi.
Kẻ giết người thực sự là đánh giá lười biếng. Khi bạn nhận được sự nghiêm ngặt / lười biếng của mã của mình, bạn có thể viết mã nhanh một cách ngu ngốc mà vẫn thanh lịch và đẹp. Nhưng nếu bạn hiểu sai thứ này, chương trình của bạn sẽ chậm hơn hàng ngàn lần và thực sự không rõ ràng tại sao điều này lại xảy ra.
Ví dụ, tôi đã viết một chương trình nhỏ tầm thường để đếm số lần mỗi byte xuất hiện trong một tệp. Đối với tệp đầu vào 25KB, chương trình mất 20 phút để chạy và nuốt 6 GB RAM! Đó là vô lý!! Nhưng sau đó tôi nhận ra vấn đề là gì, thêm một kiểu tiếng nổ duy nhất và thời gian chạy giảm xuống 0,02 giây .
Đây là nơi Haskell bất ngờ đi chậm. Và nó chắc chắn phải mất một thời gian để làm quen với nó. Nhưng theo thời gian, việc viết mã thực sự nhanh hơn sẽ dễ dàng hơn.
Điều gì làm cho Haskell nhanh như vậy? Độ tinh khiết. Các loại tĩnh. Lười biếng. Nhưng trên hết, đủ trình độ cao để trình biên dịch có thể thay đổi hoàn toàn việc triển khai mà không phá vỡ kỳ vọng của mã của bạn.
Nhưng tôi đoán đó chỉ là ý kiến của tôi ...