我不认为 GHC 足够聪明来合并这两个遍历,或者,通常情况下,GHC 可能足够聪明,但是在某些情况下你不想要这种行为,所以 GHC 不会这样做。
这是我将如何使用幺半群和foldMap.
import Data.Monoid
import Data.Foldable
首先,这是使用幺半群编写specialwith的方法。foldMapFirst
specialF :: a -> First a
specialF a = First $ if isSpecial a then Just a else Nothing
special :: [a] -> a
special as = let (First (Just s)) = foldMap specialF as in s
与 specialOrNormal 类似,使用列表 monoid。
specialOrNormalF :: a -> [a]
specialOrNormalF a = if isSpecialOrNormal a then [a] else []
specialOrNormal :: [a] -> [a]
specialOrNormal = foldMap specialOrNormalF
关于幺半群的一个巧妙之处在于,一个幺半群元组也是一个幺半群,这使得合并这些折叠变得容易:
findElements :: [a] -> (a, [a])
findElements bigList =
  let (First (Just s), son) = 
    foldMap (\a -> (specialF a, specialOrNormalF a)) bigList
  in (s, son)
如果你喜欢无点代码,你可以这样写:
findElements :: [a] -> (a, [a])
findElements = 
  first (fromJust . getFirst) . 
  foldMap 
    (   First . mfilter isSpecial . return 
    &&& mfilter isSpecialOrNormal . return
    )