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.