我有以下代码,但我觉得它太丑陋而且势在必行。有人会说它更具功能吗? (我和MaybeT混淆但是无法使它工作)也欢迎应用答案。
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
编辑:一个后续问题:如果pred和f都在IO中返回结果会怎么样(我应该把它拆分成一个单独的问题吗?)
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
答案 0 :(得分:26)
我首先从IO
monad中取出逻辑。然后您的函数可以写为
result :: IO (Maybe String)
result = foo <$> getString
foo :: String -> Maybe String
foo s | pred s = Just (f s)
| otherwise = Nothing
您可以使用一些花哨的组合器以不同的方式编写foo
,但我不认为这是必要的。最重要的是让你的逻辑超出IO
,以便更容易测试。
答案 1 :(得分:10)
这是一个很好的小组合器:
ensure :: MonadPlus m => (a -> Bool) -> (a -> m a)
ensure p x = guard (p x) >> return x
现在我们可以编写一个纯函数来检查你的谓词并在适当的时候应用f
:
process :: String -> Maybe String
process = fmap f . ensure pred
将此提升为IO
操作只是另一个fmap
:
result = fmap process getString
就个人而言,我可能会内联process
,并以这种方式写下来:
result = fmap (fmap f . ensure pred) getString
...这是对正在发生的事情的一个相对清晰的描述。
答案 2 :(得分:9)
代码的明显转换是return
操作的因素:
result = do
s <- getString
return $ if pred s
then Just (f s)
else Nothing
这使得模式更加明显:
result = liftM g getString
g s | pred s = Just (f s)
| otherwise = Nothing
通过从外部应用f
,我们可以使下一个模式显而易见:
g s = liftM f $ if pred s then Just s else Nothing
让我们重新加入if
块:
g = liftM f . mfilter pred . return
总结:
result = liftM (liftM f . mfilter pred . return) getString
答案 3 :(得分:5)
import Control.Monad
result = getString >>= (return . fmap f . (mfilter pred . Just) )
答案 4 :(得分:3)
你不能轻易摆脱笨重的if-then-else
,但你可以逃脱冗余的returns
:
import Control.Monad
result :: IO (Maybe String)
result = go <$> getString where
go s | pred s = Just $ f s
| otherwise = Nothing