Добавлен , опубликован
Что такое монада MayBe можно узнать из видео:
Довольно-таки полноценный код монады:
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace System.Monads
{
    /// <summary> Для словарей </summary>
    public static class MaybeIDictionary
    {
        /// <summary> Выполняет действие с каждым элементом словаря, если словарь существует </summary>
        public static IDictionary<TKey, TValue> Do<TKey, TValue>(this IDictionary<TKey, TValue> source,
            Action<TKey, TValue> action)
        {
            if (source != null)
            {
                foreach (var element in source)
                {
                    action(element.Key, element.Value);
                }
            }

            return source;
        }

        /// <summary> Достает значение по указанному ключу, если словарь существует и имеет такой ключ. В противном случае возвращает значение типа по умолчанию. </summary>
        public static TValue With<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key)
        {
            if (source != null)
            {
                TValue result;
                if (source.TryGetValue(key, out result))
                    return result;
            }
            return default(TValue);
        }

        /// <summary>
        /// Достает значение по указанному ключу, если словарь существует и имеет такой ключ. В противном случае возвращает заданное нами значение по умолчанию </summary>
        public static TValue Return<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue defaultValue)
        {
            if (source != null)
            {
                TValue result;
                if (source.TryGetValue(key, out result))
                    return result;
            }
            return defaultValue;
        }
    }

    /// <summary> Для перечислений - массивы, списки </summary>
    public static class MaybeIEnumerable
    {
        /// <summary> Выполняет указанное действие с каждым элементом списка, если список существует </summary>
        public static IEnumerable Do(this IEnumerable source, Action<object> action)
        {
            var enumerable = source as object[] ?? source.Cast<object>().ToArray();
            if (source != null)
                foreach (var element in enumerable)
                    action(element);
            return enumerable;
        }

        /// <summary> Выполняет указанное действие с каждым элементом списка, если список существует </summary>
        public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
        {
            var enumerable = source as TSource[] ?? source.ToArray();
            if (source != null)
                foreach (var element in enumerable)
                    action(element);
            return enumerable;
        }

        /// <summary> Выполняет указанное действие с каждым элементом списка, если список существует </summary>
        public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource, int> action)
        {
            var enumerable = source as TSource[] ?? source.ToArray();
            if (source != null)
                foreach (var element in enumerable.Select((s, i) => new {Source = s, Index = i}))
                    action(element.Source, element.Index);
            return enumerable;
        }

        /// <summary> Проталкивает указанное поле, если список существует. Аналог Select() грубо говоря </summary>
        public static IEnumerable<TResult> With<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> action)
        {
            return source != null ? source.Select(action) : null;
        }
    }
    public static class MaybeNullable
    {
        /// <summary> Выполняет указанное действие, если объект существует. </summary>
        public static TSource? Do<TSource>(this TSource? source, Action<TSource?> action)
            where TSource : struct
        {
            if (source.HasValue)
                action(source);
            return source;
        }

        /// <summary> Проталкивает указанное значение, если объект существует и значение типа по умолчанию, если не существует </summary>
        public static TResult With<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action)
            where TSource : struct
        {
            return source.HasValue ? action(source) : default(TResult);
        }

        /// <summary> Проталкивает указанное значение, если объект существует и значение указанного нами типа, если не существует </summary>
        public static TResult Return<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action, TResult defaultValue)
            where TSource : struct
        {
            return source.HasValue ? action(source) : defaultValue;
        }

        /// <summary> Проталкивает значение дальше, если выполняется условие, в противном случае возвращает значение типа по умолчанию </summary>
        public static TSource? If<TSource>(this TSource? source, Func<TSource?, bool> condition)
            where TSource : struct
        {
            return source.HasValue && condition(source) ? source : default(TSource);
        }

        /// <summary> Пироталкивает значение дальше, если условие не выполняется, в противном случае возвращает значение по умолчанию </summary>
        public static TSource? IfNot<TSource>(this TSource? source, Func<TSource?, bool> condition)
            where TSource : struct
        {
            return source.HasValue && !condition(source) ? source : default(TSource);
        }

        /// <summary> Если объект не существует, восстанавливает его указанным способом </summary>
        public static TInput Recover<TInput>(this TInput? source, Func<TInput> recoverFunc)
            where TInput : struct
        {
            return source.HasValue ? source.Value : recoverFunc();
        }

        /// <summary> Выполняет метод если объект не пуст. Возвращает ошибку, если таковая произошла во время выполнения действия </summary>
        public static Tuple<TSource?, Exception> TryDo<TSource>(this TSource? source, Action<TSource?> action)
            where TSource : struct
        {
            if (source.HasValue)
                try
                {
                    action(source);
                }
                catch (Exception ex)
                {
                    return new Tuple<TSource?, Exception>(source, ex);
                }
            return new Tuple<TSource?, Exception>(source, null);
        }

        /// <summary> Выполняет метод если объект не пуст. Возвращает ошибку, если таковая произошла во время выполнения действия и только если она удовлетворяет условию </summary>
        public static Tuple<TSource?, Exception> TryDo<TSource>(this TSource? source, Action<TSource?> action, Func<Exception, bool> exceptionChecker)
            where TSource : struct
        {
            if (source.HasValue)
                try
                {
                    action(source);
                }
                catch (Exception ex)
                {
                    if (exceptionChecker(ex))
                        return new Tuple<TSource?, Exception>(source, ex);
                    throw;
                }
            return new Tuple<TSource?, Exception>(source, null);
        }

        /// <summary> Выполняет метод если объект не пуст. Возвращает ошибку, если таковая произошла во время выполнения действия и только если она заранее указана в массиве </summary>
        public static Tuple<TSource?, Exception> TryDo<TSource>(this TSource? source, Action<TSource?> action, params Type[] expectedException)
            where TSource : struct
        {
            if (source.HasValue)
                try
                {
                    action(source);
                }
                catch (Exception ex)
                {
                    if (expectedException.Any(exp => exp.IsInstanceOfType(ex)))
                        return new Tuple<TSource?, Exception>(source, ex);
                    throw;
                }

            return new Tuple<TSource?, Exception>(source, null);
        }


        /// <summary>
        /// Allows to do some conversion of <paramref name="source"/> if its not null and catch any exceptions
        /// </summary>
        public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action)
            where TSource : struct
        {
            if (source.HasValue)
            {
                TResult result = default(TResult);
                try
                {
                    result = action(source);
                    return new Tuple<TResult, Exception>(result, null);
                }
                catch (Exception ex)
                {
                    return new Tuple<TResult, Exception>(result, ex);
                }
            }

            return new Tuple<TResult, Exception>(default(TResult), null);
        }

        /// <summary>
        /// Allows to do some conversion of <paramref name="source"/> if its not null and catch exceptions, which handled by <param name="exceptionChecker"/>
        /// </summary>
        public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action, Func<Exception, bool> exceptionChecker)
            where TSource : struct
        {
            if (source.HasValue)
            {
                TResult result = default(TResult);
                try
                {
                    result = action(source);
                    return new Tuple<TResult, Exception>(result, null);
                }
                catch (Exception ex)
                {
                    if (exceptionChecker(ex))
                        return new Tuple<TResult, Exception>(result, ex);
                    throw;
                }
            }

            return new Tuple<TResult, Exception>(default(TResult), null);
        }

        /// <summary>
        /// Allows to do some conversion of <paramref name="source"/> if its not null and catch <param name="expectedException"/> exceptions
        /// </summary>
        public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action, params Type[] expectedException)
            where TSource : struct
        {
            if (source.HasValue)
            {
                TResult result = default(TResult);
                try
                {
                    result = action(source);
                    return new Tuple<TResult, Exception>(result, null);
                }
                catch (Exception ex)
                {
                    if (expectedException.Any(exp => exp.IsInstanceOfType(ex)))
                        return new Tuple<TResult, Exception>(result, ex);
                    throw;
                }
            }

            return new Tuple<TResult, Exception>(default(TResult), null);
        }

        /// <summary> Возвращает true если объект не существует </summary>
        public static bool IsNull<TSource>(this TSource? source)
            where TSource : struct
        {
            return source.HasValue == false;
        }

        /// <summary> Возвращает true если объект существует </summary>
        public static bool IsNotNull<TSource>(this TSource? source)
            where TSource : struct
        {
            return source.HasValue;
        }
    }
    public static class MaybeObjects
    {
        /// <summary> Выполняет действие, если объект существует </summary>
        public static TSource Do<TSource>(this TSource source, Action<TSource> action)
            where TSource : class
        {
            if (source != default(TSource))
                action(source);
            return source;
        }

        /// <summary> Проталкивает значение, если объект существует </summary>
        public static TResult With<TSource, TResult>(this TSource source, Func<TSource, TResult> action)
            where TSource : class
        {
            return source != default(TSource) ? action(source) : default(TResult);
        }

        /// <summary> Проталкивает значение, если объект существует, иначе возвращает наше значение по умолчанию </summary>
        public static TResult Return<TSource, TResult>(this TSource source, Func<TSource, TResult> action, TResult defaultValue)
            where TSource : class
        {
            return source != default(TSource) ? action(source) : defaultValue;
        }

        /// <summary> Проталкивает значение дальше, если выполняется условие </summary>
        public static TSource If<TSource>(this TSource source, Func<TSource, bool> condition)
            where TSource : class
        {
            return source != default(TSource) && condition(source) ? source : default(TSource);
        }

        /// <summary> Проталкивает значение дальше, если условие не выполняется</summary>
        public static TSource IfNot<TSource>(this TSource source, Func<TSource, bool> condition)
            where TSource : class
        {
            return source != default(TSource) && !condition(source) ? source : default(TSource);
        }

        /// <summary> Восстанавливает объект указанным способом если он не существует </summary>
        public static TInput Recover<TInput>(this TInput source, Func<TInput> action)
            where TInput : class
        {
            return source ?? action();
        }

        /// <summary> Преобразует объект в указанный тип </summary>
        public static TResult OfType<TResult>(this object source)
        {
            return source is TResult ? (TResult) source : default(TResult);
        }

        /// <summary>
        /// Allows to do <paramref name="action"/> and catch any exceptions
        /// </summary>
        public static Tuple<TSource, Exception> TryDo<TSource>(this TSource source, Action<TSource> action)
            where TSource : class
        {
            if (source != default(TSource))
                try
                {
                    action(source);
                }
                catch (Exception ex)
                {
                    return new Tuple<TSource, Exception>(source, ex);
                }
            return new Tuple<TSource, Exception>(source, null);
        }

        /// <summary>
        /// Allows to do <paramref name="action"/> and catch exceptions, which handled by <param name="exceptionChecker"/>
        /// </summary>
        public static Tuple<TSource, Exception> TryDo<TSource>(this TSource source, Action<TSource> action, Func<Exception, bool> exceptionChecker)
            where TSource : class
        {
            if (source != default(TSource))
                try
                {
                    action(source);
                }
                catch (Exception ex)
                {
                    if (exceptionChecker(ex))
                        return new Tuple<TSource, Exception>(source, ex);
                    throw;
                }
            return new Tuple<TSource, Exception>(source, null);
        }

        /// <summary>
        /// Allows to do <paramref name="action"/> and catch <param name="expectedException"/> exceptions
        /// </summary>
        public static Tuple<TSource, Exception> TryDo<TSource>(this TSource source, Action<TSource> action, params Type[] expectedException)
            where TSource : class
        {
            if (source != default(TSource))
                try
                {
                    action(source);
                }
                catch (Exception ex)
                {
                    if (expectedException.Any(exp => exp.IsInstanceOfType(ex)))
                        return new Tuple<TSource, Exception>(source, ex);
                    throw;
                }

            return new Tuple<TSource, Exception>(source, null);
        }


        /// <summary>
        /// Allows to do some conversion of <paramref name="source"/> if its not null and catch any exceptions
        /// </summary>
        public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource source, Func<TSource, TResult> action)
            where TSource : class
        {
            if (source != default(TSource))
            {
                TResult result = default(TResult);
                try
                {
                    result = action(source);
                    return new Tuple<TResult, Exception>(result, null);
                }
                catch (Exception ex)
                {
                    return new Tuple<TResult, Exception>(result, ex);
                }
            }

            return new Tuple<TResult, Exception>(default(TResult), null);
        }

        /// <summary>
        /// Allows to do some conversion of <paramref name="source"/> if its not null and catch exceptions, which handled by <param name="exceptionChecker"/>
        /// </summary>
        public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource source, Func<TSource, TResult> action, Func<Exception, bool> exceptionChecker)
            where TSource : class
        {
            if (source != default(TSource))
            {
                TResult result = default(TResult);
                try
                {
                    result = action(source);
                    return new Tuple<TResult, Exception>(result, null);
                }
                catch (Exception ex)
                {
                    if (exceptionChecker(ex))
                        return new Tuple<TResult, Exception>(result, ex);
                    throw;
                }
            }

            return new Tuple<TResult, Exception>(default(TResult), null);
        }

        /// <summary>
        /// Allows to do some conversion of <paramref name="source"/> if its not null and catch <param name="expectedException"/> exceptions
        /// </summary>
        public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource source, Func<TSource, TResult> action, params Type[] expectedException)
            where TSource : class
        {
            if (source != default(TSource))
            {
                TResult result = default(TResult);
                try
                {
                    result = action(source);
                    return new Tuple<TResult, Exception>(result, null);
                }
                catch (Exception ex)
                {
                    if (expectedException.Any(exp => exp.IsInstanceOfType(ex)))
                        return new Tuple<TResult, Exception>(result, ex);
                    throw;
                }
            }

            return new Tuple<TResult, Exception>(default(TResult), null);
        }

        /// <summary>
        /// Handle exception with no actions
        /// </summary>
        public static TSource Catch<TSource>(this Tuple<TSource, Exception> source)
        {
            return source.Item1;
        }

        /// <summary>
        /// Handle exception with <param name="handler"/> action
        /// </summary>
        public static TSource Catch<TSource>(this Tuple<TSource, Exception> source, Action<Exception> handler)
        {
            if (source.Item2 != null)
                handler(source.Item2);
            return source.Item1;
        }

        /// <summary> Возвращает true если объект не существует </summary>
        public static bool IsNull<TSource>(this TSource source)
            where TSource : class
        {
            return source == default(TSource);
        }

        /// <summary> Возвращает true если объект существует </summary>
        public static bool IsNotNull<TSource>(this TSource source)
            where TSource : class
        {
            return source != default(TSource);
        }
    }
}
Исходник взят отсюда, от себя только частично комменты на русском + компоновка в один файлик + пара фиксов кода.
Монада очень важная и очень часто используемая, потому не запостить - грех.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
20
10 лет назад
Отредактирован Mihahail
0
Я видимо сильно отстал от жизни, раз в таких языках как сишарп уже юзают монады.
Код не читал, только саммари, есть какие-нибудь отличия от хаскельных монад?
Я так понял, тут в основном реализован функционал итераторов для перечислимых объектов, с выбросом Nothing в случае ошибок?
Видео не смог посмотреть. Не ставить же сильверлайт?)
0
27
10 лет назад
0
Mihahail,
Шарп поддерживает монадические конструкции, хотя конечно не в той мере в которой это происходит в языках функционального программирования.
Соль не в перечисляемых объектах, если почитаешь монаду чуть пониже, то для обычных объектов она тоже юзается. В общем соль такова - это просто пробрасывание значения. Исключение постоянных проверок на Null.
Вот пример кода который я пишу прямо сейчас. Его смысл в том чтобы проверить, содержит ли текущий объект нужную мне конструкцию. В обычном коде он выглядит так:
bool result = false;
if (calc != null && calc.handle != null)
{
    var methodHandle = calc.handle as HandleMethod;
    if (methodHandle != null && methodHandle.method == checkMethod)
        result = true;
}
То есть фактически мне нужно просто пройти в calc.handle.method и сравнить его с моим checkMethod. При том если я не могу туда попасть по причине того что объектов нет - вернуть false
С помощью монады этот же самый код я могу забить однострочником
var result = calc.With(x => x.handle).OfType<HandleMethod>().With(x => x.method).If(x => x == checkMethod).ReturnSuccess();
Соответственно такая цепочка будет толкать null дальше, либо выполнять выражение.
С хаскеллом не работал, потому перечислить отличия не могу. Но это явно сдернуто оттуда.
0
29
10 лет назад
0
Фу однострочник....
А если calc будет наллом?
0
27
10 лет назад
0
А если calc будет наллом?
то все сработает точно так же
0
29
10 лет назад
0
Extravert, как то громоздко получается из-за вот этого OfType или у тебя handle не этого типа?
0
27
10 лет назад
0
Хочу заметить, я никого не принуждаю к использованию подобного метода. Но им пользуется огромное количество людей, в том числе и я сам. Раз я временами выкладываю наработки то было бы разумно опубликовать и такие куски, чтобы в дальнейшем свободно делать публикации с использованием этой монады.
Имхо монада дает неоспоримые плюсы. Например тот же пример что выше, немного перефразируем. Скажем у нас есть вот такая ситуация:
var result = calc != null && calc.handle != null && calc.handle.method != null && calc.handle.method.value != null ? calc.handle.method.value : null;
С монадой будет проще некуда:
var result = calc.With(x => x.handle).With(x => x.method).With(x => x.value);
Монада выглядит читабельней имхо.
Опять же, можно взять к примеру стандартные монады - Select, Where, Any - ведь они действительно удобны. Но всегда будут люди, которые скажут "фээ, я лучше буду разворачивать форичи вместо однострочника"
в первом примере handle у меня абстрактного типа Handle, и я привожу его к конкретному HandleMethod и только в случае если он имеет данный тип цепочка выполняется дальше.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.