我们可以在这里使用绑定函数:
import Data.Bool(bool)
import Control.Monad(liftM2)
fixedM :: (Eq a, Monad m) => (a -> m a) -> a -> m a
fixedM f = go
where go x = f x >>= (liftM2 bool go pure <*> (x ==))
更详细的实现是:
fixedM :: (Eq a, Monad m) => (a -> m a) -> a -> m a
fixedM f x = do
x' <- f x
if x == x'
then pure x'
else fixedM f x'
x'
因此,我们首先计算f x
。如果f x
返回Just x'
,那么我们继续。如果f x
return Nothing
,则fixedM
也将返回Nothing
。x
然后我们与比较x'
。如果两者相等,我们返回pure x'
,否则我们递归 on fixedM f x'
。
或者,我们可以使用模式匹配,尽管这基本上使绑定运算符显式(并且仅适用于 a Maybe
):
import Control.Monad(ap)
fixedM :: Eq a => (a -> Maybe a) -> a -> Maybe a
fixedM f = ap go f
where go x (Just x') | x == x' = go x' (f x')
| otherwise = Just x'
go _ _ = Nothing
我们可以通过使用模式守卫使其更紧凑:
fixedM :: Eq a => (a -> Maybe a) -> a -> Maybe a
fixedM f = go
where go x | Just x' <- f x = bool (go x) (Just x) (x == x')
| otherwise = Nothing