In Haskell, we have the filterM
function. The source code for it is:
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
filterM _ [] = return []
filterM p (x:xs) = do
flg <- p x
ys <- filterM p xs
return (if flg then x:ys else ys)
Translating from do notation:
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
filterM _ [] = return []
filterM p (x:xs) = p x >>= \flg ->
filterM p xs >>= \ys ->
return(if flg then x:ys else ys)
To the best of my understanding, >>=
on lists in Haskell and SelectMany
on IEnumerable
in C# are the same operation and so, this code should work just fine:
public static IEnumerable<IEnumerable<A>> WhereM<A>(this IEnumerable<A> list, Func<A, IEnumerable<bool>> predicate)
{
// Like Haskells null
if (list.Null())
{
return new List<List<A>> {new List<A>()};
}
else
{
var x = list.First();
var xs = list.Tail(); // Like Haskells tail
return new List<IEnumerable<A>>
{
predicate(x).SelectMany(flg => xs.WhereM(predicate).SelectMany(ys =>
{
if (flg)
{
return (new List<A> {x}).Concat(ys);
}
else
{
return ys;
}
}))
};
}
}
But it doesn't work. Can anyone point me to what's wrong here?
My C# is a bit rusty, but it looks like your base case is wrong. You're returning the equivalent of []
(an empty list) while the Haskell version returns [[]]
(a list containing an empty list).
Your recursive case has the same problem. For example, in the else
branch the Haskell version returns [ys]
while your version returns ys
. Remember that return
in the list monad makes a single element list and has nothing to do with the return
keyword in C#.
It looks like your C# code is equivalent to:
filterM :: (a -> [Bool]) -> [a] -> [[a]]
filterM _ [] = return []
filterM p (x:xs) =
return $
p x >>= \flg ->
filterM p xs >>= \ys ->
if flg then x:ys else ys
I.e. return
is in the wrong spot.
I would expect something like this:
return predicate(x).SelectMany(flg =>
xs.WhereM(predicate).SelectMany(ys =>
new List<IEnumerable<A>> { flg ? (new List<A> {x}).Concat(ys) : ys }))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With