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?
Totally ripping off of TomMD, I saw the
and . map
andor . map
and couldn't help but want to tweak it:These read nicely I think.
fAnd
: are all functions in the listTrue
whenx
is applied to them?fOr
: are any functions in the listTrue
whenx
is applied to them?fOr is an odd name choice, though. Certainly a good one to throw those imperative programmers for a loop. =)
This can also be done using Arrows:
&&&
(not related to&&
) splits the input;>>>
is arrow/category composition.Here's an example:
This works because functions --
->
-- are instances of Category and Arrow. Comparing the type signatures to Don'sliftA2
andliftM2
examples shows the similarities:Besides the currying, note that you can almost convert the first type into the second by substituting
cat a ---> f
andArrow ---> Applicative
(the other difference is thatsplit_combine
isn't limited to taking pure functions in its 1st argument; probably not important though).It's uglier if you always want two functions, but I think I'd generalize it:
If f1 and f2 are the same, then you can use 'on':
in base Data.Function
Typical usage:
(from Hoogle)
This has sortof been mentioned but in a more complex way. You could use applicative stuff.
For functions basically what it does is pass the same argument to a number of functions that you can combine at the end.
So you could implement it like this:
For each
<*>
in the chain then you get another function that applies to a and then you munge all the output together usingfmap
alternately written as<$>
. The reason this works with&&
is because it takes two arguments and we have 2 functions starred together. If there was an extra star in there and another check you'd have to write something like:check this out for more examples
On top of what Don said, the
liftA2/liftM2
versions may not be lazy enough:>
let a .&&. b = liftA2 (&&) a b in pure False .&&. undefined
*** Exception: Prelude.undefined
Woops!
So instead you might want a slightly different function. Note that this new function requires a
Monad
constraint --Applicative
is insufficient.>
let a *&&* b = a >>= \a' -> if a' then b else return a' in pure False *&&* undefined
False
That's better.
As for the answer that suggests the
on
function, this is for when the functions are the same but the arguments are different. In your given case, the functions are different but the arguments are the same. Here is your example altered so thaton
is an appropriate answer:(f x) && (f y)
which can be written:
on (&&) f x y
PS: the parentheses are unnecessary.