"Монадические" костыли в дотнет
От: Venom  
Дата: 10.06.15 04:51
Оценка:
Тащемта сабж:
        public static TResult Return<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
            where TInput : class
            where TResult : class {
            return o == null ? null :
                   evaluator == null ? null : evaluator(o);
        }

Реализация и имя не канонические.
Оригинал тут: http://www.codeproject.com/Articles/109026/Chained-null-checks-and-the-Maybe-monad
Суть в том, чтобы не городить проверки на null для доставания свойств объектов, если мы не знает является ли объект null или нет.

Пользуетесь ли таким и когда это может быть удобно?
Re: "Монадические" костыли в дотнет
От: Venom  
Дата: 10.06.15 05:04
Оценка: 38 (1)
Здравствуйте, Venom, Вы писали:

V>Пользуетесь ли таким и когда это может быть удобно?


Вчера писал ручной парсинг с Linq2Xml и заметил, что сабж удобен для доставания значений из XElement, если мы не знаем null ли он.
Например:
var value = xElement.Element("Smth").Value; // Possible NRE
var value = xElement.Element("Smth").Return(x=>x.Value); // ok

И всё было хорошо, пока я не заметил, что стенку из
var value = xElement.Element("Smth").Return(x=>x.Value);
var value = xElement.Element("Smth").Return(x=>x.Value);
var value = xElement.Element("Smth").Return(x=>x.Value);

можно убрать, написав банальный метод ниже, и избавиться от лябмд:
public static DataParser {
  public static XElement GetValue(XElement element, string childElementName){
    var childElement = element.Element(childElementName);
    return childElement == null ? null : childElement.Value;
}
}

От лямбд и от необходимости таких методов как Return.

Вот еще нашел у себя старый код, но такое уже антипаттерн и наркоманство :
        private void gridView_RowLoaded(object sender, RowLoadedEventArgs e)
        {
            (e.Row as GridViewRow)
                .Do(row =>
                {
                    foreach (var cell in row.Cells.Where(c => c.Column.UniqueName == "Block"))
                        (cell.Content as CheckBox).Do(cb =>
                        {
                            cb.Checked += ReportBlockCheckBox_Checked;
                            cb.Unchecked += ReportBlockCheckBox_Unchecked;
                        });
                });
        }
Re: "Монадические" костыли в дотнет
От: Venom  
Дата: 10.06.15 06:10
Оценка:
Здравствуйте, Venom, Вы писали:

V>Пользуетесь ли таким и когда это может быть удобно?


Ответ на сабж:
http://sergeyteplyakov.blogspot.ru/2014/07/contracts-vs-monads.html
Re: Elvis operator
От: Qbit86 Кипр
Дата: 10.06.15 06:16
Оценка: 7 (2) +1
Здравствуйте, Venom, Вы писали:

V>Суть в том, чтобы не городить проверки на null для доставания свойств объектов, если мы не знает является ли объект null или нет.


В C# 6 уже появился сахар для null propagation.

V>Пользуетесь ли таким и когда это может быть удобно?


Например, при возбуждении события:
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyProperty)));
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: "Монадические" костыли в дотнет
От: Sinix  
Дата: 10.06.15 06:28
Оценка: +1
Здравствуйте, Venom, Вы писали:


V>И всё было хорошо, пока я не заметил, что стенку из

V>можно убрать, написав банальный метод ниже, и избавиться от лябмд

По-моему, вы сами ответили на свой вопрос, добавить особо нечего

Если в наследство досталась модель данных с цепочкой nullable свойств, то тут ничего не поделаешь: или ждать c#6, или писать свой хелпер для null propagation.
Если модель однотипная — эти хелперы быстро вытеснятся более специализированными, но и более удобными (в нашем проекте то же самое зовётся емнип TryGetElementValue/TryGetAttributeValue).

Префикс 'Try' важен. Иначе у вас будет два набора методов с однотипными названиями, но разным поведением: одни падают, другие возвращают null.

P.S. И да, монады тут ни к селу ни к городу. Вы ж не пишете про абелевы группы в одномерном линейном пространстве при обсуждении "x + y == y + x"?
Re[2]: "Монадические" костыли в дотнет
От: Jack128  
Дата: 10.06.15 06:56
Оценка: +1
Здравствуйте, Venom, Вы писали:

V>Здравствуйте, Venom, Вы писали:


V>>Пользуетесь ли таким и когда это может быть удобно?


V>Вчера писал ручной парсинг с Linq2Xml и заметил, что сабж удобен для доставания значений из XElement, если мы не знаем null ли он.

V>Например:
V>
V>var value = xElement.Element("Smth").Value; // Possible NRE
V>var value = xElement.Element("Smth").Return(x=>x.Value); // ok
V>

V>И всё было хорошо, пока я не заметил, что стенку из
V>
V>var value = xElement.Element("Smth").Return(x=>x.Value);
V>var value = xElement.Element("Smth").Return(x=>x.Value);
V>var value = xElement.Element("Smth").Return(x=>x.Value);
V>

V>можно убрать, написав банальный метод ниже, и избавиться от лябмд:


Можно и нужно убрать. Метод а-ля Return нужен не для Value, а для Element.
Чтобы вместо

var el = doc.Element("el1"); 
if (el != null) el = el.Element("el2"); 
if (el != null) el = el.Element("el3"); 

return el != null ? el.Value : null;

Можно было написать что нить типа
doc.Element("el1").IfNotNull(el => el.Element("el2")).IfNotNull(el => el.Element("el3")).IfNotNull(el => el.Value);

Но сейчас конечно лучше c#6 подождать..
Re[3]: "Монадические" костыли в дотнет
От: Sinix  
Дата: 10.06.15 07:07
Оценка: +2
Здравствуйте, Jack128, Вы писали:

J>Чтобы можно было написать что нить типа

doc.Element("el1").IfNotNull(el => el.Element("el2")).IfNotNull(el => el.Element("el3")).IfNotNull(el => el.Value);


Вообще-то оно должно выглядеть так:
doc.ElementValueAt("el1/el2/el3");
Re[4]: "Монадические" костыли в дотнет
От: Venom  
Дата: 10.06.15 07:43
Оценка: +2
Здравствуйте, Sinix, Вы писали:

S>Вообще-то оно должно выглядеть так:

S>
S>doc.ElementValueAt("el1/el2/el3");
S>

Ну это уже вообще какой-то XPath получается
Re[5]: "Монадические" костыли в дотнет
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.06.15 09:35
Оценка: :)
Здравствуйте, Venom, Вы писали:
V>Ну это уже вообще какой-то XPath получается
Вот именно!
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: "Монадические" костыли в дотнет
От: Venom  
Дата: 10.06.15 10:45
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Venom, Вы писали:


S>По-моему, вы сами ответили на свой вопрос, добавить особо нечего


Справедливости ради нужно отметить что это справедливо только для плоской модели как у меня.
В случае иерархии глубиной более трех, к сожалению возникнет потребность в таких "монадных" хелперах типа IfNotNull, как показал Jack128.
Кстати, у меня в другом проекте заводятся сущности (руками) для всех элементов, а значения хранятся в Attribute.Value, т.е. проблем с вложенностью не возникает, т.к. всегда работа идет на "плоском" уровне.

S>Если в наследство досталась модель данных с цепочкой nullable свойств, то тут ничего не поделаешь: или ждать c#6, или писать свой хелпер для null propagation.

Да, такая модель данных и досталась. Но имея только xml без схемы (кстати, не уверен, поможет ли она тут) по-другому модель и не опишешь, только через nullable свойства, потому что всегда может не оказаться какого-либо элемента/атрибута.

S>Если модель однотипная — эти хелперы быстро вытеснятся более специализированными, но и более удобными (в нашем проекте то же самое зовётся емнип TryGetElementValue/TryGetAttributeValue).

+1

S>Префикс 'Try' важен. Иначе у вас будет два набора методов с однотипными названиями, но разным поведением: одни падают, другие возвращают null.

+1

S>P.S. И да, монады тут ни к селу ни к городу. Вы ж не пишете про абелевы группы в одномерном линейном пространстве при обсуждении "x + y == y + x"?

+1

ЗЫ. > Ваш лимит оценок данному пользователю на сегодня исчерпан.
Первый раз достиг этого предела
Отредактировано 10.06.2015 10:48 Venom . Предыдущая версия .
Re[3]: "Монадические" костыли в дотнет
От: xy012111  
Дата: 10.06.15 13:52
Оценка: 4 (1)
Здравствуйте, Sinix, Вы писали:

S>Если модель однотипная — эти хелперы быстро вытеснятся более специализированными, но и более удобными (в нашем проекте то же самое зовётся емнип TryGetElementValue/TryGetAttributeValue).

S>Префикс 'Try' важен. Иначе у вас будет два набора методов с однотипными названиями, но разным поведением: одни падают, другие возвращают null.

