简体   繁体   中英

Monadic Programming in C#

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

Ie 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 }))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM