2011-12-19 36 views
29

Có chức năng tích hợp với chữ ký :: (Monad m) => m a -> a không?Cách trích xuất giá trị từ hành động đơn thuần

Hoogle nói rằng không có chức năng như vậy.

Bạn có thể giải thích lý do không?

+2

Liên quan: [lấy ra một giá trị ra khỏi một đơn nguyên? haskell] (http: // stackoverflow.com/questions/7314789/take-out-a-giá trị-out-of-a-monad-haskell) – Jan

+14

Không có, nhưng có một chức năng mà biến chức năng mong đợi một 'a' vào chức năng mong đợi một' ma' : '(= <<) :: Monad m => (a -> mb) -> (ma -> mb)'. Đảo ngược mong đợi của bạn, và bạn sẽ ổn thôi. =) –

+3

Tương tự như những gì Daniel Wagner đã nói, 'liftM :: Monad m => (a -> b) -> (ma -> mb)' cho phép một hàm "thông thường" chấp nhận một giá trị đơn điệu làm đầu vào, nhưng đổi lại nó phải xuất ra một giá trị đơn thuần hơn là giá trị "thông thường". –

Trả lời

41

Một đơn nguyên chỉ cung cấp hai chức năng:

return :: Monad m => a -> m a 
(>>=) :: Monad m => m a -> (a -> m b) -> m b 

Cả hai quay trở lại một cái gì đó kiểu m a, vì vậy không có cách nào để kết hợp chúng trong bất kỳ cách nào để có được một chức năng của loại Monad m => m a -> a. Để làm điều đó, bạn sẽ cần nhiều hơn hai hàm này, vì vậy bạn cần biết thêm về m hơn là nó là một đơn nguyên.

Ví dụ: đơn vị Identity đơn lẻ có runIdentity :: Identity a -> a và một số đơn vị có chức năng tương tự nhưng không có cách nào để cung cấp tính năng này một cách tổng quát. Trong thực tế, không có khả năng "trốn thoát" từ đơn nguyên là điều cần thiết cho các monads như IO.

5

Bởi vì nó có thể không có ý nghĩa (thực sự, không không có ý nghĩa trong nhiều trường hợp).

Ví dụ, tôi có thể xác định một Parser Monad như thế này:

data Parser a = Parser (String ->[(a, String)]) 

Bây giờ hoàn toàn không có cách mặc định hợp lý để có được một String ra khỏi một Parser String. Trên thực tế, không có cách nào để có được một chuỗi trong số này chỉ với Monad.

21

Có lẽ là một câu trả lời tốt hơn thế này, nhưng có một cách để xem lý do tại sao bạn không thể có một loại (Monad m) => m a -> a là để xem xét một đơn nguyên null:

data Null a = Null 

instance Monad Null where 
    return a = Null 
    ma >>= f = Null 

Bây giờ (Monad m) => m a -> a nghĩa Null a -> a, tức là nhận được một cái gì đó ra từ hư không . Bạn không thể làm điều đó.

+4

Mặt khác, thực tế này không ngăn chặn 'fromJust' tồn tại. Nó trả về nội dung của 'Chỉ số' và đặt ra một ngoại lệ trong trường hợp 'Không có gì'. Đơn vị 'Null' của bạn có thể đơn giản là luôn đưa ra một ngoại lệ khi gọi chức năng mở rộng tưởng tượng như vậy. – Jan

+13

@Jan: Mặt khác không làm cho một trong hai chức năng trở thành một ý tưởng hay. 'fromJust' là khủng khiếp và sẽ tốt hơn nếu không tồn tại. –

+1

@ C.A.McCann Tôi +1 nhận xét của bạn một phần vì 'fromJust' là xấu ... nhưng chủ yếu là vì bạn đã sử dụng" tay kia ". –

12

Điều này không tồn tại vì Monad là mẫu cho bố cục, không phải là mẫu để phân hủy. Bạn luôn có thể đặt nhiều phần hơn cùng với giao diện mà nó định nghĩa. Nó không nói một điều về việc lấy bất cứ thứ gì ngoài.

Hỏi tại sao bạn không thể thực hiện điều gì đó giống như hỏi tại sao giao diện Iterator của Java không chứa phương pháp để thêm các phần tử vào những gì nó đang lặp lại. Nó không chỉ là giao diện Iterator.

Và đối số của bạn về các loại cụ thể có chức năng trích xuất theo cách tương tự. Một số triển khai cụ thể của Iterator có thể có chức năng add. Nhưng vì nó không phải là những gì Iterator s cho, sự hiện diện mà phương pháp trên một số trường hợp cụ thể là không liên quan.

Và sự hiện diện của fromJust cũng không liên quan. Nó không phải là một phần của hành vi Monad được thiết kế để mô tả. Những người khác đã đưa ra rất nhiều ví dụ về các loại mà không có giá trị cho extract để làm việc trên. Nhưng những loại đó vẫn hỗ trợ ngữ nghĩa dự định của Monad. Cái này quan trọng. Điều này có nghĩa là Monad là giao diện tổng quát hơn so với giao diện của bạn.

6

Có chức năng tích hợp với chữ ký :: (Monad m) => m a -> a không?

Nếu Hoogle nói không có ... thì có thể không, giả định định nghĩa của bạn là "được tích hợp" là "trong thư viện cơ sở".

Hoogle nói rằng không có chức năng như vậy. Bạn có thể giải thích lý do tại sao?

Thật dễ dàng, vì Hoogle không tìm thấy bất kỳ chức năng nào trong thư viện cơ sở khớp với chữ ký loại đó!

Nghiêm túc hơn, tôi cho rằng bạn đang yêu cầu giải thích đơn điệu. Các sự cố là an toàncó nghĩa là. (Xem thêm my previous thoughts on magicMonadUnwrap :: Monad m => m a -> a)

Giả sử tôi cho bạn biết tôi có một giá trị có loại [Int]. Vì chúng tôi biết rằng [] là một đơn nguyên, điều này tương tự như cho bạn biết rằng tôi có một giá trị có loại Monad m => m Int. Vì vậy, giả sử bạn muốn nhận được Int trong số đó [Int]. Vâng, bạn muốn có Int? Cái đầu tiên? Cái cuối cùng? Điều gì xảy ra nếu giá trị mà tôi đã nói với bạn về thực sự là một danh sách trống? Trong trường hợp đó, thậm chí không có Int để cung cấp cho bạn! Vì vậy, đối với danh sách, nó là không an toàn để thử và trích xuất một giá trị đơn lẻ như vậy. Ngay cả khi an toàn (danh sách không trống), bạn cần có một chức năng danh sách cụ thể (ví dụ: head) để làm rõ những gì bạn có nghĩa là bằng cách muốn f :: [Int] -> Int. Hy vọng rằng bạn có thể intuit từ đây rằng có nghĩa là của Monad m => m a -> a chỉ đơn giản là không được xác định rõ. Nó có thể giữ nhiều ý nghĩa cho cùng một đơn nguyên, hoặc nó có thể có nghĩa là hoàn toàn không có gì cả cho một số monads, và đôi khi, nó chỉ đơn giản là không an toàn.

+2

Tôi không thấy cách 'Monad m => m a -> a' thiếu ý nghĩa theo bất kỳ cách nào cũng không áp dụng cho' >> = 'hoặc' return' hoặc 'fail'. Bạn luôn có thể nói rằng "ý nghĩa" của hoạt động không được xác định rõ trước khi biết việc triển khai đầy đủ mà 'm' cung cấp cho sự bao gồm của nó trong lớp loại Monad. Đối với ví dụ danh sách của bạn, một hàm có kiểu 'Monad m => m a -> a' có thể rất có nghĩa là bất kỳ thứ gì bạn đề xuất - và * bất kỳ thứ gì trong số chúng * có thể * là hợp lệ. Bạn luôn có thể sử dụng 'newtype' để chỉnh sửa hành vi cho ứng dụng * của bạn, nhưng từ chối ngay cả cơ hội để làm điều đó có vẻ quá nghiêm trọng. – ely

8

Giả sử có một chức năng như:

extract :: Monad m => m a -> a 

Bây giờ bạn có thể viết một "chức năng" như thế này:

appendLine :: String -> String 
appendLine str = str ++ extract getLine 

Trừ chức năng extract được đảm bảo không bao giờ chấm dứt, điều này sẽ vi phạm referential minh bạch, bởi vì kết quả của appendLine "foo" sẽ (a) phụ thuộc vào một cái gì đó khác hơn là "foo", (b) đánh giá các giá trị khác nhau khi được đánh giá trong các ngữ cảnh khác nhau.

Hoặc trong các từ đơn giản hơn, nếu có một hoạt động thực sự hữu ích extract Haskell sẽ không hoàn toàn hoạt động.

+2

[unsafePerformIO] (http://hackage.haskell.org/package/base-4.7.0.1/docs/System-IO-Unsafe.html#v%3aunsafePerformIO) thực hiện điều này. – ely

0

Vâng, kỹ thuật có unsafePerformIO cho đơn nguyên IO. Tuy nhiên, như tên gọi chính nó cho thấy, chức năng này là xấu và bạn chỉ nên sử dụng nó nếu bạn thực sự biết bạn đang làm gì (và nếu bạn phải hỏi bạn có biết hay không thì bạn không)

+1

Ngoài ra còn có 'unsafeHead' cho danh sách monad ... (oh chờ đã, nó được gọi là' head' ... nhưng nó cũng tương tự, mặc dù không hoàn toàn mạnh mẽ, không an toàn.) –

+0

@DanBurton Trên thực tế, 'unsafePerformIO' là thường an toàn hơn; nó có thể khởi động tên lửa để phá hủy mặt trăng, nhưng ít nhất nó không làm cho chương trình của bạn bị sập. – jpaugh

Các vấn đề liên quan