Добавлен , опубликован
Что такое монада 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 лет назад
0
Спасибо.
После конструкций хаскеля однострочник выглядит стремно(очень очень очень) =) Хотя я пожалуй всё понял. Но сам бы так не стал писать, наверно.
Ещё я плохо знаю сишарп, когда читаю исхожу из аналогий с С++, который знаю чуть лучше.
Ещё я плохо знаком с шаблонами, как по этой конструкции определить, где возникло исключение? А то протолкнем налл на самый верх, а где он появился? Это ведь придётся лезть в код монады чтобы она, скажем, кидала алерт куда-нибудь?
В хаскелле, кстати, это всё делается иначе и гораздо проще, с использованием механизма классов типов.. Я (ну, это видно) не часто комментирую такие топики, но тут конструкция из моего любимого(но не самого часто используемого) языка была перенесена на оо-язык, что вообще-говоря интересно само по себе. Вряд ли это самая полезная ссылка, которую я могу привести, но вот
0
29
10 лет назад
0
Mihahail, ну я думаю в случае написания такого кода использует совсем другой подход к проектированию системы и написанию коду, так чтобы исключений не было вообще или сводилось к минимуму. Ну и там есть-что то типа исключений, в код особо не смотрел, но что-то похожее есть
0
27
10 лет назад
Отредактирован Devion
0
Mihahail, там монады есть для trycatch. То есть достаточно дописать к названию монады слово Try в начале (например TryWith) и она будет выбивать екзепшн, если таковой есть. Но опять же ты прав - с однострочниками в этом плане всего туговато
На самом деле сам часто поглядываю на хаскель, но никак не могу его освоить. Пробовал брать литературу в руки, но там сразу все начинается с терминов аля комбинаторы/унарные методы/анализаторы без малейших разъяснений, потому ничего не понимая в фундаменте мне не дается понимание этой парадигмы и на этом я закругляюсь. Плюс когда смотрю даже на элементарный код - вопросов уйма, а спросить про них не у кого - хаскель не очень популярный язык чтобы было к кому обратиться, если есть вопросы.
0
20
10 лет назад
0
Extravert, я в процессе освоения сейчас, с основами вроде разобрался. По выходным я могу на любой вопрос дать любой ответ =)
Следует сразу сказать - он и правда расширяет мозг, но ничего серьезного(типа игрового движка) на нём нельзя написать просто. Лаконично - да, но не просто. Точнее оно когда-нибудь станет "просто", но для этого нужно знатно поднатореть во всём этом. И да, самому придумывать архитектуру каждой большой программы, ибо паттернов - как бы нет. И понятия "идиоматичный код на хаскеле" - как бы тоже. На русском уж точно такого материала не сыщешь, а на английском можно как раз за некоторыми best practices поглядеть Real World Haskell.
Действительно качественной литературы(особенно на русском) - вообще почти нет, а все книги "для уже программистов" - начинаются просто(имхо) не с того. Я заметил, что людей повергает в ступор вопрос о том, как же собственно работает программа, в которой нет никаких выражений вида "сделай вот это".
Мне на начальном этапе очень помогли две главы этой, хм, книги-руководства: anton-k.github.io/ru-haskell-book/book/1.html#%D0%B7%D0%BD%D0%B0...
Тут особенно важно, что вводится некий "вычислитель", действующий особым образом в соответствии с кодом на хаскеле.
0
29
10 лет назад
0
Ещё я плохо знаком с шаблонами, как по этой конструкции определить, где возникло исключение? А то протолкнем налл на самый верх, а где он появился?
Кст на счет этого, тут неожиданно вспомнил, чтобы не было нежданчиков, можно использовать юнит тесты и все будет ОК. Ну это если юнит тесты написаны хорошо конечно же
0
20
10 лет назад
0
alexprey, да как тебе сказать... Не писал ничего крупного после той моей попытки сделать леталку в космосе. Поэтому с юнит тестами знаком сугубо википедийно.
0
29
10 лет назад
0
Mihahail, суть юнит тестов, чтобы описать правильное поведение объекта для большинства случаев (пограничных значений). Если поведение меняется, то юнит тест падает. Хорошим тоном считается писать юнит тесты для класса еще до имплементации самих методов.
0
20
10 лет назад
Отредактирован Mihahail
0
alexprey, после эрлангов с хаскелем это кажется диким - прописывать каждый случай, каждую ветку ast. Или там не всё так плохо?
У меня есть роскошь: возможность серьезно изучать и пытаться писать на языках типа лиспа, хаскеля, эрланга. Т.е. то, что считается маргинальщиной и т.н. не нужно. Это мало способствует развитию навыков software engineering'a, зато интересно.
0
29
10 лет назад
0
Mihahail, обычно прописывают лишь пограничный значения для параметров. Например, метод
GetMessages(int[] messageIds)
Тут 2 пограничных значения:
1 - messageIds = null -> тут должен быть NullArgumentException
2 - messageIds = new int[] {} -> тут должен вернуть пустой список
Это и описывается в тест кейсах
0
20
10 лет назад
0
alexprey, в смысле? Разве GetMessages в описанных случаях не делает так как написано сам по себе?)
Или программист сам не уверен, как работает GetMessages?
0
29
10 лет назад
0
Mihahail, в случае говнокода да :D Суть в том, что когда работает много людей в проекте, все должно работать с примерно одинаковой логикой, как я и описал выше. Конечно но же можно и засунуть туда кейс, что при null возвращает все сообщения, но это весьма не очевидно. Почему именно те два кейса такие, ну например мы юзаем там SqlDataProvider и некороший программист написал его так:
IList<MessageEntity> GetMessages(int[] messageIds) {
   var collection = new EntityCollection<MessageEntity>();
   using(var cmd = new MySqlCommand()) {
       cmd.CommandText = string.Format(@"
SELECT *
FROM messages
WHERE MessageId IN ({0})
        ", messageIds.Join(","));

        FillEntityCollection(cmd, collection);
    }
    return collection;
}
Если у нас параметр null -> NullPointerException словим
Ecли у нас пустой массив -> MySqlException из-за того, что будет выражение вида
WHERE MessageId IN ()
Уже не однократно матерился на работе из-за того, что другие не обрабатывают эти 2 кейса и программа сваливается при проверке. Хотя все ждут что метод вернет пустое множество.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.