Search code examples
haskellcombinatorspointfree

In Haskell performing `and` and `or` for boolean functions


I just wrote the following two functions:

fand :: (a -> Bool) -> (a -> Bool) -> a -> Bool
fand f1 f2 x = (f1 x) && (f2 x)

f_or :: (a -> Bool) -> (a -> Bool) -> a -> Bool
f_or f1 f2 x = (f1 x) || (f2 x)

They might be used to combined the values of two boolean functions such as:

import Text.ParserCombinators.Parsec
import Data.Char

nameChar = satisfy (isLetter `f_or` isDigit)

After looking at these two functions, I came to the realization that they are very useful. so much so that I now suspect that they are either included in the standard library, or more likely that there is a clean way to do this using existing functions.

What was the "right" way to do this?


Solution

  • One simplification,

    f_and = liftM2 (&&)
    f_or  = liftM2 (||)
    

    or

          = liftA2 (&&)         
          = liftA2 (||)
    

    in the ((->) r) applicative functor.


    Applicative version

    Why? We have:

    instance Applicative ((->) a) where
        (<*>) f g x = f x (g x)
    
    liftA2 f a b = f <$> a <*> b
    
    (<$>) = fmap
    
    instance Functor ((->) r) where
        fmap = (.)
    

    So:

      \f g -> liftA2 (&&) f g
    = \f g -> (&&) <$> f <*> g          -- defn of liftA2
    = \f g -> ((&&) . f) <*> g          -- defn of <$>
    = \f g x -> (((&&) . f) x) (g x)    -- defn of <*> - (.) f g = \x -> f (g x)
    = \f g x -> ((&&) (f x)) (g x)      -- defn of (.)
    = \f g x -> (f x) && (g x)          -- infix (&&)
    

    Monad version

    Or for liftM2, we have:

    instance Monad ((->) r) where
        return = const
        f >>= k = \ r -> k (f r) r
    

    so:

      \f g -> liftM2 (&&) f g
    = \f g -> do { x1 <- f; x2 <- g; return ((&&) x1 x2) }               -- defn of liftM2
    = \f g -> f >>= \x1 -> g >>= \x2 -> return ((&&) x1 x2)              -- by do notation
    = \f g -> (\r -> (\x1 -> g >>= \x2 -> return ((&&) x1 x2)) (f r) r)  -- defn of (>>=)
    = \f g -> (\r -> (\x1 -> g >>= \x2 -> const ((&&) x1 x2)) (f r) r)   -- defn of return
    = \f g -> (\r -> (\x1 ->
                   (\r -> (\x2 -> const ((&&) x1 x2)) (g r) r)) (f r) r) -- defn of (>>=)
    = \f g x -> (\r -> (\x2 -> const ((&&) (f x) x2)) (g r) r) x         -- beta reduce
    = \f g x -> (\x2 -> const ((&&) (f x) x2)) (g x) x                   -- beta reduce
    = \f g x -> const ((&&) (f x) (g x)) x                               -- beta reduce
    = \f g x -> ((&&) (f x) (g x))                                       -- defn of const
    = \f g x -> (f x) && (g x)                                           -- inline (&&)