Философия LINQ
От: Димчанский Литва http://dimchansky.github.io/
Дата: 28.03.15 19:21
Оценка:
Как-то упустил момент, что при десахаризации LINQ применяется SelectMany с дополнительным resultSelector, в результате чего получается последовательная цепочка вызовов .SelectMany, которых никак не избежать.
Ниже можно видеть пример монады Maybe на C#. В первом случае, даже если мы имеем Nothing, цепочка SelectMany будет вся пройдена, во втором случае, мы сразу обрываем вычисления.
Кто-то может объяснить, почему был выбран именно такой подход рассахаривания, почему не как в большинстве функциональных языков (второй вариант)?
Достаточно было просто, чтобы select последний рассахаривался в Map типа:
    public static IMaybe<TB> Map<TA, TB>(
            this IMaybe<TA> a,
            Func<TA, TB> selector)

как в той же Scala, например.
В чем глубокий смысл существующей реализации сахара LINQ?

Собственно код:
using System;

namespace MaybeMonad
{
    internal class Program
    {
        private static void Main()
        {
            var result = from a in "Hello ".ToMaybe()
                from b in Nothing<string>.Instance
                from c in "World!".ToMaybe()
                from d in " :)".ToMaybe()
                select a + b + c + d;

            /*
            var result = "Hello ".ToMaybe()
                .SelectMany(a => Nothing<string>.Instance, (a, b) => new {a, b})
                .SelectMany(@t => "World!".ToMaybe(), (@t, c) => new {@t, c})
                .SelectMany(@t => " :)".ToMaybe(), (@t, d) => @t.@t.a + @t.@t.b + @t.c + d); 
             */

            Console.WriteLine("Result: " + result);
            Console.WriteLine();

            /*
            ctor Just<String>
            Bind on Just
            ctor Nothing<String>
            Bind on Nothing
            ctor Nothing<<>f__AnonymousType0`2>
            Bind on Nothing
            ctor Nothing<<>f__AnonymousType1`2>
            Bind on Nothing
            Result: Nothing
            */

            var result2 =
                "Hello ".ToMaybe().SelectMany(a =>
                    Nothing<string>.Instance.SelectMany(b =>
                        "World!".ToMaybe().SelectMany(c =>
                            " :)".ToMaybe().Map(d => a + b + c + d))));

            Console.WriteLine("Result2: " + result2);

            /*
            ctor Just<String>
            Bind on Just
            Bind on Nothing
            Result2: Nothing
            */
        }
    }

    public interface IMaybe<T>
    {
        IMaybe<TR> Bind<TR>(Func<T, IMaybe<TR>> selector);  
    }

    public sealed class Nothing<T> : IMaybe<T>
    {
        public static readonly Nothing<T> Instance = new Nothing<T>();

        private Nothing()
        {
            Console.WriteLine("ctor Nothing<" + typeof (T).Name + ">");
        }

        public IMaybe<TR> Bind<TR>(Func<T, IMaybe<TR>> selector)
        {
            Console.WriteLine("Bind on Nothing");
            return Nothing<TR>.Instance;
        }

        public override string ToString()
        {
            return "Nothing";
        }
    }

    public sealed class Just<T> : IMaybe<T>
    {
        private readonly T _value;

        public T Value
        {
            get { return _value; }
        }

        public Just(T value)
        {
            Console.WriteLine("ctor Just<" + typeof (T).Name + ">");
            _value = value;
        }

        public IMaybe<TR> Bind<TR>(Func<T, IMaybe<TR>> selector)
        {
            Console.WriteLine("Bind on Just");
            return selector(Value);
        }

        public override string ToString()
        {
            return "Just(" + Value + ")";
        }
    }

    internal static class Ext
    {
        public static IMaybe<T> ToMaybe<T>(this T value)
        {
            return new Just<T>(value);
        }

        public static IMaybe<TB> SelectMany<TA, TB>(
            this IMaybe<TA> a,
            Func<TA, IMaybe<TB>> selector)
        {
            return a.Bind(selector);
        }

        public static IMaybe<TR> SelectMany<TA, TB, TR>(
            this IMaybe<TA> a,
            Func<TA, IMaybe<TB>> selector,
            Func<TA, TB, TR> resultSelector)
        {
            return a.Bind(v => selector(v).Bind(b => resultSelector(v, b).ToMaybe()));
        }

        public static IMaybe<TB> Map<TA, TB>(
            this IMaybe<TA> a,
            Func<TA, TB> selector)
        {
            return a.Bind(v => selector(v).ToMaybe());
        }
    }
}
Re: Философия LINQ
От: Sinix  
Дата: 28.03.15 21:09
Оценка: 24 (2) +2
Здравствуйте, Димчанский, Вы писали:

Д>В чем глубокий смысл существующей реализации сахара LINQ?


В том, что иногда банан — это просто банан SelectMany никогда не предназначался для натягивания монад на шарп.

Конкретно про SelectMany можно почитать классика, если говорить про монады вообще — я очень сомневаюсь, что они появятся в шарпе в чистом виде, по крайней мере пока сохраняется общая линия развития языка — это точно.

Все крупные фишки шарпа всегда добавляются как _законченное_ решение под конкретный набор проблем, добавление монад "чтоб было" скорее создаст новые, особых сценариев, которые в рамках шарпа будет удобнее решать через монады тоже нет.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.