6

I'm searching for something like

liftPredMaybe :: (a -> Bool) -> a -> Maybe a
liftPredMaybe p a
  | p a = Just a
  | otherwise = Nothing

Is there such a function in Haskell already?

Michiel Borkent
  • 34,228
  • 15
  • 86
  • 149

2 Answers2

9

Not quite a ready-made solution, but with guard (from Control.Monad) and (<$) (from Data.Functor) we can write:

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

(Thanks to Daniel Wagner for suggesting a nice name for this function.)

A more pointfree spelling of dubious taste is \p -> (<$) <*> guard . p.

duplode
  • 33,731
  • 7
  • 79
  • 150
  • 1
    This is my preferred spelling, and I've frequently included it in projects. I like the name `ensure`, because it fits well with the "0-1 nondeterminism" interpretation of `Maybe` and with the "n-way nondeterminism" interpretation of `[]`. – Daniel Wagner Mar 14 '18 at 22:38
  • @DanielWagner `ensure` is a very good name. One nice little thing about this spelling is that `guard @Maybe` is one half of the isomorphism between `Bool` and `Maybe ()`. – duplode Mar 15 '18 at 02:06
  • It is strange to call those "golf spellings" when the "ungolfed" spelling, `\p a -> a <$ guard (p a)`, is as short or shorter (and more readable by far!). – Daniel Wagner Mar 15 '18 at 15:17
  • @DanielWagner Good point... I have changed the wording a bit. – duplode Mar 15 '18 at 16:54
  • @DanielWagner FWIW, I was just told that this combinator exists in *protolude*, [which calls it `guarded`](https://hackage.haskell.org/package/protolude-0.2.2/docs/Protolude.html#v:guarded). – duplode Jun 08 '18 at 16:17
  • A very straightforward implementation is `\p a -> if p a then pure a else empty`. It can be spun into a prettier golf spelling, namely `liftA2 (bool empty) pure`. – duplode May 06 '19 at 23:42
6

One way to compose it is like this, using Control.Monad:

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

Another alternative is to use Data.Foldable:

liftPredF :: (a -> Bool) -> a -> Maybe a
liftPredF f = find f . pure

This is, however, less general, so I'd lean towards favouring the MonadPlus-based implementation.

In both cases, though, the idea is to first lift a 'naked' value into a container using either pure or return, and then apply a filter. Often, you don't need to declare an actual function for this; instead, you can just inline the composition where you need it.

Examples:

Prelude Control.Monad> liftPredMaybe even 42 :: Maybe Integer
Just 42
Prelude Control.Monad> liftPredMaybe (even . length) "foo" :: Maybe String
Nothing
Prelude Control.Monad> liftPredMaybe (odd . length) "foo" :: Maybe String
Just "foo"
duplode
  • 33,731
  • 7
  • 79
  • 150
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • [1/2] (1) You'll probably want to replace `pure` in `liftPredF` with a function for a concrete functor (e.g. `(:[])` or `Just`). As written, the compiler rejects it due to type ambiguity unless `ExtendedDefaultingRules` is enabled (or [if you are in GHCi](https://downloads.haskell.org/~ghc/8.4.1/docs/html/users_guide/ghci.html#type-defaulting-in-ghci), which enables it by default).(2) By the way, thanks for giving me one more example of the `Foldable` instance for `Maybe` being potentially useful: `find @Maybe :: (a -> Bool) -> Maybe a -> Maybe a` – duplode Mar 16 '18 at 10:01
  • [2/2] (3) If one is willing to bring in [`Data.Witherable`](https://hackage.haskell.org/package/witherable-0.2/docs/Data-Witherable.html), `liftPredMaybe f = filter f . Just` becomes possible. – duplode Mar 16 '18 at 10:02