2011-12-23 20 views
9

Loại (>>=)Một chức năng tương tự như (>> =) nhưng mà trả về một đơn nguyên khác nhau

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

Tôi muốn có một hàm có kiểu:

(Monad m, Monad n) => m a -> (a -> n b) -> n b 

Chức năng này có thể là được sử dụng để chuỗi các monads khác nhau với nhau.

tôi phải đối mặt với vấn đề này khi tôi đã cố gắng để có được 3000 từ các đối số dòng lệnh -p 3000:

main = getArgs >>= (\args -> (elemIndex "-p" args) >>= (\id -> warpDebug (fromIntegral.read (args !! (id+1))) Ilm)) 

Đây rõ ràng sẽ không biên dịch vì getArgs trả về một IO [String]elemIndex trả về một Maybe Int. Một chức năng của loại trên có thể được sử dụng để thanh lịch giải quyết vấn đề này. Câu hỏi của tôi là:

  • Chức năng này đã được xác định chưa? (Hoogle không tìm thấy bất kỳ)
  • Nếu không có, có thể do một số lý do. Vậy lý do là gì? Đây có phải là hành vi không tốt? Tôi nghĩ rằng đó là một cách tốt hơn so với việc phải sử dụng một biểu thức trường hợp.

Trả lời

22

Chức năng như vậy không tồn tại. Trên thực tế, nếu bạn lấy n làm đơn sắc danh tính, nó sẽ cho phép bạn tạo một hàm m a -> a, không thể xác định rõ ràng cho tất cả các đơn vị.

Để giải quyết vấn đề chung về "sáng tác" hai đơn vị, bạn có thể xem xét monad transformers.

Tuy nhiên, có vẻ như quá mức cần thiết để sử dụng máy biến áp đơn lẻ trong ví dụ của bạn. Bạn có thể chỉ định nghĩa một hàm [String] -> Maybe Args (đối với một số kiểu tùy chỉnh Args - ví dụ Int trong ví dụ) xử lý đối số dòng lệnh, sau đó khớp mẫu trên kết quả (hoặc sử dụng maybe) từ trong đơn vị IO.

7

Chức năng này không tồn tại vì nó sẽ không có ý nghĩa đối với tất cả các monads. Về cơ bản nó tương đương với chức năng giải nén đơn lẻ Monad m => m a -> a - khác biệt duy nhất là bạn ngay lập tức đặt nó vào một đơn nguyên khác.

Lý do hàm này không được xác định cho tất cả các đơn vị là vì nó không có ý nghĩa đối với một số trong số đó. Ví dụ: lấy Maybe: cách duy nhất để giải nén nó là ném một lỗi nếu bạn có Nothing và lỗi thời gian chạy được xem xét. Một ví dụ cực đoan hơn sẽ là IO - sử dụng một hàm có thể "giải nén" các giá trị IO sẽ dẫn đến hành vi kỳ lạ và có khả năng không xác định.

Vì vậy, bạn không có chức năng như vậy nói chung. Tuy nhiên, rất nhiều mono cụ thể do đi kèm với các chức năng như vậy. Một ví dụ tuyệt vời là runST; đây thực sự là một cách an toàn để đối phó với nhà nước. Bạn thực sự là do có các chức năng như vậy cho cả hai MaybeIO (fromJustunsafePerformIO tương ứng), nhưng chúng có các vấn đề tôi nêu ở trên và bạn nên tránh chúng.

Giải pháp cho vấn đề của bạn, sau đó, là để xem liệu có tồn tại một chức năng cho bất kỳ monads nào bạn đang xử lý hay không. Nếu có, kiểm tra bất kỳ cạm bẫy tiềm năng nào - nó có tạo ra các lỗi thời gian chạy hoặc gây ra hành vi kỳ lạ không?

Trong trường hợp của bạn, nếu bạn là hoàn toàn chắc chắn rằng Maybe không bao giờ là Nothing, hãy sử dụng fromJust. Tuy nhiên, đây không phải là cách thực hành tốt, vì vậy bạn chỉ nên dính vào mẫu khớp với giá trị trong số Maybe.

2

Câu trả lời tùy thuộc vào việc bạn có cần sử dụng các ca khúc Có thể và IO cùng nhau hoặc riêng rẽ.

Nếu bạn cần sử dụng chúng cùng nhau - câu trả lời là bạn cần phải soạn các đơn vị IOMaybe bằng cách xây dựng một bộ biến áp đơn nguyên có chứa biến thế IO monad và biến thể đơn nguyên có lẽ.

Nếu bạn cần chúng riêng biệt, sau đó là một giải pháp đơn giản hoạt động:

import System.Environment 
import Data.List 

main = getArgs >>= (\args -> return (elemIndex "-p" args 
    >>= \y -> return $ y + 900) >>= print) 

Lưu ý return. Vì vậy, bạn có Maybe đơn nguyên trong các parentnes bên trong (giữa elemIndex900), nhưng không phải IO. Tức là, bạn không thể thực hiện các tác vụ IO trước khi rời khỏi Maybe đơn nguyên, như tôi đã trình bày với bản in.

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