Search code examples
haskellfunctional-programmingalternative-functor

Looking for generalisation of the `if p x then x else empty` construct


I have a couple of snippets which feel like they're doing the same thing, but I'm not entirely convinced there is a generalised construct to handle them both. In one place, I have

ensure :: (String -> Bool) -> String -> String
ensure p x =
    if p x then
        x
    else
        ""

This might in use look something like

ensure (/= "kim") "alex"    -- returns "alex"
ensure (/= "kim") "kim"     -- returns ""

in another, I have the very similar

ensure :: (a -> Bool) -> Maybe a -> Maybe a
ensure p maybeX = do
    x <- maybeX
    if p x then
        Just x
    else
        Nothing

This would instead look something like

ensure even 6     -- returns Just 6
ensure even 11    -- returns Nothing

Both are checking whether a value is correct according to some predicate, and if it's not they're returning a default "empty"-looking value. There is a slight difference though – which means the second function could be rewritten as

ensure :: (Maybe a -> Bool) -> Maybe a -> Maybe a
ensure p maybeX =
    if p x then
        x
    else
        Nothing

to make them more similar, putting the responsibility of "unwrapping" the Maybe on the predicate. With this new definition, both functions would fall under

ensure :: Alternative f => (f a -> Bool) -> f a -> f a
ensure p x =
    bool x empty (p x)

So, my question is,

Does this bool x empty (p x) exist in some form so I don't have to implement this function myself? The problem with inlining bool x empty (p x) is that in my case, both p and x are quite long.


Solution

  • Below are suggestions from the comments. One using Monoid, by 9000:

    ensure :: Monoid a => (a -> Bool) -> a -> a
    ensure p a = if p a then a else mempty
    

    Another using MonadPlus, by user3237465:

    ensure :: MonadPlus m => (a -> Bool) -> a -> m a
    ensure p = mfilter p . return
    

    And a variant of the second one that only requires Alternative, by Daniel Wagner:

    ensure :: Alternative f => (a -> Bool) -> a -> f a
    ensure p x = x <$ guard (p x)