2011-10-07 26 views
14

Tôi có mã sau nhưng tôi cảm thấy nó quá xấu và bắt buộc. Có ai nói lại nó để có chức năng hơn không? (Tôi sai lầm với MaybeT nhưng không thể làm cho nó hoạt động) Câu trả lời áp dụng chào đón là tốt.IO và Có lẽ tương tác đơn lẻ

getString :: IO String 

pred :: String -> Bool 

f :: String -> String 

result :: IO (Maybe String) 
result = do 
    s <- getString 
    if pred s 
    then return $ Just $ f s 
    else return Nothing 

EDIT: Một câu hỏi tiếp theo: những gì nếu cả hai pred và e cũng đưa ra kết quả trong vòng IO (nên tôi chia này ra thành một câu hỏi riêng biệt?)

getString :: IO String 

pred :: String -> IO Bool 

f :: String -> IO String 

result :: IO (Maybe String) 
result = do 
    s <- getString 
    b <- pred s 
    if b 
    then Just <$> f s 
    else return Nothing 

Trả lời

25

tôi sẽ bắt đầu bằng cách lấy logic ở đây ra khỏi đơn vị IO. Chức năng của bạn có thể được viết như

result :: IO (Maybe String) 
result = foo <$> getString 

foo :: String -> Maybe String 
foo s | pred s = Just (f s) 
     | otherwise = Nothing 

Bạn có thể viết foo theo những cách khác nhau sử dụng một số combinators ưa thích, nhưng tôi không nghĩ đó là cần thiết ở đây. Điều quan trọng nhất là để có được logic của bạn ra khỏi IO để nó dễ dàng hơn để kiểm tra.

+6

+1 Nó không thể bị từ chối mà nhận được logic ra khỏi 'IO' là quan trọng hơn có một giải pháp một dòng ưa thích. – leftaroundabout

5
import Control.Monad 

result = getString >>= (return . fmap f . (mfilter pred . Just)) 
+0

Lưu ý rằng 'mfilter' thay thế if-then-else của bạn. – fuz

+2

'result = (f <$> mfilter pred.Chỉ cần) <$> getString' – fuz

+0

@FUZxxl không nhập kiểm tra. 'mfilter pred. Chỉ cần :: a -> Có thể a', nhưng 'f <$> x' đang chờ' x :: Có thể a'. Bạn cần một số toán tử thành phần ứng dụng 'f <.> g = \ x -> f <$> g x', nếu được ưu tiên thấp hơn' .' cho phép 'result = (f <.> mfilter pred. Chỉ cần) <$> getString'. Ngoài ra, thay thế 'Chỉ' bằng' trả về' cho phép bất kỳ 'Monad' nào được sử dụng. – pat

3

Bạn không thể dễ dàng nhận được ngay với các clunky if-then-else, nhưng bạn có thể nhận được ngay với các thừa returns:

import Control.Monad 

result :: IO (Maybe String) 
result = go <$> getString where 
    go s | pred s = Just $ f s 
     | otherwise = Nothing 
+0

http://blog.n-sch.de/2010/11/27/rebindable-if-then-else-expressions/ –

9

Việc chuyển đổi rõ ràng từ mã của bạn là yếu tố các return hoạt động:

result = do 
    s <- getString 
    return $ if pred s 
      then Just (f s) 
      else Nothing 

Điều này làm cho mô hình rõ ràng hơn:

result = liftM g getString 
g s | pred s = Just (f s) 
    | otherwise = Nothing 

Bằng cách áp dụng f từ bên ngoài, chúng ta có thể làm cho các mô hình tiếp theo rõ ràng:

g s = liftM f $ if pred s then Just s else Nothing 

nào cho phép chúng ta remplace các if khối:

g = liftM f . mfilter pred . return 

Tóm tắt:

result = liftM (liftM f . mfilter pred . return) getString 
10

Dưới đây là một bộ tổ hợp nhỏ đẹp:

ensure :: MonadPlus m => (a -> Bool) -> (a -> m a) 
ensure p x = guard (p x) >> return x 

Bây giờ chúng ta có thể viết một hàm thuần túy mà kiểm tra ngữ của bạn và áp dụng f khi thích hợp:

process :: String -> Maybe String 
process = fmap f . ensure pred 

Nâng này để một hành động IO chỉ đơn giản là một fmap:

result = fmap process getString 

Cá nhân, tôi muốn có thể là nội tuyến process và viết theo cách này thay thế:

result = fmap (fmap f . ensure pred) getString 

... mô tả tương đối rõ ràng về những gì đang xảy ra.

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