Những vấn đề lập trình nào Monads giải quyết? [đóng cửa]


14

Tôi đã đọc rất nhiều bài viết giải thích các đơn vị là gì, cách thức unitbindhoạt động, một số trong số chúng lao thẳng vào lý thuyết thể loại rất trừu tượng (ít nhất là đối với tôi) khiến đôi mắt chảy máu, một số bỏ qua điều đó hoàn toàn và chạm vào những điều tương tự kỳ lạ của burritos, hộp và những gì không.

Sau một vài tuần nghiên cứu và rất nhiều tế bào thần kinh chiên, (tôi nghĩ) tôi hiểu cách thức hoạt động của Monads. Nhưng vẫn còn một điều thoát khỏi sự hiểu biết của tôi, một điều mà ít bài đăng thực sự chạm đến (ngoại trừ IO và trạng thái):

TẠI SAO?

Tại sao Monads quan trọng? tại sao họ quan trọng đến vậy? Vấn đề họ đang giải quyết là gì? Những vấn đề đó chỉ có thể được giải quyết với Monads hay có một số cách khác?


3
Các chương trình rất lớn và phức tạp. Chúng ta cần cách để cấu trúc chúng. Có rất nhiều cách. Monads là một. Mũi tên là một. Functor là một. Đối tượng, Lớp học, Hàm, Phương thức, Mô-đun, Đặc điểm, Mixins, Gói, là một số thứ khác, Thật sự không rõ câu hỏi cụ thể của bạn là gì. Bạn đang hỏi tại sao chúng ta cần cấu trúc chương trình?
Jörg W Mittag

1
@ JörgWMittag: Tôi KHÔNG hỏi tại sao chúng ta cần cấu trúc các chương trình. Rõ ràng một vấn đề lớn được chia thành những vấn đề nhỏ hơn mà bạn có thể giải quyết và sau đó kết hợp để có được giải pháp cho vấn đề lớn. Tôi đang hỏi những vấn đề mà Monads giải quyết. Là nó? Cấu trúc mã? Có phải đó là tất cả những ồn ào về? Ví dụ, trong Haskell đây là cách bạn thực hiện I / O. Ở đó, đơn nguyên là một vở kịch giả vờ như một thứ gì đó thuần khiết trong khi thực tế là không. Câu hỏi của tôi là trong tiêu đề, tôi không biết làm thế nào để nói điều đó một cách rõ ràng hơn.
Dummy Me



1
@RobertHarvey: Tôi cũng tìm thấy hai liên kết đầu tiên bạn đã thêm nhưng giống như trong câu trả lời của những bài đăng đó và các bài đăng khác, các câu trả lời đi thẳng đến các đơn vị Có thể, Bang và IO, nhanh chóng chuyển đến các ví dụ hoặc mã và kết thúc bằng "có rất nhiều các vấn đề có thể được giải quyết bằng cách sử dụng Monads ". Đó là những loại vấn đề khác? Tôi đang tìm kiếm một câu trả lời ở cấp độ cao hơn, thay vì nhắc lại các ví dụ tương tự thực sự đi đến gốc rễ của các vấn đề (bất kể đó là gì) và giải thích cách Monads giải quyết chúng và tại sao đó là lựa chọn tốt nhất, trái ngược với việc sử dụng thứ khác. Cảm ơn rất nhiều cho các thông tin phản hồi mặc dù.
Dummy Me

Câu trả lời:


10

Bạn không cần đơn nguyên để giải quyết bất cứ điều gì. Họ chỉ làm cho một số điều đơn giản hơn. Rất nhiều người đi quá trừu tượng và lý thuyết khi giải thích các đơn nguyên. Hầu hết, các đơn nguyên là một mô hình xuất hiện nhiều lần trong lập trình. Bằng cách nhận ra mẫu đó, chúng ta có thể đơn giản hóa mã của mình và tránh thực hiện lại các chức năng nhất định.

Đối với Haskell, từ quan điểm cụ thể, thứ đơn vị dễ thấy nhất cho phép là ký hiệu . Danh sách hiểu trong Haskell và các ngôn ngữ khác cũng tận dụng lợi thế của các đơn nguyên. Bạn cũng có thể tạo các thư viện như Control.Monad .

