Purescript 效果:防止深度嵌套
Purescript Effects: Prevent deep nesting
目前,main
具有以下签名:
main :: Eff (dom :: DOM) (Maybe (Eff (dom :: DOM) (Maybe Element)))
我想要以下内容:
main :: Eff (dom :: DOM) (Maybe Element)
findItem :: forall e. IsElement e => e -> Eff (dom :: DOM) (Maybe Element)
findItem e = getElementsByClassName "thing" (toElement e) >>= (item 0)
-- main :: Eff (dom :: DOM) (Maybe Element)
main = (map findItem) <$> (window >>= document >>= body)
最好的方法是什么?
当然,我可以这样做:
findItem :: forall e. IsElement e => Maybe e -> Eff (dom :: DOM) (Maybe Element)
findItem (Just e) = getElementsByClassName "findItemEdit" (toElement e) >>= (item 0)
findItem Nothing = pure Nothing
main :: Eff (dom :: DOM) (Maybe Element)
main = findItem =<< body =<< document =<< window
但我不想在 findItem
函数中处理 Maybe
。
我能想到的最好办法是使用 Maybe
变质,这实际上是从第一个示例中获取 findItem
并将其转换为第二个示例中的 findItem
示例:
main :: Eff (dom :: DOM) (Maybe Element)
main = maybe (pure Nothing) findItem =<< body =<< document =<< window
使用traverse :: forall a b eff. (a -> Eff eff b) -> Maybe a -> Eff eff (Maybe b)
。该功能更通用,但这就是您想要使用它的方式。每当您发现自己想要 "swap" 两种类型时,例如 Maybe a -> Eff eff (Maybe a)
或 Maybe (List a) -> List (Maybe a)
,您可以使用 traverse
或 sequence
。
具体来说,您的 main
看起来像:
main :: Eff (dom :: DOM) (Maybe Element)
main = do
mdocBody <- window >>= document >>= body
mmitem <- traverse findItem mdocBody
-- mmitem has type `Maybe (Maybe Element)`
-- we can use `join` to collapse
pure (join mmitem)
或者,作为自由点,
main :: Eff (dom :: DOM) (Maybe Element)
main =
window >>= document >>= body
>>= map join <<< traverse findItem
您可以使用 MaybeT monad 转换器:
main = runMaybeT do
b <- MaybeT (window >>= document >>= body)
MaybeT (findItem b)
MaybeT
是包裹在 monad m
中的 Maybe
的新类型(在我们的例子中 m
是 Eff (dom :: DOM)
):
newtype MaybeT m a = MaybeT (m (Maybe a))
及其 bind
以您喜欢的方式处理嵌套和取消嵌套:
bind (MaybeT x) f = MaybeT do
x >>= case _ of
Nothing -> pure Nothing
Just y -> case f y of MaybeT m -> m
目前,main
具有以下签名:
main :: Eff (dom :: DOM) (Maybe (Eff (dom :: DOM) (Maybe Element)))
我想要以下内容:
main :: Eff (dom :: DOM) (Maybe Element)
findItem :: forall e. IsElement e => e -> Eff (dom :: DOM) (Maybe Element)
findItem e = getElementsByClassName "thing" (toElement e) >>= (item 0)
-- main :: Eff (dom :: DOM) (Maybe Element)
main = (map findItem) <$> (window >>= document >>= body)
最好的方法是什么?
当然,我可以这样做:
findItem :: forall e. IsElement e => Maybe e -> Eff (dom :: DOM) (Maybe Element)
findItem (Just e) = getElementsByClassName "findItemEdit" (toElement e) >>= (item 0)
findItem Nothing = pure Nothing
main :: Eff (dom :: DOM) (Maybe Element)
main = findItem =<< body =<< document =<< window
但我不想在 findItem
函数中处理 Maybe
。
我能想到的最好办法是使用 Maybe
变质,这实际上是从第一个示例中获取 findItem
并将其转换为第二个示例中的 findItem
示例:
main :: Eff (dom :: DOM) (Maybe Element)
main = maybe (pure Nothing) findItem =<< body =<< document =<< window
使用traverse :: forall a b eff. (a -> Eff eff b) -> Maybe a -> Eff eff (Maybe b)
。该功能更通用,但这就是您想要使用它的方式。每当您发现自己想要 "swap" 两种类型时,例如 Maybe a -> Eff eff (Maybe a)
或 Maybe (List a) -> List (Maybe a)
,您可以使用 traverse
或 sequence
。
具体来说,您的 main
看起来像:
main :: Eff (dom :: DOM) (Maybe Element)
main = do
mdocBody <- window >>= document >>= body
mmitem <- traverse findItem mdocBody
-- mmitem has type `Maybe (Maybe Element)`
-- we can use `join` to collapse
pure (join mmitem)
或者,作为自由点,
main :: Eff (dom :: DOM) (Maybe Element)
main =
window >>= document >>= body
>>= map join <<< traverse findItem
您可以使用 MaybeT monad 转换器:
main = runMaybeT do
b <- MaybeT (window >>= document >>= body)
MaybeT (findItem b)
MaybeT
是包裹在 monad m
中的 Maybe
的新类型(在我们的例子中 m
是 Eff (dom :: DOM)
):
newtype MaybeT m a = MaybeT (m (Maybe a))
及其 bind
以您喜欢的方式处理嵌套和取消嵌套:
bind (MaybeT x) f = MaybeT do
x >>= case _ of
Nothing -> pure Nothing
Just y -> case f y of MaybeT m -> m