А не смущает, что префикс Try обычно применяется в методах a-la Try/Parse pattern? Сам для отличия от Get, которые всегда возвращают не-нулевое значение, применяю Find, то есть будет FindElementValue/FindAttributeValue. ИМХО, и симпотичнее (не двух-составной префикс, не путает с Try/Parse) и Find, как мне кажется, подразумевает, что результат может быть и не найден.
Re[4]: "Монадические" костыли в дотнет
От: Sinix  
Дата: 10.06.15 14:07
Оценка: 2 (2) +1
Здравствуйте, xy012111, Вы писали:

S>>Префикс 'Try' важен. Иначе у вас будет два набора методов с однотипными названиями, но разным поведением: одни падают, другие возвращают null.

X>А не смущает, что префикс Try обычно применяется в методах a-la Try/Parse pattern?
Их явно отличает out-параметр и bool-результат. У неподготовленных разработчиков оба варианта никаких вопросов не вызывают, это главное.


X>Сам для отличия от Get, которые всегда возвращают не-нулевое значение, применяю Find, то есть будет FindElementValue/FindAttributeValue.

Этот вариант похуже на мой взгляд.

Во-первых, вгоняет в глубокий когнитивный диссонанс, если используются парные методы. Сравни GetSomething()/TryGetSomething() и GetSomething()/FindSomething(). Хотя Find() и без парных непонятен, т.к. практически нигде не используется.
Во-вторых, иногда Find не подходит, например,
var response = TrySendRequest(url)

Придумывать для подобных случаев частные случаи — прямой путь к абсолютно непонятному API.
Re[5]: "Монадические" костыли в дотнет
От: xy012111  
Дата: 10.06.15 14:25
Оценка: 8 (1) +1
Здравствуйте, Sinix, Вы писали:

S>>>Префикс 'Try' важен. Иначе у вас будет два набора методов с однотипными названиями, но разным поведением: одни падают, другие возвращают null.

X>>А не смущает, что префикс Try обычно применяется в методах a-la Try/Parse pattern?
S>Их явно отличает out-параметр и bool-результат. У неподготовленных разработчиков оба варианта никаких вопросов не вызывают, это главное.

В том-то и дело, что паттерн для Try методов есть. но ваш Try-метод в него не вписывается. Кажется, это не очень здорово для API, если можно сделать иначе.

X>>Сам для отличия от Get, которые всегда возвращают не-нулевое значение, применяю Find, то есть будет FindElementValue/FindAttributeValue.

S>Этот вариант похуже на мой взгляд.

S>Во-первых, вгоняет в глубокий когнитивный диссонанс, если используются парные методы. Сравни GetSomething()/TryGetSomething() и GetSomething()/FindSomething().


А в чём диссонанс? "Получить" и "Найти" — вроде ясно всё, если минимумом языка владеть.

S>Хотя Find() и без парных непонятен, т.к. практически нигде не используется.


Если уж на то пошло, давайте сравним, где и как используется TryGet и Find И увидим, что именно ваш TryGet скорее подкованного человека введёт в диссонанс. Используется без out-параметра очень изредко, и в глубоких дебрях. А вот Find всегда одинаково и довольно-таки на поверхности.

S>Во-вторых, иногда Find не подходит, например,

S>var response = TrySendRequest(url)


Конечно тяжело говорить, не видя всего анамнеза, но мне кажется, что и такой Try здесь не фонтан. Например, SendRequest(url), всегда возвращающий некий response с кодом ошибки, который скажет о причине неудавшейся отправки может быть лучшим решением.

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

S>Придумывать для подобных случаев частные случаи — прямой путь к абсолютно непонятному API.


Спасибо за ваш вывод, я понял.
Re[4]: "Монадические" костыли в дотнет
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.06.15 18:59
Оценка: 4 (1) +2
Здравствуйте, Venom, Вы писали:

V>Здравствуйте, Sinix, Вы писали:


S>>Здравствуйте, Venom, Вы писали:


S>>По-моему, вы сами ответили на свой вопрос, добавить особо нечего


V>Справедливости ради нужно отметить что это справедливо только для плоской модели как у меня.

V>В случае иерархии глубиной более трех, к сожалению возникнет потребность в таких "монадных" хелперах типа IfNotNull, как показал Jack128.
Ну, если нет возможности применить что-то типа XPath, а быстродействие — неважно, то можно сделать и более сложный хелпер, способный сократить цепочку Jack128 до
doc.Element("el1").IfNotNull(el => el.Element("el2").Element("el3").Value);

На основе, собсно, Expression Tree, в котором операторы "." переписываются в ".?".

S>>Если в наследство досталась модель данных с цепочкой nullable свойств, то тут ничего не поделаешь: или ждать c#6, или писать свой хелпер для null propagation.

