I have two predicates
interface Foo {}
interface Bar {}
declare const isFoo: (a:unknown):a is Foo
declare const isBar: (a:unknown):a is Bar
What is the functional way to combine two predicates to create a new predicate (for simplicity, let's assume it's a => isFoo(a) && isBar(a)
?
With fp-ts
, I initially thought I could fold(monoidAll)([isFoo, isBar])
, but fold
expects the array to be of booleans, not of functions that evaluate to boolean.
This works
import { monoid as M, function as F, apply as A, identity as I, reader as R } from 'fp-ts'
interface Foo{}
interface Bar{}
declare const isFoo:(a:unknown) => a is Foo
declare const isBar:(a:unknown) => a is Bar
const isFooAndBar = F.pipe(A.sequenceT(R.reader)(isFoo, isBar), R.map(M.fold(M.monoidAll)))
But boy howdy is that convoluted. I thought there could be another way. I ended up writing my own monoid that takes two predicates and combines them, calling it monoidPredicateAll
:
const monoidPredicateAll:M.Monoid<Predicate<unknown>> = {
empty: ()=>true,
concat: (x,y) => _ => x(_) && y(_)
}
Is there a canonical FP way of combining two predicates? I know I could do something like
xs.filter(x => isFoo(x) && isBar(x))
But it can get complicated with more predicates, and re-using a monoid makes it less likely I'll do a typo like isFoo(x) || isBar(x) && isBaz(x)
when I meant all &&
(and that's where a xs.filter(fold(monoidPredicateAll)(isFoo,isBar,isBaz))
would help out.
I found a discussion about this on SO, but it was about Java and a built-in Predicate
type, so didn't directly address my question.
Yes, I'm overthinking this :)
I ended up doing this:
Then I could do