Understanding filterM

后端 未结 2 1281
情歌与酒
情歌与酒 2021-02-18 23:57

Consider

filterM (\\x -> [True, False]) [1, 2, 3]

I just cannot understand the magic that Haskell does with this filterM use ca

2条回答
  •  一向
    一向 (楼主)
    2021-02-19 00:38

    This is pretty straightforward, after we've put it all down on paper (someone smart once gave this advice: don't try doing it all in your head, put it all on paper!):

    filterM          :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
    filterM _ []     =  return []
    filterM p (x:xs) =  do { flg <- p x
                           ; ys  <- filterM p xs
                           ; return (if flg then x:ys else ys) }
    
    -- filterM (\x -> [True, False]) [1, 2, 3]
    g [x,y,z] = filterM (\x -> [True, False]) (x:[y,z])
              = do {
                     flg <- (\x -> [True, False]) x
                   ; ys  <- g [y,z]
                   ; return ([x | flg] ++ ys) }
             = do {
                    flg <- [True, False]               -- no `x` here!
                  ; ys  <- do { flg2 <- (\x -> [True, False]) y
                              ; zs  <- g [z]
                              ; return ([y | flg2] ++ zs) }
                  ; return ([x | flg] ++ ys) }
             = do {
                    flg  <- [True, False]
                  ; flg2 <- [True, False]
                  ; zs   <- do { flg3 <- (\x -> [True, False]) z
                               ; s  <- g []
                               ; return ([z | flg3] ++ s) }
                  ; return ([x | flg] ++ [y | flg] ++ zs) }
             = do {
                    flg  <- [True, False]
                  ; flg2 <- [True, False]
                  ; flg3 <- [True, False]
                  ; s    <- return []
                  ; return ([x | flg] ++ [y | flg2] ++ [z | flg3] ++ s) }
    

    The unnesting of the nested do blocks follows from the Monad laws.

    Thus, in pseudocode:

        filterM (\x -> [True, False]) [1, 2, 3]
        =
          for flg in [True, False]:    -- x=1
              for flg2 in [True, False]:     -- y=2
                  for flg3 in [True, False]:     -- z=3
                      yield ([1 | flg] ++ [2 | flg2] ++ [3 | flg3])
    

提交回复
热议问题