V>Да, такая модель данных и досталась. Но имея только xml без схемы (кстати, не уверен, поможет ли она тут) по-другому модель и не опишешь, только через nullable свойства, потому что всегда может не оказаться какого-либо элемента/атрибута.
C xml цепочками оперировать как раз легко — благодаря XPath.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: "Монадические" костыли в дотнет
От: Venom  
Дата: 11.06.15 03:40
Оценка:
Здравствуйте, xy012111, Вы писали:

X>А в чём диссонанс? "Получить" и "Найти" — вроде ясно всё, если минимумом языка владеть.


По-моему, вариант Sinix немного лучше.
Вот почему:
В случае Get/Find не сразу видна их связь (один падает, другой возращает null).
В случае Get/TryGet эта связь видна и есть поверхностная ассоциация с Try/Parse паттерном, который работает схожим образом. И из-за этой поверхностной ассоциации такой вариант предпочтительнее в плане читабельности и интуитивности использования.
Re[7]: "Монадические" костыли в дотнет
От: fddima  
Дата: 11.06.15 03:49
Оценка: 4 (1) +2
Здравствуйте, Venom, Вы писали:

V>В случае Get/TryGet эта связь видна и есть поверхностная ассоциация с Try/Parse паттерном, который работает схожим образом. И из-за этой поверхностной ассоциации такой вариант предпочтительнее в плане читабельности и интуитивности использования.

А ещё в MSVS почему-то никак не могут дойти со своими подсказками, что секция returns из xmldoc имеет значение, как раз для того что бы быстро обнаруживать подобную инфромацию (возвращается null или всё таки исключение). До смешного же доходит — summary показываем, аргументы — показываем (хотя часто как раз там и названий хватает), а возврат — нет.

PS: Да-да, я знаю, что взрослые расширения наподобии решарпера показывают.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[8]: "Монадические" костыли в дотнет
От: Venom  
Дата: 11.06.15 04:58
Оценка:
Здравствуйте, fddima, Вы писали:

F> А ещё в MSVS почему-то никак не могут дойти со своими подсказками, что секция returns из xmldoc имеет значение, как раз для того что бы быстро обнаруживать подобную инфромацию (возвращается null или всё таки исключение). До смешного же доходит — summary показываем, аргументы — показываем (хотя часто как раз там и названий хватает), а возврат — нет.


F> PS: Да-да, я знаю, что взрослые расширения наподобии решарпера показывают.


Хм, у меня 7.0.1 решарпер. Суспенчу, не вижу разницы в тултипе MSVS между включенным и выключенным состояниями R#. Видимо, древноват он у меня.
Согласен, в тултипе MSVS было бы удобно видеть информацию из returns и из exceptions.
Re[6]: "Монадические" костыли в дотнет
От: Vladek Россия Github
Дата: 11.06.15 06:34
Оценка:
Здравствуйте, xy012111, Вы писали:

X>Здравствуйте, Sinix, Вы писали:


S>>>>Префикс 'Try' важен. Иначе у вас будет два набора методов с однотипными названиями, но разным поведением: одни падают, другие возвращают null.

X>>>А не смущает, что префикс Try обычно применяется в методах a-la Try/Parse pattern?
S>>Их явно отличает out-параметр и bool-результат. У неподготовленных разработчиков оба варианта никаких вопросов не вызывают, это главное.

X>В том-то и дело, что паттерн для Try методов есть. но ваш Try-метод в него не вписывается. Кажется, это не очень здорово для API, если можно сделать иначе.


Паттерн тут простой — попытаться сделать операцию без исключительных ситуаций. Какие там параметры и как они передаются — не важно.
Re[2]: Elvis operator
От: Vladek Россия Github
Дата: 11.06.15 06:37
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Здравствуйте, Venom, Вы писали:


V>>Суть в том, чтобы не городить проверки на null для доставания свойств объектов, если мы не знает является ли объект null или нет.


Q>В C# 6 уже появился сахар для null propagation.


А почему Elvis? Типа задаём вопрос has Elvis left the building yet?
Re[3]: Elvis operator
От: Qbit86 Кипр
Дата: 11.06.15 07:13
Оценка:
Здравствуйте, Vladek, Вы писали:

V>А почему Elvis?


«Since it first appeared in related languages, this has been known as the Elvis operator due to its resemblance to an emoticon... Groovy uses the '?:' operator, referred to as the "Elvis Operator" in the language documentation.»

Короче, графема ?. похожа на Элвиса (глаза и причёска).
Глаза у меня добрые, но рубашка — смирительная!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.