2015-11-08 2 views
6

Мне интересно, есть ли идиоматический способ написать управляющий код, похожий на прикованные if/else заявления на императивном языке в IO Monad.Связанные строки if/else в IO Monad

Таким образом, в языке, как Python, я обычно прямо что-то вроде этого:

if οs.path.isdir(fname): 
    # do whatever 
elif os.path.isfile(fname): 
    # ... 
else: 
    # ... 

Лучшее, что я мог придумать в Haskell является следующее:

isf <- doesFileExist path 
isd <- if isf then return False else doesDirectoryExist path 
case (isf, isd) of 
    (True, _)  -> return ... 
    (_,  True) -> return ... 
    _    -> return ... 

Что не так хорошо , и мне интересно, есть ли лучший способ написать такое.

Кроме того, чтобы подтвердить мое понимание: в случае с IO Monad требуется часть if isf в isd <- ..., если вы не хотите всегда выполнять обе операции. Мое предположение было бы в том, что в других Монадах (ленивых Монадах?) Это не понадобится, потому что isd будет оцениваться лениво.

Редактировать

На основе первых комментариев, я закончил со следующим:

firstMatchM :: (Monad m) => a -> [(a -> m Bool, b)] -> b -> m b 
firstMatchM arg [] def = return def 
firstMatchM arg ((check,x):xs) def = do 
    t <- check arg 
    if t then return x else firstMatchM arg xs def 

doFirstM :: (Monad m) => a -> [(a -> m Bool, a -> m b)] -> (a -> m b) -> m b 
doFirstM arg acts def = do 
    fm <- firstMatchM arg acts def 
    fm arg 

handlePath2 path = doFirstM path 
    [(\p -> doesFileExist p, 
     \p -> return "file" 
    ),(\p -> doesDirectoryExist p, 
     \p -> return "dir" 
    )] $ \p -> return "Error" 

который подобен второму предложению @ ци, немного я предпочитаю ifM, потому что это ближе к императивная версия.

+0

но даже в питона вы должны реорганизовать те;) – Carsten

+0

ОК, я укушу, Как? :) – ynimous

+0

Ну, вы можете сделать что-то вроде создания списка действий и использовать такие вещи, как 'foldM' или' forM' и т. Д., Чтобы получить нужный результат. Это будет обобщать на любое количество 'elif', хотя это было бы громоздким только для 3 случаев. То же самое было бы верно в python: 'test, action в tests_and_actions: if test (input): action (input)'. – Bakuriu

ответ

4

Если мы не хотим привлекать монады трансформаторов, основной вариант катится наши собственные монадическая if:

ifM :: Monad m => m Bool -> m a -> m a -> m a 
ifM act t e = do 
    b <- act 
    if b then t else e 

Тогда структура кода похожа на тот, в императивных языках:

test :: IO String 
test = ifM anAction (do 
      putStrLn "branch a" 
      return "a") 
     $ ifM otherAction (do 
      putStrLn "branch b" 
      return "b") 
     $ return "none" 

, где anAction, otherAction :: IO Bool.

В качестве альтернативы, использовать что-то вроде

ifChain :: [(IO Bool, IO a)] -> IO a -> IO a 
ifChain [] e = e 
ifChain ((g, a) : acts) e = do 
    b <- g 
    if b then a else ifChain acts e 
+0

@Bakuriu Право. – chi

+0

Мне нравится 'ifM' лучше, я думаю. Как бы вы это сделали с монадическими трансформаторами? – ynimous

+0

@ynimous Я попробовал трансформаторы монады ('MaybeT'), но мне не понравился результат: слишком много« лифтов и шаблонов ». Возможно, кто-то еще может опубликовать некоторый код, который на самом деле доступен для чтения. – chi