由于列表推导不允许这样做,我使用 monad 推导并定义了一个自定义 monad 进行了一些修改。结果是以下工作:
example :: [Int]
example = toList [1000 + n
| n <- fromList [0..]
, _ <- nonStopGuard (n > 1)
, let z = 10*n
, _ <- stopGuard (z < 42) ]
-- Output: [1002,1003,1004]
上面是一个普通的列表推导式,但有两种不同的守卫。 nonStopGuard 用作常规守卫,除了需要奇怪的语法。 stopGuard 反而做了更多的事情:一旦它变为假,它就会停止考虑先前生成器中的进一步选择(例如<-[0..])。
我写的小库如下图:
{-# LANGUAGE DeriveFunctor, MonadComprehensions #-}
import Control.Monad
import Control.Applicative
data F a = F [a] Bool
deriving (Functor, Show)
上面的Bool 是一个stop位,表示我们必须stop考虑进一步的选择。
instance Applicative F where pure = return; (<*>) = ap
instance Monad F where
return x = F [x] False
F [] s >>= _ = F [] s
F (x:xs) sx >>= f = F (ys ++ zs) (sx || sy || sz)
where
F ys sy = f x
F zs sz = if sy then F [] False else F xs sx >>= f
当f x 发出停止信号时,最后一个if 将丢弃xs 部分。
nonStopGuard :: Bool -> F ()
nonStopGuard True = F [()] False
nonStopGuard False = F [] False
普通的守卫永远不会发出停止的信号。它只提供一个或零个选择。
stopGuard :: Bool -> F ()
stopGuard True = F [()] False
stopGuard False = F [] True
一个停止守卫反而会在它变为假时发出停止信号。
fromList :: [a] -> F a
fromList xs = F xs False
toList :: F a -> [a]
toList (F xs _) = xs
最后一个警告:我不完全确定我的 monad 实例定义了一个实际的 monad,即它是否满足 monad 法则。
按照@icktoofay 的建议,我写了一些快速检查测试:
instance Arbitrary a => Arbitrary (F a) where
arbitrary = F <$> arbitrary <*> arbitrary
instance Show (a -> b) where
show _ = "function"
prop_monadRight :: F Int -> Bool
prop_monadRight m =
(m >>= return) == m
prop_monadLeft :: Int -> (Int -> F Int) -> Bool
prop_monadLeft x f =
(return x >>= f) == f x
prop_monadAssoc :: F Int -> (Int -> F Int) -> (Int -> F Int) -> Bool
prop_monadAssoc m f g =
((m >>= f) >>= g)
==
(m >>= (\x -> f x >>= g))
运行 100000 次测试没有发现反例。所以,很可能上面的F 是一个真正的monad。