Do đó, theo định nghĩa, truy vấn cơ sở dữ liệu hoặc tập tin ghi không thể được thực hiện theo kiểu chức năng thuần túy theo định nghĩa. Đó là ví dụ, là một trong những lý do chúng ta cần các đơn nguyên.
Không ai "cần" các đơn nguyên, đó chỉ là một cách để mô tả mọi thứ. Trong thực tế, nó có lẽ không phải là cách tốt nhất. Một số hình thức gõ hiệu ứng , các kiểu duy nhất hoặc một hệ thống dựa trên logic tuyến tính đầy đủ có vẻ thuyết phục hơn về mặt lý thuyết nhưng tất cả đều là sự khởi đầu triệt để hơn từ các hệ thống loại nổi tiếng và phức tạp hơn để diễn đạt. Monadic IO như được tìm thấy trong Haskell là một sự thỏa hiệp giữa tính khả dụng và đơn giản, vì về cơ bản, nó mô hình hóa lập trình hoàn toàn bắt buộc theo cách dễ dàng cùng tồn tại với hệ thống kiểu ML hiện có được sử dụng trong ngôn ngữ.
Câu hỏi là - tại sao chúng ta coi đầu ra STDOUT là một cái gì đó không trong sạch? Có, bất kỳ trình xử lý tệp nào cũng có rủi ro - chúng tôi không bao giờ có thể chắc chắn rằng dữ liệu sẽ luôn được ghi. Nhưng còn STDOUT thì sao? Tại sao chúng ta nên nghĩ về nó như là một cái gì đó không đáng tin cậy? Là nó không đáng tin cậy hơn mà đánh giá chính nó? Ý tôi là, chúng ta luôn có thể bóp cò và do đó, tính toán gián đoạn.
Không phải vậy, và chúng tôi thì không. Đầu vào và đầu ra từ toàn bộ chương trình có thể được coi đơn giản là đối số và kết quả từ việc coi toàn bộ chương trình là một hàm thuần lớn. Miễn là nó in cùng một thứ thành thiết bị xuất chuẩn nếu bạn cho nó ăn cùng một thứ từ stdin, thì đó vẫn là một chức năng thuần túy. Trong thực tế, trước khi giới thiệu IO đơn âm, Haskell đã sử dụng hệ thống I / O dựa trên luồng sử dụng luồng lười biếng thuần túy cho đầu vào và đầu ra. Nó đánh rơi nó bởi vì nó rõ ràng là một nỗi đau để sử dụng, điều này có thể cho bạn một số ý tưởng về lý do tại sao bạn không nghe thấy bất cứ điều gì như thế này. :]
Để đưa ra quan điểm theo cách sillier, hãy xem xét ngôn ngữ bí truyền tối giản, Lazy K :
Lazy K là ngôn ngữ lập trình chức năng minh bạch được thu thập rác, với hệ thống I / O dựa trên luồng đơn giản.
Điều khác biệt Lazy K với các ngôn ngữ khác là hầu như thiếu các tính năng khác. Nó không, ví dụ, không cung cấp một hệ thống đa hình Hindley-Milner tích hợp. Nó không được cung cấp với một thư viện tiêu chuẩn rộng lớn với sự hỗ trợ cho lập trình GUI độc lập với nền tảng và các ràng buộc với các ngôn ngữ khác. Cũng không thể viết bất kỳ thư viện nào như vậy, trong số những thứ khác, Lazy K không cung cấp bất kỳ cách nào để định nghĩa hoặc tham chiếu đến bất kỳ chức năng nào ngoài các hàm dựng sẵn. Sự bất lực này được bổ sung bởi sự thiếu hỗ trợ phù hợp cho các số, chuỗi hoặc bất kỳ loại dữ liệu nào khác. Tuy nhiên, Lazy K là Turing-đầy đủ.
(...)
Các chương trình K lười biếng sống trong cõi Platonic vượt thời gian giống như các hàm toán học, điều mà trang Unlambda gọi là "vương quốc may mắn của phép tính lambda chưa được thuần hóa". Giống như bộ sưu tập rác ẩn quá trình quản lý bộ nhớ từ lập trình viên, do đó tính minh bạch tham chiếu che giấu quá trình đánh giá. Thực tế là một số tính toán là cần thiết để xem hình ảnh của bộ Mandelbrot hoặc để "chạy" chương trình Lazy K, là một chi tiết triển khai. Đó là bản chất của lập trình chức năng.
(...)
Làm thế nào để xử lý đầu vào và đầu ra trong một ngôn ngữ mà không có tác dụng phụ? Theo một nghĩa nào đó, đầu vào và đầu ra không có tác dụng phụ; chúng là như vậy, có thể nói, tác dụng trước và sau. Vì vậy, đó là trong Lazy K, nơi một chương trình được coi đơn giản là một hàm từ không gian của các đầu vào có thể đến không gian của các đầu ra có thể.
Tôi nghi ngờ bạn sẽ tìm thấy một ngôn ngữ chức năng thuần túy hơn thế!
Mặc dù vậy, hãy ghi nhớ rằng những điều trên chỉ áp dụng cho việc chủ yếu lấy đầu vào và đầu ra của một hàm thuần túy và kết nối chúng với stdin / stdout "bên ngoài" theo một cách nào đó. Có một sự khác biệt lớn giữa điều đó và có quyền truy cập vào các nguyên thủy I / O cấp hệ thống thực sự. Các chi tiết triển khai đọc và ghi vào các luồng có thể rò rỉ tạp chất trừ khi được đóng gói cẩn thận.
Tôi hy vọng đây là lý do chính mà bạn không thể thực hiện điều này trực tiếp trong Haskell - các trường hợp sử dụng hợp lý là mỏng so với sử dụng IO đơn âm, và sau này có rất nhiều lợi ích khi có quyền truy cập vào thực tế. Tôi tin rằng đó là lý do tại sao, ví dụ, các đối số dòng lệnh cho chương trình không chỉ đơn giản được chuyển thành đối số main
, mặc dù có vẻ như chúng phải theo trực giác.
Bạn có thể phục hồi một phiên bản tối thiểu của một cái gì đó như thế này trong một chương trình cụ thể, mặc dù - chỉ chụp các đối số là giá trị tinh khiết và sau đó sử dụng các interact
chức năng cho phần còn lại của chương trình của bạn.