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),您可以使用 traversesequence

具体来说,您的 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 的新类型(在我们的例子中 mEff (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