Tất cả đều cung cấp các đơn giản hóa hữu ích và khi bạn đã triển khai nó cho một đơn nguyên, bạn sẽ tự động nhận được nó cho tất cả các đơn nguyên . Đây là một trong những lý do chính tại sao việc tái sử dụng mã dễ dàng hơn nhiều cho lập trình chức năng so với các mô hình khác.


2
Tôi thực sự muốn nói rằng thứ dễ thấy nhất mà các đơn vị haskell kích hoạt là một cách dễ dàng để có các hiệu ứng IO thực sự hoạt động theo cách bạn mong đợi. Tôi nghĩ rằng bất cứ ai tự hỏi những gì các đơn vị cung cấp cho chúng tôi nên thử Miranda . Miranda là ngôn ngữ mà Haskell được thiết kế để thay thế, và rất dễ học đối với bất kỳ ai có kinh nghiệm về Haskell. Sự khác biệt chính là nó không có IO đơn âm, khiến ngôn ngữ khó làm việc hơn nhiều so với Haskell cho bất kỳ dự án không tầm thường nào.
Jules

Vâng, IOđơn vị nổi bật nhất của Haskell , nhưng tôi đã không liệt kê các ví dụ về các đơn nguyên riêng lẻ. Tôi đã liệt kê các ví dụ về sự trừu tượng bao trùm mà họ kích hoạt. Tôi đã cố gắng hiểu tại sao, ví dụ, người đầu tiên nghĩ đến việc sử dụng một đơn vị cho IO sẽ nghĩ rằng đó là một ý tưởng tốt nói chung.
Karl Bielefeldt

1
@Jules: Hoặc nhìn vào lịch sử của Haskell. Trước Monads, các nhà thiết kế Haskell đã thử nghiệm Lazy Streams và Continuations làm cơ sở cho I / O và cả hai đều khó sử dụng.
Jörg W Mittag

5

Sẽ dễ hiểu hơn nếu bạn nhìn vào các đơn vị cụ thể và xem những vấn đề họ giải quyết. Ví dụ: trong Haskell:

  • IO: Cho phép IO được biểu diễn trong hệ thống loại, do đó bạn có thể xóa các hàm thuần túy khỏi các hàm thực hiện IO.

  • Danh sách: Cho phép bạn thực hiện việc hiểu danh sách và tính toán gật đầu.

  • Có thể: Một sự thay thế tốt hơn cho null và hỗ trợ một cái gì đó có thể so sánh với toán tử hợp nhất null của C #.

  • Parsec: Một DSL thuận tiện để viết trình phân tích cú pháp.

Vì vậy, thật dễ dàng (tôi hy vọng) để thấy sự hợp lý cho các đơn nguyên riêng lẻ, vì tất cả chúng đều khá hữu ích. Nhưng các vấn đề họ giải quyết cũng khá khác nhau và về mặt này, họ dường như không có nhiều điểm chung, ngoại trừ tất cả chúng đều liên quan đến một số logic cho các hoạt động xích. Monads rất hữu ích vì chúng cho phép bạn xây dựng nhiều công cụ như vậy.

Các ví dụ trên có thể được thực hiện mà không có đơn nguyên? Chắc chắn chúng có thể được thực hiện theo cách đặc biệt, nhưng có các đơn nguyên được tích hợp vào ngôn ngữ cho phép hỗ trợ ngôn ngữ trực tiếp nhưdo hoạt động với tất cả các đơn vị.


2

Một điều khiến nó khó hiểu là các chức năng "phổ biến" như bind<*>được định hướng. Nhưng để hiểu các khái niệm trước tiên, dễ dàng hơn để xem xét các chức năng khác. Cũng đáng lưu ý rằng các đơn vị nổi bật bởi vì họ hơi vượt trội so với các khái niệm kết nối khác. Vì vậy, tôi sẽ bắt đầu với functor thay thế.

