STDOUT và tạp chất của nó


10

Tôi đã đọc rất nhiều sách và bài viết về lập trình chức năng và vẫn xấu hổ vì không thể hiểu chắc chắn một số khái niệm rất cơ bản.

Một trong những ý tưởng chính của lập trình chức năng là cùng một đầu vào luôn tạo ra cùng một đầu ra. 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.

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.

Câu trả lời:


6

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 interactchức năng cho phần còn lại của chương trình của bạn.


Thưa ông, tôi phải thú nhận, tôi thích bất kỳ câu trả lời nào của bạn trên bất kỳ ngăn xếp nào. Bạn chắc chắn nên viết một cuốn sách Haskell và tôi KHÔNG đùa.
shabunc

@shabunc: Đôi khi tôi đã tự hỏi tổng số câu trả lời của tôi về SO gần bằng kích thước của một cuốn sách như thế nào ...
CA McCann

Bạn có thể đưa ra một ví dụ về một hệ thống dựa trên logic tuyến tính đầy đủ? Điều đó có vẻ thú vị, nếu nó tồn tại.
cấu hình

@configurator: Lưu ý cách tôi liên kết với các ngôn ngữ cụ thể cho các ngôn ngữ khác, nhưng một trang wikipedia cho logic tuyến tính? Than ôi, nếu tôi có một ví dụ, tôi sẽ đưa ra nó. : [Tất cả những gì tôi đã nghe nói là các nguyên mẫu một phần và hệ thống thử nghiệm từ nghiên cứu CS. Nếu bạn muốn tìm hiểu sâu hơn về điều đó, đây là một số tài liệu tương đối dễ tiếp cận trên các hệ thống loại tuyến tính có thể giúp bạn bắt đầu.
CA McCann

3

Mặc dù độ tinh khiết trong một chương trình chức năng là một mục tiêu xứng đáng, nhưng thực tế là mọi chương trình hữu ích, không tầm thường sẽ có một số tạp chất (hoặc "tác dụng phụ"), vì những lý do bạn đã đề cập.

Theo định nghĩa, các chương trình hoàn toàn thuần túy là một hộp đen kín và về cơ bản là không thú vị.

Các ngôn ngữ chức năng Haskell sẽ chăm sóc của vấn đề này bằng cách cách ly các tác dụng phụ như đầu ra trong monads . Các đơn nguyên duy trì một phong cách lập trình chức năng thuần túy, trong khi vẫn có thể tạo ra đầu ra.


chắc chắn, bạn đã đúng Nhưng tôi hiểu tại sao độ tinh khiết 100% là không tưởng. Tôi hỏi chỉ là về STDOUT.
shabunc

1
STDOUT là một tác dụng phụ, giống như bất kỳ khác. Trong nội bộ, đơn nguyên sẽ thực hiện bất kỳ kiểm tra lỗi có thể được yêu cầu.
Robert Harvey

vâng, đây là câu hỏi này là gì - tại sao nó được coi là tác dụng phụ giống như bất kỳ câu hỏi nào khác?
shabunc

2
Bất cứ điều gì làm thay đổi thế giới bên ngoài đều được coi là tác dụng phụ.
Robert Harvey

1

Viết thư cho STDOUT có thể thất bại nếu bạn không được kết nối với thiết bị đầu cuối hoặc nếu bạn (vì một lý do nào đó) đã đóng bộ mô tả tệp cho nó.

Ngoài ra, STDOUT không phải lúc nào cũng là "màn hình giao diện điều khiển". Đôi khi, nó được chuyển sang một chương trình khác. Đôi khi đường ống bị vỡ.


0

Nó giúp nếu bạn nghĩ về sự thuần khiết trong thuật ngữ "Thay đổi trạng thái của thế giới bên ngoài". Điều đó có thể bao gồm ghi vào bảng điều khiển, tệp nhật ký, đẩy CD hoặc "Phóng tên lửa".

Nó cũng có thể là một vấn đề về mặt thực thi đồng thời. Nếu bạn biết một chức năng không có tác dụng phụ, bạn có thể dễ dàng sắp xếp đồng thời vì bạn có thể chứng minh rằng không thể có điều kiện chủng tộc hoặc tương tự.


Thay đổi trạng thái của thế giới bên ngoài hoặc phụ thuộc vào trạng thái của thế giới bên ngoài. Xem câu hỏi này để thảo luận thêm dọc theo những dòng này.
MatrixFrog
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.