Регистрация Главная Пользователи Все разделы прочитаны
Сообщения за день Справка Регистрация
Старый 06.03.2016, 13:27   #1
 Разведчик
Аватар для HideFebruary
 
HideFebruary лучик света в грозовом небеHideFebruary лучик света в грозовом небеHideFebruary лучик света в грозовом небеHideFebruary лучик света в грозовом небеHideFebruary лучик света в грозовом небеHideFebruary лучик света в грозовом небеHideFebruary лучик света в грозовом небе
Регистрация: 25.02.2016
Сообщений: 5
Популярность: 771
Сказал(а) спасибо: 0
Поблагодарили 5 раз(а) в 5 сообщениях
 
Post Monads [1] = Do

Прошлая статья: Monads [0]

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

Сама по себе, эта тема мало интереса представляет для читерства как такового, но для повышения уровня кода и знаний это будет полезно. Как можно повысить уровень кода? Один из принципов ООП гласит, что нужно исключать дублирование кода, я выбрал именно этот принцип, потому что это самая популярная проблема языка на мой взгляд. Вторая проблема которую я выделю - лаконичность кода, он должен быть легко читаемым и понятным по смыслу. Сложно представить что C# не лаконичен по своей сути, его оказуалили так, что на нем легко сможет писать и ребенок, но все же есть некоторые места которые меня очень раздражают.

Начнем с дублирования. Нам дали полиморфизм, что бы мы писали логику в одном классе и использовали её в других без дублирования. Нам дали дженерики, что бы мы могли отвязать логику от конкретных типов и привязать её хоть ко всем сразу. Но знаете, дублирования какого кода полно в куче исходников и от которого не так-то просто избавиться? Приведу небольшой пример:

Код:
void Foo( object argument )
{
   //todo
}
Допустим, этот метод находится в очень критичной отказоустойчивой системе и нам нужно произвести какие-то действия с аргументом. Нужно ли объяснять что необходимо проверить инициализацию аргумента? Тем более если аргумент приходит от какого-то внешнего пользовательского кода. Что бы обратиться к аргументу, сначала нужнно сделать такую проверку:

Код:
If ( argument == null ) return;
Обычно, если аргумент неинициализирован, то вызывается ошибка, но мы помним что код находится в важной системе и максимум можем написать в лог о том, что аргумент невалидный, либо как-нибудь по другому оповестить об этом. Только в 2015 году, в версии 6 языка C# предложили оператор ".?" который сокращает дублирование данных проверок. При чем в нашем случаее этот оператор никак не поможет, а таких проверок куча. И только в следующей версии языка разработчики планируют ввести обязательные аргументы которые никогда не равны null, но это уже другая история.

Теперь про лаконичность. Начну с конструкции try.

Код:
void Foo( object argument )
{
   try
   {
      Console.WriteLine( argument.GetHashCode () );
   }
   catch
   {

   }
}
Для меня этого кода слишком много, особенно раздражает пустой, но обязательный блок catch, я бы хотел что-то вроде этого:

Код:
void Foo( object argument )
{
   Try( Console.WriteLine( argument.GetHashCode() ));
}
На самом деле, все это меня не натолкнуло на поиск какого-либо решения. Эти вещи незначительны и с ними можно прижиться. Как-то раз я увидел в интернетах про монаду Maybe на языке C#, конкретная реализация меня не впечатлила, но вот возможность языка комбинировать методы расширения с дженериками рождая при этом что-то вроде монад, очень даже. Я начал с этим экспериментировать, максимально обобщая монады, заменяя некоторые конструкции языка и получилось кое-что, о чем я и буду писать в этом цикле статей.

Итак, что же такое монады, описание из вики:

Цитата:
Мона́да в функциональном программировании — это абстракция линейной цепочки связанных вычислений. Её основное назначение — инкапсуляция функций с побочным эффектом от чистых функций, а точнее их выполнений от вычислений. Монады применяются в языке Haskell, так как он повсеместно использует ленивые вычисления, которые вместе с побочным эффектом, как правило, образуют плохо прогнозируемый результат.

Мы возьмем за основу это понятие и будем развивать в C#. Наши монады будут возвращать предсказуемый результат, по крайней мере мы снабдим их этой возможностью. Монады будут завязаны на методах расширения, принимать в себя один аргумент в виде дженерика и еще один аргумент в виде действия над ним. Действием будет являться один из трех стандартных делегатов: [Ссылки могут видеть только зарегистрированные пользователи. ], [Ссылки могут видеть только зарегистрированные пользователи. ], [Ссылки могут видеть только зарегистрированные пользователи. ].

Развивая эту идею, появилось правило взаимодействия монад. Есть одна основная монада, другие монады работают с ней, самостоятельно они должны делать по минимуму, это нужно для концепции в целом и для решения первой проблемы с дублированием кода в частности.

Итак, наша первая монада - Do. Она делает что-то с вашим аргументом ( или не делает, по ситуации ), а потом возвращает этот же аргумент, что бы можно было делать цепочку таких выражений в функциональном стиле.

Код:
public static T Do<T>( this T argument, Action<T> doAction )
{
    If ( argument != null ) doAction?.Invoke ( argument );
    return argument;
}
Пример её использования:

Код:
"Hello Do".Do ( WriteLine );
Продление цепочки:
Код:
"Hello Do"
    .Do ( WriteLine )
    .Do ( WriteLine )
    .Do ( str => WriteLine ( $"Приветственное сообщение: {str}" ) );
Так как действие вызывается через Action, мы не можем попросить монаду посчитать нам что-то или изменить исходный аргумент. Что бы это можно было сделать, мы добавим в качестве действия функтор.

Код:
public static TResult Do<T, TResult>( this T argument, Func<T,TResult> doFunc,
    TResult defaultResult = default(TResult) )
{
    return argument == null && doFunc == null ? defaultResult : doFunc ( argument );
}
Обратите внимание на реализацию с использованием default значения, это нужно для структур, если с классами этого можно избежать и просто вернуть null, а во внешнем коде использовав оператор "??" присвоить какое-то значение, то со структурами так просто не выйдет и мы даем возможность определить значение выходного результата заранее, если выполнить выражение не получается. Теперь мы можем легко изменять нашу переменную по монадической цепочке:

Код:
"Hello".Do ( WriteLine )
    .Do ( s => s += " Do" ).Do ( WriteLine )
    .Do ( s => s.Replace ( "Do", "World" ) ).Do ( WriteLine );
Этих двух методов будет достаточно для монады Do, что бы в дальнейшем её использовали и другие монады, а о них продолжу в следующих частях.

Исходники монады под спойлером:

Спойлер
________________
code safe my friend

Последний раз редактировалось HideFebruary; 06.03.2016 в 13:57.
  Ответить с цитированием
Пользователь сказал cпасибо:
Sinyss (23.05.2016)
Ответ

Метки
c sharp, dotnet, extension, monads

Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
[Статья] Monads [0] HideFebruary C# 0 01.03.2016 21:25

Заявление об ответственности / Список мошенников

Часовой пояс GMT +4, время: 00:25.

Пишите нам: [email protected]
Copyright © 2024 vBulletin Solutions, Inc.
Translate: zCarot. Webdesign by DevArt (Fox)
G-gaMe! Team production | Since 2008
Hosted by GShost.net