Добавлен Devion,
опубликован
Что такое монада 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);
}
}
}
Исходник взят отсюда, от себя только частично комменты на русском + компоновка в один файлик + пара фиксов кода.
Монада очень важная и очень часто используемая, потому не запостить - грех.
Монада очень важная и очень часто используемая, потому не запостить - грех.
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Отредактирован Mihahail
Код не читал, только саммари, есть какие-нибудь отличия от хаскельных монад?
Я так понял, тут в основном реализован функционал итераторов для перечислимых объектов, с выбросом Nothing в случае ошибок?
Видео не смог посмотреть. Не ставить же сильверлайт?)
Шарп поддерживает монадические конструкции, хотя конечно не в той мере в которой это происходит в языках функционального программирования.
Соль не в перечисляемых объектах, если почитаешь монаду чуть пониже, то для обычных объектов она тоже юзается. В общем соль такова - это просто пробрасывание значения. Исключение постоянных проверок на Null.
С помощью монады этот же самый код я могу забить однострочником
С хаскеллом не работал, потому перечислить отличия не могу. Но это явно сдернуто оттуда.
А если calc будет наллом?
Имхо монада дает неоспоримые плюсы. Например тот же пример что выше, немного перефразируем. Скажем у нас есть вот такая ситуация:
Опять же, можно взять к примеру стандартные монады - Select, Where, Any - ведь они действительно удобны. Но всегда будут люди, которые скажут "фээ, я лучше буду разворачивать форичи вместо однострочника"
бтв забыл сказать - код с 'Truple' только для новых шарпиков, скажем шарп в юнити его не понимает
После конструкций хаскеля однострочник выглядит стремно(очень очень очень) =) Хотя я пожалуй всё понял. Но сам бы так не стал писать, наверно.
Ещё я плохо знаю сишарп, когда читаю исхожу из аналогий с С++, который знаю чуть лучше.
Ещё я плохо знаком с шаблонами, как по этой конструкции определить, где возникло исключение? А то протолкнем налл на самый верх, а где он появился? Это ведь придётся лезть в код монады чтобы она, скажем, кидала алерт куда-нибудь?
В хаскелле, кстати, это всё делается иначе и гораздо проще, с использованием механизма классов типов.. Я (ну, это видно) не часто комментирую такие топики, но тут конструкция из моего любимого(но не самого часто используемого) языка была перенесена на оо-язык, что вообще-говоря интересно само по себе. Вряд ли это самая полезная ссылка, которую я могу привести, но вот
Отредактирован Devion
Следует сразу сказать - он и правда расширяет мозг, но ничего серьезного(типа игрового движка) на нём нельзя написать просто. Лаконично - да, но не просто. Точнее оно когда-нибудь станет "просто", но для этого нужно знатно поднатореть во всём этом. И да, самому придумывать архитектуру каждой большой программы, ибо паттернов - как бы нет. И понятия "идиоматичный код на хаскеле" - как бы тоже. На русском уж точно такого материала не сыщешь, а на английском можно как раз за некоторыми best practices поглядеть Real World Haskell.
Мне на начальном этапе очень помогли две главы этой, хм, книги-руководства: anton-k.github.io/ru-haskell-book/book/1.html#%D0%B7%D0%BD%D0%B0...
Тут особенно важно, что вводится некий "вычислитель", действующий особым образом в соответствии с кодом на хаскеле.
Отредактирован Mihahail
У меня есть роскошь: возможность серьезно изучать и пытаться писать на языках типа лиспа, хаскеля, эрланга. Т.е. то, что считается маргинальщиной и т.н. не нужно. Это мало способствует развитию навыков software engineering'a, зато интересно.
1 - messageIds = null -> тут должен быть NullArgumentException
2 - messageIds = new int[] {} -> тут должен вернуть пустой список
Это и описывается в тест кейсах
Или программист сам не уверен, как работает GetMessages?
Ecли у нас пустой массив -> MySqlException из-за того, что будет выражение вида
Отредактирован Mihahail