Functor cung cấp một chức năng (theo ký hiệu Haskell) fmap :: (Functor f) => (a -> b) -> f a -> f b. Nói cách khác, bạn có một bối cảnh fmà bạn có thể nâng một hàm vào. Như bạn có thể tưởng tượng hầu hết mọi thứ là một functor. Danh sách, Có thể, Hoặc, chức năng, I / O, bộ dữ liệu, trình phân tích cú pháp ... Mỗi đại diện cho một bối cảnh trong đó một giá trị có thể xuất hiện. Vì vậy, bạn có thể viết các hàm cực kỳ linh hoạt hoạt động trong hầu hết mọi ngữ cảnh bằng cách sử dụng fmaphoặc biến thể nội tuyến của nó<$> .

Những thứ khác bạn muốn làm với bối cảnh? Bạn có thể muốn kết hợp hai bối cảnh. Vì vậy, bạn có thể muốn có được một khái quát về zip :: [a] -> [b] -> [(a,b)]ví dụ như thế này : pair :: (Monoidal f) => f a -> f b -> f (a,b).

Nhưng bởi vì nó thậm chí còn hữu ích hơn trong thực tế, các thư viện Haskell thay vào đó cung cấp Applicative, đó là sự kết hợp của FunctorMonoidal, và cũng Unitchỉ thêm rằng bạn thực sự có thể đặt các giá trị "bên trong" bối cảnh của mình unit.

Bạn có thể viết các hàm cực kỳ chung chung bằng cách chỉ ra ba điều sau về bối cảnh bạn đang làm việc.

Monadchỉ là một điều khác mà bạn có thể nêu lên trên đó. Điều tôi chưa từng đề cập trước đây là bạn đã có hai cách để kết hợp hai bối cảnh: Bạn không chỉ có pairchúng mà còn có thể xếp chồng chúng, ví dụ bạn có thể có một danh sách các danh sách. Trong ngữ cảnh I / O, một ví dụ sẽ là một hành động I / O có thể đọc các hành động I / O khác từ một tệp, vì vậy bạn sẽ có một loại FilePath -> IO (IO a). Làm thế nào chúng ta có thể thoát khỏi ngăn xếp đó để có được một chức năng thực thi IO a? Đó là nơi mà Monadsjoin đi vào, nó cho phép chúng ta kết hợp hai bối cảnh xếp chồng lên nhau của cùng loại. Điều tương tự cũng xảy ra với các trình phân tích cú pháp, Có thể vv Và bindchỉ là một cách thực tế hơn để sử dụngjoin

Vì vậy, một bối cảnh đơn điệu chỉ phải cung cấp bốn thứ và nó có thể được sử dụng với hầu hết tất cả các máy móc được phát triển cho I / O, cho các trình phân tích cú pháp, cho các lỗi, v.v.


1

Monads cho phép bạn thể hiện các tính toán không thuần túy khác nhau cũng như đơn giản hóa mã

  • tính toán trạng thái (get / set state qua monad)
  • I / O (ghi nhật ký, giao diện người dùng, tệp hoặc chỉ tạo / tiêu thụ danh sách X)
  • Ngoài ra, luồng điều khiển "phi tuyến tính" (nghĩa là ngoại lệ, có thể, v.v.)

Và, quan trọng là không ảnh hưởng đến các cấu trúc ngôn ngữ thuần túy và có được ngôn ngữ sạch hơn từ nó


2
Đúng - nhưng đó chỉ là một phần của những gì các đơn vị được sử dụng cho. Danh sách hiểu hoặc Maybekhông liên quan đến bất cứ điều gì bên ngoài.
JacquesB

Sẽ rất hữu ích nếu downvoters giải thích lý do tại sao họ đã làm như vậy. Tôi thấy không có gì sai với câu trả lời này.
Jules

@JacquesB, tuy nhiên, nhà nước.
Jules

1
Các loại thế giới, các loại duy nhất, các loại tuyến tính cũng cho phép bạn làm điều đó.
Jörg W Mittag

@Jules Liệt kê là một đơn nguyên không điều kiện là trạng thái? Bạn có thể làm rõ định nghĩa của bạn về "trạng thái" là gì?
Jack
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.