Diff между чистый ADO.NET vs Controllable Query

6yrop


где
 
 
public static class Func
{
public static TResult Apply<T, TResult>(T arg, Func<T, TResult> func)
{
return func(arg);
}
}

public class TypeCommand<T>
{
public readonly SqlCommand SqlCommand;
public TypeCommand(SqlCommand sqlCommand)
{
SqlCommand = sqlCommand;
}
}

public class Param<T>
{
public readonly Option<T> Value;
public Param(Option<T> value)
{
Value = value;
}
}

public static class Extensions
{
public static Param<T> Param<T>(this T it)
{
return new Param<T>(it);
}

public static TypeCommand<T> SetCommandText<T>(this SqlCommand it, string commandText, T recordPrototype)
{
if (!string.IsNullOrEmpty(it.CommandText throw new ApplicationException;
it.CommandText = commandText;
return new TypeCommand<T>(it);
}

[CanGetParamValue]
public static SqlParameter Add(this SqlCommand it, string name, Param<string> param)
{
var parameter = new SqlParameter {ParameterName = name, SqlDbType = SqlDbType.NVarChar, Size = -1};
if (param.Value.HasValue) parameter.Value = param.Value.Value;
return it.Parameters.Add(parameter);
}
}

Причем Controllable Query будет заставлять записать код именно в таком виде, иначе будет выдавать сообщение о том, что следует изменить.
При использовании Controllable Query связи между фрагментами кода, показанные на рисунке стрелками, перемещаются из памяти программиста в память машины. Тем самым память программиста разгружается для более эффективного использования. :)

ppplva

А это кто-нибудь использует? Кроме тебя, конечно. Просто в интернете по "Controllable query" находятся два каких-то стремных doc-документа на русском и все. Я даже исходников не обнаружил.

apl13

Ты должен медитировать ежедневно хотя бы по шесть часов, чтобы постичь суть Controllable Query! :pop:

kokoc88

А это кто-нибудь использует?
Никому не хочется 6 строк кода менять на 12.

6yrop

Никому не хочется 6 строк кода менять на 12.

var firstName = Console.ReadLine;
using (var connection = new SqlConnection(""
{
connection.Open;
using (var command = new SqlCommand {Connection = connection})
{
var commandText = "SELECT PersonId, FirstName, LastName FROM Person WHERE 1=1";
if (string.IsNullOrEmpty(firstName
{
commandText += " AND FirstName = @FirstName";
command.Parameters.AddWithValue("@FirstName", firstName);
}
command.CommandText = commandText;
using (var reader = command.ExecuteReader
while (reader.Read
{
var personId = (int) reader["PersonId"];
var firstName2 = (string) reader["FirstName"];
var lastName = (string) reader["LastName"];
}
}
}


var firstName = Console.ReadLine;
using (var connection = new SqlConnection(""
{
connection.Open;
using (var command = new SqlCommand {Connection = connection})
{
var typeCommand = Func.Apply(
new {firstName = firstName.Nothing(string.IsNullOrEmpty).Select(_ => _.Param command},
p => {
var commandText = "SELECT PersonId, FirstName, LastName FROM Person WHERE 1=1";
if (p.firstName.HasValue)
{
commandText += " AND FirstName = @FirstName";
p.command.Add("@FirstName", p.firstName.Value);
}
return p.command.SetCommandText(commandText, new {
PersonId = default(int
FirstName = default(string
LastName = default(string)
});
});
using (var reader = command.ExecuteReader
while (reader.Read
{
var record = reader.Materialize(typeCommand);
}
}
}

Смотрим на последние номера строк, получаем 22 против 28. Как ты получил свои цифры "6 строк кода менять на 12"?

6yrop

отвечу через несколько дней

kokoc88

Смотрим на последние номера строк, получаем 22 против 28. Как ты получил свои цифры "6 строк кода менять на 12"?
Отбери у машины свой разум, она не справляется.

6yrop

ты можешь рассказать, как ты получил "6 против 12"?

kokoc88

ты можешь рассказать, как ты получил "6 против 12"?
Я в тебя верю, просто выгрузи разум из памяти машины обратно в мозг.

6yrop

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

kokoc88

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

6yrop

6 из 22-х строк заменит на 12, и посмотреть на результат её работы
откуда берется 6? почему 6? какие это строки?

6yrop

спасибо :)

6yrop

Никому не хочется 6 строк кода менять на 12.
в статически типизированных языках "лишние" символы обычное дело. Есть кому это помогает, есть кто этого не понимает.

kokoc88

firstName = firstName.Nothing(string.IsNullOrEmpty).Select(_ => _.Param
И, о мои битовые боги, писать такое вместо new Param(firstName)?
Уж лучше оставаться программистом на тёплой ламповой Java.

6yrop

вместо new Param(firstName)?
ты не понял, это совсем не то. Та строчка эта работа с монадой http://en.wikipedia.org/wiki/Option_type

kokoc88

ты не понял, это совсем не то. Та строчка эта работа с монадой http://en.wikipedia.org/wiki/Option_type
Ты не понял, это совсем не то. Ты строчка - это кусок гавнокода, который в нормальном проекте нужно стереть и заменить на нормальный: http://en.wikipedia.org/wiki/Code_smell

6yrop

Ты строчка - это кусок гавнокода, который в нормальном проекте нужно стереть и заменить на нормальный: http://en.wikipedia.org/wiki/Code_smell
а ты пользуешься Option type-ом?

Maurog

Тем самым память программиста разгружается
ценность данного топика меньше нуля имхо :shocked:

6yrop

если бы ты оставил комментарии, почему это так? То было бы совсем хорошо.

kokoc88

если бы ты оставил комментарии, почему это так? То было бы совсем хорошо.
Если бы ты слушал комментарии, то было бы совсем хорошо. А так ты несколько раз в год вылазишь сюда в поисках признания, раз за разом становясь пищей для тролля. Я твои посты использую для развлечения: своего и коллег на работе.

6yrop

Это не тебе сообщение, от тебя комментариев по делу не дождешься.

6yrop

2 Зачем ты снес про Option type, это имеет непосредственное отношение к теме треда .

kokoc88

Это не тебе сообщение, от тебя комментариев по делу не дождешься.
Ты ни от кого их не дождёшься, потому что нет никакого дела. Пора бы .

6yrop

Я твои посты использую для развлечения: своего и коллег на работе.
Поглумиться над полусумасшедшим C#-кодировщиком, ты и твои коллеги не в состоянии найти более интересного, увлекательного развлечения?
Ты, конечно, сильно мешаешь, засерая мои треды, но факт остается фактом, тебе это до сих пор не надоело, значит задевает.

6yrop

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

Dasar

в таком виде не имеет, т.к. есть лишь личные терки и нет никакого обсуждения кода.
Если же все-таки вернутся к коду и к его конструктивному обсуждению, то:
во-первых, претензии у -а были к форме кода, а не к использованию OptionType, т.е. необходимо показать, что если использовать OptionType, то по другому оформить код не получается
во-вторых, желательно показать, зачем в данном случае применяется OptionType, и какие бонусы это дает.

kokoc88

ну и отъебись от меня, зачем снова и снова засираешь мои треды?
Заигнорь меня снова, детка. Хотя тогда твои треды будут для тебя пусты. Вопрос в том, что больше тебя огорчает.

6yrop

я тебя разигнорил, когда искал что-то в прошлых тредах, с игнором было не удобно искать.

kokoc88

Вам устное замечание. Флуд в тематическом разделе. если нет дела, то проходи мимо. Прекращай, иначе будет readonly, чтобы остыл
Когда люди постят в раздел по делу, получают ответ по делу: , , , .
Когда раз в пол года спамят какое-то гавно, игнорируя разумные комментарии, любой имеет право ответить как угодно. Конструктивные обсуждения темы Шурика закончились где-то в 2009 году. Теперь это местный Аджил.
С угрозами о read only я посылаю тебя на хуй. Могу не постить сюда ничего, мне в принципе плевать, пусть раздел и дальше скатывается в унылое гавно.

6yrop

претензии у -а были к форме кода
самая обычная для дотнета форма http://blogs.msdn.com/b/wesdyer/archive/2008/01/11/the-marve...

6yrop

С угрозами о read only я посылаю тебя на хуй. Могу не постить сюда ничего, мне в принципе плевать, пусть раздел и дальше скатывается в унылое гавно.
да, именно так и сделай

6yrop

во-вторых, желательно показать, зачем в данном случае применяется OptionType, и какие бонусы это дает.
фрагмент кода, содержащий формирование sql-строки, становится самоописывающимся. В такой записи виден входной параметр Option<Pram<string>>, т.е. значение либо присутствует, либо отсутствует. Если просто string, то специальное значение null как бы скрыто.

Dasar

у тебя сейчас получается "перевернутая" последовательность: сначала значение, а потом его "заворачивание" в тип, что в стиле кодирования под c# не часто увидешь.

firstName = firstName.Nothing(string.IsNullOrEmpty).Select(_ => _.Param

тоже самое можно и как-то так оформить:

firstName = cq.Param(firstName)
.With_NothingChecker(string.IsNullOrEmpty)

такое ближе к парадигме создания объекта с последующей настройкой

Dasar

Если просто string, то специальное значение null как бы скрыто.
т.е. если добавляем Nothing, то null-овое значение параметра трактуется что по данному параметру искать не надо, если же Nothing убрать, то будут искаться записи где данный параметр null?

6yrop

у тебя сейчас получается "перевернутая" последовательность: сначала значение, а потом его "заворачивание" в тип, что в стиле кодирования под c# не часто увидешь.
если совсем как в той статье, то вот так
 
from _ in firstName.AsOption where !string.IsNullOrEmpty(_) select _.Param

вместо

firstName.Nothing(string.IsNullOrEmpty).Select(_ => _.Param

Dasar

если совсем как в той статье, то вот так
так явно же в прикладном коде не хочется видеть весь этот монадный мусор вообще. Он должен быть за кадром.
вместо всех этих монадных ухищрений хочется видеть что-то осмысленное

6yrop

так явно же в прикладном коде не хочется видеть весь этот монадный мусор вообще. Он должен быть за кадром.
фактически в самом условии задачи: "значение может присутствовать может отсутствовать", т.е. may be. Поэтому, либо ты это условие записываешь в явном виде (в виде монады и машина делает за тебя остальную работу, либо сам пишешь тесты, в которых прописываешь специальные значения null, empty. При это человек может просто забыть написать тест или ошибиться.

Dasar

Поэтому, либо ты это условие записываешь в явном виде (в виде монады)
вот ниже запись в явном виде, при этом весь монадный мусор скрыт внутри OptionalParam:

{firstName = cq.OptionalParam(firstName, string.IsNullOrEmpty command}

6yrop

т.е. ты хочешь сделать вот такой Extract Method
 
public static Option<Param<T>> OptionalParam<T>(T value, Func<T, bool> predicate)
{
return value.Nothing(predicate).Select(_ => _.Param;
}

да пожалуйста, делай.

Dasar

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

6yrop

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

6yrop

хочется, чтобы это уже было сделано в библиотеке
думаю, что библиотеки вообще не будет. Будет один cs-файл с оберточными классами Func, Param, TypeCommand и отдельно тулза для автотестов, навигации по коду и т.д., т.е. в настоящем рантайме она не участвует. Мне кажется, этот одни файл будет совсем небольшим, и пользователь его в состоянии просмотреть и понять как всё работает. Поэтому не хочется дополнительных нагромождений.

Dasar

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

Dasar

, и пользователь его в состоянии просмотреть и понять как всё работает. Поэтому не хочется дополнительных нагромождений.
важнее, чтобы конечный код был простым и лаконичным. Именно его же приходится постоянно читать, писать и разбираться с ним.
например, взять тот же C++ - неважно же какой код стоит за operator-ами ввода/вывода >>, <<, и неважно, что изначально они использовались для сдвига битов. Главное, что удалось сделать простой и понятный синтаксис, которым легко и понятно пользоваться.

6yrop

Прокомментирую, почему я не сразу понял о какой 6-ке строк идет речь. Обсуждение началось с вот этой пары фраз:
 
А это кто-нибудь использует?
 
Никому не хочется 6 строк кода менять на 12.
  

  

В итоге оказалось, что речь идет о замене последовательности символом:
 

if (string.IsNullOrEmpty(firstName
command.Parameters.AddWithValue("@FirstName", firstName);
command.CommandText = commandText;
var personId = (int) reader["PersonId"];
var firstName2 = (string) reader["FirstName"];
var lastName = (string) reader["LastName"];

на последовательность символов:
 

var typeCommand = Func.Apply(
new {firstName = firstName.Nothing(string.IsNullOrEmpty).Select(_ => _.Param command},
p =>
if (p.firstName.HasValue)
p.command.Add("@FirstName", p.firstName.Value);
return p.command.SetCommandText(commandText, new {
PersonId = default(int
FirstName = default(string
LastName = default(string)
});
var record = reader.Materialize(typeCommand);

Обе последовательности не являются осмысленной компьютерной программой (решающей какую либо задачу). В силу чего они не имеют отношения к первоначальной фразе, где шла речь об использовании моего проекта. Я предполагал, что разговариваю с человеком, а не с тролем, поэтому думал, что он разумен и отслеживает цепочку разговора.
Поскольку рисунка перед глазами у меня не было (я не пересматривал его, поскольку хорошо помнил его смысл то у меня не было возможности увидеть число 6 на рисунке. Как только мне его , я сразу увидел.

kokoc88

Обе последовательности не являются осмысленной компьютерной программой (решающей какую либо задачу). В силу чего они не имеют отношения к первоначальной фразе, где шла речь об использовании моего проекта. Я предполагал, что разговариваю с человеком, а не с тролем, поэтому думал, что он разумен и отслеживает цепочку разговора.
Боже, сколька букафф и целый час возни только ради того, чтобы оправдать факт, что ты ступил. Проще было написать: "Ой, я ступил". Все бы это поняли и нормально восприняли.
Два плюс два ты скадываешь, видимо, так?
Ints.Two.AsOption(_ =_!= 0).Select(_ => _.ForPlus.ThowIfNull(first => first.Plus(Ints.Two.AsOption(_ =_!= 0).Select(_ => _.ForPlus.ThrowIfNull(_ => _.Value

99.999% программистов делают это вот так:
2+2

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

6yrop

Если хочешь, чтобы твоя библиотека получила хороший отзыв от профессионалов, она должна делать так же. Нужен простой, понятный и короткий код, а не слюни "к этому синтаксису нужно привыкнуть, и тогдаааааа".
Профессионалы на то и профессионалы, чтобы отличать trade-off от нытья. Проблема тянется с 70-х годов, и профессионалы должны видеть за что платим теми +6 строчками. Вот пример профессионального взгляда на проблему http://cseweb.ucsd.edu/~lerner/papers/quail.pdf

6yrop

Да, в нашем случае имеет смысл убрать монадический синтаксис из презентационных примеров, заменив
 
 
new {firstName = firstName.Nothing(string.IsNullOrEmpty).Select(_ => _.Param command},

на
 
 
new {firstName = firstName.OptionalParam(!string.IsNullOrEmpty(firstName command},

Dasar

firstName.OptionalParam(!string.IsNullOrEmpty(firstName
почему не просто: firstName.OptionalParam(string.IsNullOrEmpty) ?

6yrop

Боже, сколька букафф и целый час возни только ради того, чтобы оправдать факт, что ты ступил. Проще было написать: "Ой, я ступил". Все бы это поняли и нормально восприняли.
Ни чего подобно, ни какого "я с тупил" не было, твоя фраза "6 против 12" не имела и не имеет отношения к контексту обсуждения, чистый тролинг.

6yrop

почему не просто: firstName.OptionalParam(string.IsNullOrEmpty) ?
потому что второй аргумент в этом случае надо интерпретировать как условие, при котором НЕ создается параметр.

6yrop

есть такое. но в c#, в итоге, синтаксис получается ужасный, поэтому лучше когда библиотека позволяет записать код без ручного указания монадических преобразований
Можешь пояснить, что ты называешь монадным мусором? По моему, смысл Maybe монады как раз в том, что в начале мы входим в нее, а затем передаем ее в цепочку вызовов M1->M2->...->Mn; и прелесть в том, что на любом шаге цепочки Mi мы можем сделать частичные вычисления. Это и отражено в синтаксисе .Select(функция для частичного вычисления). Т.е. синтаксис хорошо сочетается со смыслом монады. А прятать сам смысл, что внутри там Maybe монада, это только запутывает. То, что у некоторых нет в их наборе инструментов Maybe монады, это их профессиональные проблемы. Она имеет широкое применение и вне Controllable Query.
Иногда требуется вот такое searchText.Nothing(string.IsNullOrEmpty).Select(_ => new { Field1 = _.Param Field2 = (_ + "%").Param })

6yrop

внутри там Maybe монада
и это отражение ключевого момента Controllable Query; шарящий программист сразу догадается, что тут можно применять и такие типы как .

kokoc88

Профессионалы на то и профессионалы, чтобы отличать trade-off от нытья.
В первую очередь они хорошо отличают boilerplate, у тебя же это напрочь отсутствует. Заметь, ты не первый раз постишь в этот раздел в поисках признания и каждый раз получаешь в тык, потому что твой код ужасен. Тебе давно пора уже смириться с тем, что ты не умеешь делать хороших фреймворков и являешься обычным кодером. Тебя троллят не столько за то, что ты не можешь понять аргументированные комментарии. Тебя троллят за то, что ты упорно продолжаешь считать, что "все вокруг ничего не понимают".
Ни чего подобно, ни какого "я с тупил" не было, твоя фраза "6 против 12" не имела и не имеет отношения к контексту обсуждения, чистый тролинг.
Нет, эта фраза вообще не была троллингом, она была полна конструктива и точно объясняла, почему никто не хочет пользоваться твоим поделием.

6yrop

продолжаешь считать, что "все вокруг ничего не понимают"
пока это только к тебе относится

6yrop

Нет, эта фраза вообще не была троллингом, она была полна конструктива и точно объясняла, почему никто не хочет пользоваться твоим поделием.
расскажи какое отношение вот эти 6 строчек символов имеют к оценке моего проекта?

if (string.IsNullOrEmpty(firstName
command.Parameters.AddWithValue("@FirstName", firstName);
command.CommandText = commandText;
var personId = (int) reader["PersonId"];
var firstName2 = (string) reader["FirstName"];
var lastName = (string) reader["LastName"];

6yrop

если будет 1000 против 1006 оценка будет лучше или хуже?

kokoc88

пока это только к тебе относится
Я тебе рекомендую выгрузить мозг из машины в голову, изучить следующие посты и сделать наконец-то правильные выводы. Мне так интересно, можно ли Аджилу что-то доказать, что я даже воспользуюсь поиском по + Development:
 
 
 
 
 
 
 ,
 

 
Если ты не сможешь сделать правильные выводы по моим ссылкам, то попроси -я сделать голосовалку по поводу твоих потуг под грифом Controllable Query.

6yrop

прокликал. Да у тебя целое досье на меня. Мое чсв растет от такого количества внимания.

kokoc88

прокликал. Да у тебя целое досье на меня. Мое чсв растет от такого количества внимания.
Это "досье" есть сверху по ссылке "Search". Если у тебя от этого растёт ЧСВ (хотя лично я думаю, что на самом деле ты сейчас испытываешь нереальный баттхёрт то можешь ещё поискать темы, где ты слился. Они все очень похожи, так что гуляй и не плоди новых.
В качестве констуктивного обсуждения, которого так требует , я бы хотел защитить этот и предыдущий посты как on-topic. Потому что мне надоел спамм под тематикой "Controllable Query", каждая тема которого по сути своей содержит одно и то же ("посмотрите, как я крут"): бесполезное сравнение CQ с Dapper, бесполезный никому не нужный diff с кодом на ADO.NET, бесполезный комментарий о CQ и lambda lifting, и так далее. Единственное осмысленное действие каждого поста - привлечь внимание к Controllable Query. При этом сама библиотека за несколько лет вообще никак не меняется, а гавнокод остаётся гавнокодом.

6yrop

Это "досье" есть сверху по ссылке "Search".
наверное, Золушка тоже так думала, отбирая зернышки

kokoc88

Не знаю, о чём думала Золушка, зёрнышки за неё отбирали птички. Ты всё равно плодишь одинаковые темы, все их сливаешь, и во всех кто-нибудь тебя "не понимает".

6yrop

Предлагаю друг друга заигнорить. Я так и сделаю.

kokoc88

Предлагаю друг друга заигнорить. Я так и сделаю.

Ну вот оно и ЧСВ, оказавшееся на самом деле баттхёртом. Что и требовалось доказать.

Dasar

Можешь пояснить, что ты называешь монадным мусором? По моему, смысл Maybe монады как раз в том
при хорошем синтаксисе, смысл монады Maybe в том, что мы один раз помечаем, что значение опциональное, а дальше с ним работаем как с обычным значением. к сожалению, синтаксис C# такое не позволяет записать.
например, есть следующий код:

double x = 2.0;
var y = 3.0;
var z = Math.Pow(x, y);
Console.WriteLine(z);
var r = Math.Sqrt(z) + 1;

и если нам захотелось сделать x опциональным значением, то код должен и остаться таким же:

Maybe<double> x = GetOptionalParameterX;
var y = 3.0;
var z = Math.Pow(x, y);
Console.WriteLine(z);
var r = Math.Sqrt(z) + 1;

тип переменных z, r при этом станет Maybe<double>, а Console.WriteLine при Nothing-значении либо будет пропускаться, либо выведет пустую строку, в зависимости от того, есть у него перегрузка по Maybe<T> или нет
в C# же придется это записать следующим образом:

Maybe<double> x = GetOptionalParameterX;
var y = 3.0;
var z = x.Select(_ => Math.Pow(_, y;
z.Select(_ => Console.WriteLine(_;
var r = z.Select(_ => Math.Sqrt(_) + 1);

вот все эти Select-ы я и называю монадным мусором, который по смыслу не нужен, а нужен лишь для того, чтобы потрафить недоделанности c#-компилятора.
Соответственно, для того, чтобы все-таки на C# иметь чистую читабельную запись (и не указывать постоянно Select) - требуется на уровне библиотеки нагенерить кучу заглушечных перегрузок функций

luna89

В чем смысл Option в C#? В хаскеле понятно - там ссылка не может быть null. В C# любое значение может быть null, это значит что оно уже Option. А что делать если вместо Option передадут null?

Dasar

А что делать если вместо Option передадут null?
тип Option делается структурой, и тогда переменная типа Option<T> не может быть null-евой.
если же T-переменная null-евая, то она Option-конструктором маркируется как Nothing
смысл в том, что T)null).ToString сделать нельзя (кинет exception а Option<T>.Nothing.ToString сделать можно (вернет Option<string>.Nothing, при условии что нет других перегрузок)

6yrop

А что делать если вместо Option передадут null?
я в последнее время сделал Option структурой, теперь null компилятор не пропустит. Моей мотивацией было то, что теперь в параметрах по умолчанию можно использовать Nothing.

void Method1(Option<string> arg = new Option<string>
{
}

К сожалению, для ref типов параметры по умолчанию могу принимать только null (не считая string-и).

luna89

вот все эти Select-ы я и называю монадным мусором, который по смыслу не нужен, а нужен лишь для того, чтобы потрафить недоделанности c#-компилятора.
Я правильно понимаю, что если доделать C# компилятор, то Nothing будет распространяться автоматически? В результате, если куда-то случайно попал Nothing, то мы не получим сразу NullPointerException, а вместо этого на выходе будет Nothing, совершенно не понятно откуда взявшийся.

luna89

Шурик, а ты когда-нибудь слышал об SQLJ? Над ним работали, как утверждает википедия, компании IBM, Oracle, Compaq, Informix, Sybase, Cloudscape и Sun Microsystems. Почему же такая замечательная вещь не встретила признания? Может быть, дело не только в том, что косные разработчики не способны узреть свет истины?

luna89

Вспомнился к слову этот пост.

Dasar

то Nothing будет распространяться автоматически? В результате, если куда-то случайно попал Nothing
Зависит от кода самой монады. Например, для отладочных целей можно сделать, чтобы монада запоминала в результате какого вызова был получен первый раз Nothing.
Также основное отличие от null, что null может появиться в любом месте и на этапе компиляции это никак не проверяется, а Nothing может появиться только в том коде, где это явно указано.

Dasar

ты когда-нибудь слышал об SQLJ?
судя по беглому чтения wiki, они не решили одну из основных часто встречающихся задач: сборка запроса из кусочков.
Причем, если с raw-sql эта задача хотя бы решается через ручную склейку строк, то здесь она, по идее, вообще не решается.

luna89

Зависит от кода самой монады. Например, для отладочных целей можно сделать, чтобы монада запоминала в результате какого вызова был получен первый раз Nothing.
Как ты считаешь, удобно ли это? А в продакшене?

luna89

The Oracle SQLJ implementation also offers extensions to support dynamic SQL operations, which are not predefined and the operations can change in real time.

Dasar

Как ты считаешь, удобно ли это? А в продакшене?
я много работаю с кодом, где опциональные параметры - это норма. И, конечно, намного проще один раз использовать монаду Maybe, чем руками ловить null-и в каждой строчке кода.

6yrop

Шурик, а ты когда-нибудь слышал об SQLJ?
я не то что слышал, более того я год назад активно гуглил эту тему, и на твой вопрос у меня есть уже нагугленный ответ в явном виде:
 
Embedded Queries Explicit queries may be embedded within the program-
ming language or handled by a preprocessor [27]. Examples include SQLJ [8].
Embedded SQL provides a statically-typed approach to explicit query execu-
tion. One significant drawback of embedding is that it does not support dynamic
queries, as discussed in Section 7
. Another problem is that the change to the syn-
tax of a programming language typically break other tools, including refactoring
tools, IDEs, and CASE tools.
...
We conjecture that lack of support for dynamic queries is the primary
reason for abandonment of most forms of embedded SQL [27].

http://www.odbms.org/download/010.03%20Cook%20Integrating%20...
  

Я же изначально позиционировал проект для динамического SQL, иначе можно обходиться хранимками и кодогенерацией.
 
IBM, Oracle, Compaq, Informix, Sybase, Cloudscape и Sun Microsystems

Касательно известных компаний, это мне напомнило мой вопрос знакомому java-сту, уехавшему в штаты:
Я: - а ты не в курсе IBM-вский проект pureQuery востребован в мире Java? Какова его популярность?
Он: - Тут обычно опенсурс рулит, а не всякие поделки этих корпораций ...
Мой акцент не на опенсорсе, просто напомнило контраст моего и его отношения к корпорациям.

luna89

И, конечно, намного проще один раз использовать монаду Maybe, чем руками ловить null-и в каждой строчке кода.
Зачем их ловить? Вылетел NullPointerException - значит ты неправильно используешь API. Исправь свой код и работай дальше.

Dasar

The Oracle SQLJ implementation also offers extensions to support dynamic SQL operations, which are not predefined and the operations can change in real time.
примеры какие-нибудь есть?
для начала на банальные вещи:
имя таблицы как переменная, имя столбца как переменная, набор столбцов как переменная,
более сложные:
условие как переменная, набор условий как переменная, набор полей для сортировки как переменная
экзотика:
смешивание в одном выражении sqlj и sql-raw, подтягивание поля из другой таблицы с автоматическим выполнением join-а, поддержка аналитических функций, поддержка работы с xml(FOR XML, OPENXML, XQuery) и т.д.

luna89

и на твой вопрос у меня есть уже нагугленный ответ в явном виде

Если инструмент решает 90% задач, то никто не будет менять его на другой инструмент. Ручная склейка решает 90% задач.
Возьмем для примера юникс утилиты. Некоторые из них принимают аргумент -0, некоторые нет (например grep). Казалось бы, надо во все утилиты добавить такую возможность. Но всем похуй. Изредка скрипты ломаются из-за пробелов в именах файлов - но это не большая проблема. Подумай, если grep не починили за 40 лет, а динамический sql за 30 лет, то какова вероятность что кто-то когда-то вообще захочет это чинить?

luna89

примеры какие-нибудь есть?
Нет примеров, потому что это мертворожденное говно.

6yrop

Ручная склейка решает 90% задач.Возьмем для примера юникс утилиты. Некоторые из них принимают аргумент -0, некоторые нет (например grep). Казалось бы, надо во все утилиты добавить такую возможность. Но всем похуй. Изредка скрипты ломаются из-за пробелов в именах файлов - но это не большая проблема. Подумай, если grep не починили за 40 лет, а динамический sql за 30 лет, то какова вероятность что кто-то когда-то вообще захочет это чинить?
У описанной тобой проблемы решение известно, а для проблемы перемешивания языка запросов и языка общего назначения решение не найдено. Например, вот диссертация, которая посвящена решению, о котором говорил в прошлом треде.
Цитата и нее:
 
Neither the academic research community nor industry has been able to solve
this problem, despite three decades of effort.
  

Dasar

Зачем их ловить? Вылетел NullPointerException - значит ты неправильно используешь API. Исправь свой код и работай дальше.
это означает, что тогда за этим null-ом нет опционального параметра.
Опциональность параметра подразумевает, что обе ситуации валидны: и когда параметр указан, и когда параметр не указан.
И в ситуации, когда параметр не указан мы имеем на входе null, который придется отлавливать в каждой строке кода, чтобы, не дай бог, не нарваться на null-exception.
ps
Банальный пример (высосанный из пальца): "проверить что уровень жидкости в бачке омывателя в норме, и если это не так, то просигнализировать водителю)"
код вроде простой

if (car.бачок_омывателя.датчик_уровня.уровень > рекомендуемые_параметры_эксплуатации.уровень_бачка_омывателя.min)
{
приборная_панель.лампочка_работы_омывателя.зажечь;
}

но если и бачок омывателя, и датчик уровня, и рекомендуемые параметры эксплутации, и лампочка_работы_омывателя являются опциями(валидная ситуация, когда они отсутствуют то приходится руками ловить каждый null и код превращается в следующего монстра

if (car.бачок_омывателя != null && car.бачок_омывателя.датчик_уровня != null
&& рекомендуемые_параметры_эксплуатации != null && рекомендуемые_параметры_эксплуатации.уровень_бачка_омывателя != null
&& car.бачок_омывателя.датчик_уровня.уровень > рекомендуемые_параметры_эксплуатации.уровень_бачка_омывателя.min)
{
if (приборная_панель != null && приборная_панель.лампочка_работы_омывателя != null)
{
приборная_панель.лампочка_работы_омывателя.зажечь;
}
}

luna89

Тут была написана фигня

kokoc88

car.бачок_омывателя.датчик_уровня.уровень > рекомендуемые_параметры_эксплуатации.уровень_бачка_омывателя.min
Ты не написал, как это всё выглядит на C#, когда используется Option.

Dasar

Нет примеров, потому что это мертворожденное говно.
он мертворожденный, потому что они решили лишь одну маленькую проблемку (да еще при этом и предложили свой нестандартный синтаксис а на всё остальное положили болт.
Тот же Hibernate достаточно широко используется, и под Java-ой, и под c#, но в hibernate, конечно, подход более системный.

6yrop

Тот же Hibernate достаточно широко используется, и под Java-ой, и под c#, но в hibernate, конечно, подход более системный.
а какое отношение Hibernate имеет к проблеме перемешивания конструкций на языке запросов и на языке общего назначения? Там всё те же строки или Query Object. Кстати, при желании подход из Controllable Query можно реализовать и для HibernateQL и для JPA.

ava3443

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

Dasar

Ты не написал, как это всё выглядит на C#, когда используется Option.
ужасно, но лучше, чем ручное контролирование null-я
простой вариант (достаточно подключить библиотеку из нескольких функций):

car._n(_=>_.бачок_омывателя)._n(_=>_.датчик_уровня)._nv(_=>_.уровень)
< рекомендуемые_параметры_эксплуатации._n(_ => _.уровень_бачка_омывателя)._nv(_=>_.min)

сложный вариант (необходима здоровая библиотека, а так же желательно добавлять дополнительную валидацию при компиляции):

opt(_ => car.бачок_омывателя.датчик_уровня.уровень)
< opt(_ => рекомендуемые_параметры_эксплуатации.уровень_бачка_омывателя.min)

ps
есть еще более сложные варианты, с переписыванием кода, но они совсем редко встречаются. В этих случаях уже проще брать какой-нибудь язык, работающий поверх c#, типа nemerle.

6yrop

Использовать два понятных ортогональных друг другу языка по-моему легче и эффективнее, чем одного монстра.
Про эффективность использования двух языков абсолютно согласен (SQL хороший DSL но они же должны общаться друг с другом?

6yrop

Использовать два понятных ортогональных друг другу языка по-моему легче и эффективнее, чем одного монстра.
Могу опять ответить цитатой:
In general the approach of loosely coupling different systems
using a string-based interface with a domain-specific
interfacing language is flexible, can leverage the properties
of each layer effectively, does not force the developer to
learn another language or paradigm, and hence is widely
adopted by large-scale system developers.
Unfortunately, this approach is unsafe in that it greatly
complicates two standard software engineering tasks: typechecking
and refactoring. Large systems that are made of
loosely coupled heterogeneous components have little support
for performing typechecking at the boundaries of different
languages. Furthermore, performing even simple refactoring
in such systems is daunting and error prone, because
the required changes cut across different layers, causing subtle
bugs to slip between the cracks.
http://cseweb.ucsd.edu/~lerner/papers/quail.pdf

luna89

Furthermore, performing even simple refactoring
in such systems is daunting and error prone, because
the required changes cut across different layers, causing subtle
bugs to slip between the cracks.

Рефакторинга БД не существует. Если схема данных оказалась неправильная, то это значит что неправильны все другие слои, включая бизнес-логику и UI. Надо переделывать все. Это не рефакторинг.

6yrop

ужасно, но лучше, чем ручное контролирование null-я
Мы в таких случаях переходим на использования свойств HasValue и ValueOrEx, если это внутри одного метода. ValueOrEx можно дергать только внутри if (foo.HasValue) { ... }. Отмечу, что для системного типа Nullable<> такое правило проверяет ReSharper. Можно попросить JetBrains расширить их ContractAnnotation, чтобы кроме условий на аргументы можно было бы указывать условия на соседние свойства. Тогда будет статическая проверка использования ValueOrEx только внутри if (foo.HasValue) { ... }.

Dasar

Рефакторинга БД не существует. Если схема данных оказалась неправильная, то это значит что неправильны все другие слои, включая бизнес-логику и UI.
Так все слои рефакторятся вместе и одновременно, в том числе и автоматизировано. Обычно особых проблем с этим нет, если как раз весь код в одном месте, и отсутствует сильная нарезка в виде, что код БД в одном месте на одном языке, серверный код в другом месте на другом языке, клиентский код в третьем на третьем языке.
ps
Рефакторинга БД не существует.
как это так: "жопа есть, а слова нет?" (c)
 Рефакторинг баз данных
 Рефакторинг баз данных. Эволюционное проектирование

apl13

Как ты считаешь, удобно ли это?
Ну таки это даже не persistent data structure, не?

6yrop

double x = 2.0;
var y = 3.0;
var z = Math.Pow(x, y);
Console.WriteLine(z);
var r = Math.Sqrt(z) + 1;
давай посмотрим, что происходит при Extract Method?
 

double x = 2.0;
var y = 3.0;
var z = Math.Pow(x, y);
MyMethod1(z);
var r = Math.Sqrt(z) + 1;

void MyMethod1(double z)
{
Console.WriteLine(z);
}

Что произойдет, если напишем Maybe<double> x = GetOptionalParameterX;?

Dasar

Что произойдет, если напишем Maybe<double> x = GetOptionalParameterX;?
код скомпилится. MyMethod1 не будет вызываться, когда x is Nothing

6yrop

код скомпилится. MyMethod1 не будет вызываться, когда x is Nothing
а я хочу вот так

void MyMethod1(Option<double> z)
{
    z.Match(_ => Console.WriteLine(_ => log.Write("Всем привет!"
}

или так

void MyMethod1(Option<double> z)
{
if (z.HasValue)
Console.WriteLine(z.ValueOrEx);
else
log.Write("Всем привет!");
}

  

Dasar

а я хочу вот так
если ты тип параметра в MyMethod1 поменяешь с double на Maybe<double>, то так и будет

6yrop

если ты тип параметра в MyMethod1 поменяешь с double на Maybe<double>, то так и будет
Очень херово. Смотри как сейчас в C#-е. Я пишу Maybe<double> x = GetOptionalParameterX. Код не компилируется. И я пишу строчку кода "Привет всем!". Меня компилятор подвел к тому, чтобы я принял решение, как обрабатывать отсутствие значения. У тебя же компилятор принимает за тебя решения. У тебя это не Option Type, это что-то другое, и оно мне даром не нужно.

Dasar

Смотри как сейчас в C#-е. Я пишу Maybe<double> x = GetOptionalParameterX.
а дальше лопатишь тысячи строк кода, которые в той или иной мере зависят от x, чтобы поменять типы на Maybe-типы.

6yrop

это ж разрыв мозга, метод не вызывался, поменяли тип параметра, он стал вызываться (при этом речь идет не о overloading).

Dasar

добро пожаловать в монадное программирование, или в аспектно-ориентированное программирование!

6yrop

а дальше лопатишь тысячи строк кода, которые в той или иной мере зависят от x, чтобы поменять типы на Maybe-типы.
ты до сих пор не пользуешься ReSharper-ом?

6yrop

добро пожаловать в монадное программирование, или в аспектно-ориентированное программирование!
приведи ссылки, показывающие связь аспектно-ориентированного программирования с таким поведение вызовов методов. Пока моё имхо, ты гонишь.

Dasar

это ж разрыв мозга, метод не вызывался, поменяли тип параметра, он стал вызываться (при этом речь идет не о overloading).
давай вернемся к монаде Maybe, и к ее смыслу.
Монада Maybe задает default-ное поведение, что если на входе Nothing, то и на выходе Nothing, кроме случаев, когда есть явное указание, что Nothing или тип Maybe<T> целиком в данной функции необходимо обрабатывать по другому. Такое указание обычно оформляется в виде наличие функции принимающей тип Maybe<T>, если такой перегрузки нет, то автоматически используется default-ное поведение.
(при этом речь идет не о overloading)
монада Maybe<T> как раз и делает автоматический overloading функций следующим образом:
для каждой функции F имеющей хотя бы один аргумент типа T, и не имеющей перегрузки для Maybe<T> создается перегруженный вариант F(Maybe<T> arg) с телом if (arg.HasValue) return F(arg.Value); else return Nothing;
под другим углом зрения - тоже самое утверждение звучит так: для каждой функции F имеющей хотя бы один аргумент типа T, и не имеющей перегрузки для Maybe<T> создается перегруженный вариант F(Maybe<T>.Nothing arg) с телом return Nothing;

6yrop

для каждой функции F имеющей хотя бы один аргумент типа T, и не имеющей перегрузки для Maybe<T> создается перегруженный вариант F(Maybe<T> arg) с телом if (arg.HasValue) return F(arg.Value); else return Nothing;
Рассмотрим функцию с двумя аргументами:
 

Maybe<double> x = GetOptionalParameterX;
var y = 3.0;
var z = Math.Pow(x, y);
MyMethod1(z, 2.3);
var r = Math.Sqrt(z) + 1;

void MyMethod1(double z, double z2)
{
Console.WriteLine(z);
Console.WriteLine(z2);
}

Что будет напечатано? z2 не будет напечатана для z==Nothing? А если метод заинлайнить, то мы получим другой результат (z2 будет напечатана)? Я хочу, чтобы Exract Method был без дополнительных эффектов.
Еще пример. Два фрагмента кода:

Console.WriteLine("z=");
Console.Write(z);


Console.WriteLine("z={0}", z);

Будут выдавать разные результаты?

6yrop

давай вернемся к монаде Maybe, и к ее смыслу.
Монада Maybe задает default-ное поведение, что если на входе Nothing, то и на выходе Nothing, кроме случаев, когда есть явное указание, что Nothing или тип Maybe<T> целиком в данной функции необходимо обрабатывать по другому. Такое указание обычно оформляется в виде наличие функции принимающей тип Maybe<T>, если такой перегрузки нет, то автоматически используется default-ное поведение.
Смысл Option Type-а — безопасно выходить из ветки условного оператора. Когда переменная возникает только внутри одной ветки, а ее обработка делается на уровень выше, где находится сам условный оператор или выше.

M1(M2;

double M1
{
if (/*некоторое условие*/)
{
double a = ...
return a;
}
}
void M2
{
//обработка значения a
}

6yrop

Смысл Option Type-а — безопасно выходить из ветки условного оператора.
Поэтому те, кто не пользуется Option Type
либо не в состоянии сделать такую декомпозицию задачи;
либо делаю это небезопасно (привет NullReferenceException);
либо делаю это громоздко и неуклюже через Null Object шаблон.

6yrop

Maybe<double> x = GetOptionalParameterX;
var y = 3.0;
var z = x.Select(_ => Math.Pow(_, y;
z.Select(_ => Console.WriteLine(_;
var r = z.Select(_ => Math.Sqrt(_) + 1);
Было бы близко к идеальному, если бы в сишарпе сделали вместо "from xxx in" обычный sql-ый "from" (с опциональным алиаслм и с перекрытием одноименной переменной верхнего уровня). Поскольку каждый раз придумывать новое название переменной напрягает.
 

Maybe<double> x = GetOptionalParameterX;
var y = 3.0;
var z = from x select Math.Pow(x, y);
from z select Console.WriteLine(z);
var r = from z select Math.Sqrt(z) + 1;

Тем самым строгость сохраняется (ни какой подковёрной магии при этом читаемость не страдает. Идеальный синтаксис получился бы, если заменить “from x select” просто на двоеточие “x:”
 

Maybe<double> x = GetOptionalParameterX;
var y = 3.0;
var z = x: Math.Pow(x, y);
z: Console.WriteLine(z);
var r = z: Math.Sqrt(z) + 1;

6yrop

И при замене double x=2.0 на Maybe<double> x = GetOptionalParameterX происходило бы следующее. ReSharper подсвечивает красным все строчки, где надо принять решения о Just/Nothing значениях; при этом в контекстном меню предлагается вставить "x:". Тем самым, для фиксации в коде решения, принимаемое программистом, требуется всего лишь выбор пункта из списка (обычно список 1-4 пункта). Вот это, действительно, эффективное сочетание человека и машины.

Dasar

Я хочу, чтобы Exract Method был без дополнительных эффектов.
так если ты делаешь Extract Method после того, как у тебя уже z имеет типа Maybe<double>, то Extract Method тебе сделает:

void MyMethod1(Maybe<double> z, double z2)
{
Console.WriteLine(z);
Console.WriteLine(z2);
}


> Еще пример. Два фрагмента кода:
здесь тебе уже ничего не поможет, потому что предложенный тобой рефакторинг сделает ровно тоже самое:

Console.WriteLine("z=");
z: Console.Write(z);

z:Console.WriteLine("z={0}", z);

и в одном случае, для Nothing будет выводится z=, а в другом не будет
ps
не очень понятно зачем все эти пометки z: нужны, то же самое тебе и IDE скажет без всякой необходимости - это явно фиксировать в коде

6yrop

так если ты делаешь Extract Method после того, как у тебя уже z имеет типа Maybe<double>, то Extract Method тебе сделает:
Нет, Extract Method делался на полгода раньше, когда всё было без Maybe. Проследи цепочку диалога.
 
здесь тебе уже ничего не поможет, потому что предложенный тобой рефакторинг сделает ровно тоже самое:
  

нет конечно, поскольку решение принимает человек, и у него есть возможность cделать, например, так
 

z: {
Console.WriteLine("z=");
Console.Write(z);
}

 

не очень понятно зачем все эти пометки z: нужны, то же самое тебе и IDE скажет без всякой необходимости - это явно фиксировать в коде
 

см. код из предыдущего абзаца. Т.е. в коде есть и отметки и отсутствие отметок, это не является расчетной функцией кода без пометок, поэтому IDE не в состоянии это вывести.

Dasar

Т.е. в коде есть и отметки и отсутствие отметок, это не является расчетной функцией кода без пометок, поэтому IDE не в состоянии это вывести.
Твое предложение полностью совпадает с моим - за исключением одной мелочи.
Там где ты предлагаешь писать z: я предлагаю ничего не писать, а там где ты предлагаешь не писать z:, я предлагаю указывать, что тип аргумента функции Maybe<T>.
В моем предложение банально надо меньше писать и не вводится никаких новых сущностей.
Если же хочется все-таки видеть z:, то их может и IDE нарисовать если сильно хочется.

6yrop

Нет, совпадения нет. У меня в точности Option Type как сейчас в сишарпе. У тебя что-то совсем другое. Ответь нормально на вопрос про метод с двумя аргументами.

Dasar

Что будет напечатано? z2 не будет напечатана для z==Nothing? А если метод заинлайнить, то мы получим другой результат (z2 будет напечатана)?
да.
Выделение двух строк в виде отдельного метода явно показывает, что между этими строками есть взаимосвязь, соответственно, это закономерно - что если не прилагать усилия, то не выведутся сразу обе.
ps
всё тот же рефакторинг при изменении типа x с double на Maybe<double> может вполне, и спросить - MyMethod1 необходимо вызывать для Nothing-а или нет.

6yrop

Выделение двух строк в виде отдельного метода явно показывает, что между этими строками есть взаимосвязь, соответственно, это закономерно - что если не прилагать усилия, то не выведутся сразу обе.
Связь образована по одной причине, а выводить z2 или нет это другая отдельная песня.
всё тот же рефакторинг при изменении типа x с double на Maybe<double> может вполне, и спросить - MyMethod1 необходимо вызывать для Nothing-а или нет.

ReSharper спрашивает в случае если код не компилируется, у тебя всё компилируется.

Dasar

ReSharper спрашивает в случае если код не компилируется, у тебя всё компилируется.
значит надо доработать, чтобы спрашивал.
ps
сейчас же при изменении имени переменной, функции и т.д. - в любом же случае предлагает поменять и другие использования имени, в независимости от того, компилится новый вариант или нет.

6yrop

ReSharper спрашивает в случае если код не компилируется
У меня закрадываются подозрения, что в полном объеме преимуществами статической типизации пользуются очень немногие.

6yrop

значит надо доработать, чтобы спрашивал.
сейчас же при изменении имени переменной, функции и т.д. - в любом же случае предлагает поменять и другие использования имени, в независимости от того, компилится новый вариант или нет.
Ну аналогия очень далекая, потому бесполезная. Ты, как часто делаешь, свел к том, что если твое решение вот еще немного подпилить тут, а еще тут, то, возможно, оно взлетит. :grin:

6yrop

сейчас же при изменении имени переменной, функции и т.д. - в любом же случае предлагает поменять и другие использования имени, в независимости от того, компилится новый вариант или нет.
не предлагает, а просто меняет. Хотя режим предпросмотра есть, но для переименования им не пользуются, нет надобности. Слушай, забей уже на средства Visual Studio, они убоги еще в задумке, используй продуманные решения от JetBrains. Многие твои вопросы отпадут после практики использования ReSharper-а.

6yrop

ReSharper спрашивает в случае если код не компилируется, у тебя всё компилируется.значит надо доработать, чтобы спрашивал.
сейчас ReSharper так не работает, он спрашивает и ответ фиксируется в коде, а ты предлагаешь, чтобы ReSharper спросил про все места, но ответы не фиксируются в коде. Текущее распределение ролей между ReSharper-ом и компилятором дает более надежную схему. ReSharper – ассистент, а контролирует компилятор. Ты предлагаешь, чтобы программист не просто поменял тип переменной, а не забыл нажать кнопку рефакторинга. Это будут тупо забывать делать. В случае настоящего Option Type-а компилятор всё проконтролирует. Ты, наверное, начнешь выкручиваться, скажешь кнопку надо заменить на автовызов при редактировании текста. А если выключили ReSharper, что тогда нельзя нормально работать с Option Type-ом?
Резюме, не стоит ради Option Type-а курочить такие базовые вещи как правила вызова методов и зоны ответственности компилятора и ReSharper-а.
P.S. опять таки порекомендую ощутить ReSharper на кончиках пальцев

luna89

А вот я не понял насчет Option в C#. Есть например большой класс, в котором некоторые поля имеют тип Option. Чему они будут равны, если не инициализировать их в конструкторе?

6yrop

если Option объявлен как struct, то будет new Option т.е. Nothing.

6yrop

также как, например, int? (Nullable<int>)

luna89

если Option объявлен как struct, то будет new Option т.е. Nothing.
Какой-то карго-хаскель. В хаскеле структуру хотя бы невозможно частично проинициализировать.

6yrop

не понял про частичную инициализацию, по-моему всё логично; тип bool инициализируется false, тип Option — Nothing. Вообще, поменьше полагайся на то, чем там инициируются филды.

6yrop

 

А это кто-нибудь использует?
Никому не хочется 6 строк кода менять на 12.
 

Если сравнивать строчки "прикладного" кода (*1 то вот так











 ADO.NETCQ

количество строк
83

количество символов
285202


чистый ADO.NET:
 

var command = new SqlCommand;
command.CommandText = "SELECT Id, Name FROM Person WHERE 1=1";
if (!string.IsNullOrEmpty(name
{
command.CommandText += " AND Name = @Name";
command.Parameters.AddWithValue("@Name", name);
}
var persons = command.GetEnumerable<Person>

Controllable Query:
 

var persons = Func.Apply(new {name = name.Nothing(string.IsNullOrEmpty).Param}, p =>
new QBuilder("SELECT Id, Name FROM Person")._(" WHERE Name = ", p.name).Type<Person>
).GetEnumerable;

Читаемость запроса у варианта с CQ тоже лучше — запрос записан в одну строчку без разделения оператором if.
Сокращение строк произошло по следующей причине. В чистом ADO.NET из-за sql injection используют плейсхолдеры в виде @paramName. В CQ sql injection не возникают по своим внутренним причинам.
-ая идея тут опять работает, был перегружен метод QBuilder._(string, Option<Param<T>>)
(*1) хотя сама по себе метрика отстойная

kokoc88

Если сравнивать строчки "прикладного" кода (*1 то вот так
У тебя баттхёрт даже после игнора?
Читаемость запроса у варианта с CQ тоже лучше — запрос записан в одну строчку без разделения оператором if.
Читаемость запроса примерно такая:
А если параметров будет 2-3, то даже мой любимый набор смайликов не справится с тем, чтобы передать всю гамму эмоций, которую может вызывать у программистов такой код.
Сокращение строк произошло по следующей причине.

Я тебе предлагаю сократить его ещё. Смотри, станет всего одна строка!111
var persons = Func.Apply(new{name=name.Nothing(string.IsNullOrEmpty).Param},p=>new QBuilder("SELECT Id,Name FROM Person")._(" WHERE Name=", p.name).Type<Person>.GetEnumerable;

А теперь ещё до 120 байт!11111111
var persons = F.A(new{n=n.N(str.NE).P},p=>new Q("SELECT Id,Name FROM Person")._(" WHERE Name=", p.n).T<Person>.E;

А смысл в том, что как гавно не заверни...

zorin29

Читаемость запроса у варианта с CQ тоже лучше — запрос записан в одну строчку без разделения оператором if.
Черт побери, я же не хотел писать в этой теме...
C# императивный язык же. Каждая строчка - это одно действие, исполняемое машиной. Понять, какое действие исполняет твоя строчка, можно не иначе как прочитав весь этот тред.
Вот этот кусок вообще в одиночку является аргументом против использования CQ:
name = name.Nothing(string.IsNullOrEmpty).Param

А вот для сравнения код на том же Dapper, который, конечно же, хуже CQ:

var query = "SELECT Id, Name FROM Person WHERE 1=1";
if (!string.IsNullOrEmpty(name
{
query += " AND Name = @Name";
}

var persons = connection.Query<Person>(query, new {Name = name});

P.S. Еще поясни плиз вот это:
В чистом ADO.NET из-за sql injection используют плейсхолдеры в виде @paramName. В CQ sql injection не возникают по своим внутренним причинам.
Я бы сказал, что в чистом ADO.NET по разным причинам рекомендуется передавать параметры запроса как параметры, а не как часть строки запроса. А в CQ как ты побеждаешь SQL injection? Обходишься без параметров?

kokoc88

 
Я бы сказал, что в чистом ADO.NET по разным причинам рекомендуется передавать параметры запроса как параметры, а не как часть строки запроса. А в CQ как ты побеждаешь SQL injection? Обходишься без параметров?

Ты можешь перегружать +, суммируя параметры и объект-запрос. Правда в реальных приложениях это может понизить читаемость.
Правда в этой версии аффтар использует функции, куда видимо зашивает логику. И ещё его дикая идея была в анализе linq.expression на предмет сложения строк в оном.

zorin29

я имею в виду, что параметры все равно используются. Только вместо "where Name = @Name" теперь пишем QBuilder._(" WHERE Name = ", p.name)

6yrop

Спасибо за хорошие вопросы. Отвечу на все три вопроса по порядку.
Понять, какое действие исполняет твоя строчка, можно не иначе как прочитав весь этот тред.
Вот этот кусок вообще в одиночку является аргументом против использования CQ:
1
name = name.Nothing(string.IsNullOrEmpty).Param
А если вот так?

name = !string.IsNullOrEmpty(name) ? name.Param : new Param<string>?
Для понимания требуются самые стандартные знания C#-а.
А если ищем не по Name, а по Id? Условие такое. Требуется считать значение с консоли. Если значение целое число, найти Person с этим Id. Если значение пустая строка или null, вывести всех Persons. Если строка не пустая и не целое число, то вывести сообщение "bla-bla-bla" и повторить чтение с консоли. Как-то вот так:
 
int? id;
while (true)
{
var input = Console.ReadLine;
if (string.IsNullOrEmpty(input
{
id = null;
break;
}
int value;
if (int.TryParse(input, out value
{
id = value;
break;
}
Console.WriteLine("bla-bla-bla");
}

var command = new SqlCommand {CommandText = "SELECT Id, Name FROM Person WHERE 1=1"};
if (id.HasValue)
{
command.CommandText += " AND Id = @Id";
command.Parameters.AddWithValue("@Id", id.Value);
}
var persons = command.GetEnumerable<Person>

6yrop

Приведенный код это стандартный C# и чистый ADO.NET.
Далее мы хотим бонусы статической типизации для sql-строк и sql-параметров. Бонусы таковы:
1. автоматическая error checking,
2. symbol-based navigation,
3. re-factoring sql-запросов и объектов базы данных,
4. statement completion.
Для этого требуется отделить скобками фрагмент с запросом, в начале фрагмента поставить открывающую скобку, в конце поставить закрывающую скобку. Это делается с помощью функции:

public static TResult Apply<T, TResult>(T arg, Func<T, TResult> func)
{
return func(arg);
}

Получаем. Шаг 1.

int? id;
while (true)
{
... //код вырезан, он присутствует в предыдущем сообщении
}

var persons = Func.Apply(new {}, p => {
var command = new SqlCommand {CommandText = "SELECT Id, Name FROM Person WHERE 1=1"};
if (id.HasValue)
{
command.CommandText += " AND Id = @Id";
command.Parameters.AddWithValue("@Id", id.Value);
}
return command.GetEnumerable<Person>
});

Шаг 2. Делаем lambda lifting для переменной id. Получаем:

var persons = Func.Apply(new {id}, p => {
var command = new SqlCommand {CommandText = "SELECT Id, Name FROM Person WHERE 1=1"};
if (p.id.HasValue)
{
command.CommandText += " AND Id = @Id";
command.Parameters.AddWithValue("@Id", p.id.Value);
}
return command.GetEnumerable<Person>
});

Шаг 2. Меняем тип id параметра с "int?" на "Param<int>?":
 
var persons = Func.Apply(new {id = id.Param}, p => {
var command = new SqlCommand {CommandText = "SELECT Id, Name FROM Person WHERE 1=1"};
if (p.id.HasValue)
{
command.CommandText += " AND Id = @Id";
command.Add("@Id", p.id.Value);
}
return command.GetEnumerable<Person>
});

Шаг 3. Заменяем вызов сеттера свойства CommandText на метод SetCommandText<T>(string commandText):

var persons = Func.Apply(new {id = id.Param}, p => {
var command = new SqlCommand;
var commandText = "SELECT Id, Name FROM Person WHERE 1=1";
if (p.id.HasValue)
{
commandText += " AND Id = @Id";
command.Add("@Id", p.id.Value);
}
return command.SetCommandText<Person>(commandText);
}).GetEnumerable;

Теперь в этом коде на sql-строки распространяются бонусы статической типизации.
Перевешивают ли бонусы статической типизации затраты на описанные три шага? На мой взгляд, да, несомненно!

6yrop

Может ли машина сама переписывать код как указано в шагах 1-3? Пока у меня нет ответа на этот вопрос. Сейчас машина только может проверять выполнение требуемых условий. Она выдает понятные сообщения до тех пор, пока все условия не будут выполнены.
Из реального, в IDE можно сделать автоматизацию для lambda lifting аналогично Introduce Parameter. Тем самым затраты будут минимизированы.

kokoc88

Перевешивают ли бонусы статической типизации затраты на описанные три шага? На мой взгляд, да, несомненно!
, ты подтолкнул несчастного полтора часа писать какую-то ахинею. Тебе не стыдно, что всего полтора часа? Надо было сразу просить перевести код хотя бы на 5 динамических параметров и три динамических столбца, желательно ещё с двумя запросами в разные динамически выбираемые таблицы. И обязательно попросить поподробнее рассказать про бонусы статической типизации, в прошлый раз это закончилось . Кстати, немного выше по этой ссылке можно увидеть рекомендацию делать такие запросы копипастой, это же небольшая плата за статическую типизацию.
Впрочем, что результат и так гавно не понимает только один человек в этом разделе...

6yrop

В этом посте отвечу на второй из трех вопросов.
 
А вот для сравнения код на том же Dapper, который, конечно же, хуже CQ:
var query = "SELECT Id, Name FROM Person WHERE 1=1";
if (!string.IsNullOrEmpty(name
{
    query += " AND Name = @Name";
}
var persons = connection.Query<Person>(query, new {Name = name});
Тут нет бонусов статической типизации для sql-строк. Замечательный факт состоит в том, что (будем называть их Query Lifting) применимы и к другим string-based интерфейсам и на разных платформах, таким как ADO.NET, JDBC, HibernateQL, JPA, Enity Framework EntityQL, Dapper и т.д
Вот так Query Lifting выглядит для Dapper:

var persons = Func.Apply(new {name = name.OptionalParam(string.IsNullOrEmpty connection}, p => {
var query = "SELECT Id, Name FROM Person WHERE 1=1";
if (p.name.HasValue)
{
query += " AND Name = @Name";
}
return p.connection.Query<Person>(query, new {Name = p.name});
});

В этом коде на sql-строки распространяются бонусы статической типизации.
P.S. Еще есть вот такой вариант:
 

var persons = Func.Apply(new {name = name.OptionalParam(string.IsNullOrEmpty)}, p => {
var query = "SELECT Id, Name FROM Person WHERE 1=1";
if (p.name.HasValue)
{
query += " AND Name = @Name";
}
return new Query<Person>(query, new {Name = p.name});
}).GetEnumerable(connection);

6yrop

В этом посте отвечу на последний из трех вопросов.
Да, метод с название “_” лучше заменить на “Append”:

var persons = Func.Apply(new {name = name.OptionalParam(string.IsNullOrEmpty)}, p =>
new QBuilder("SELECT Id, Name FROM Person").Append(" WHERE Name = ", p.name)
.Type<Person>.GetEnumerable;

Теперь отвечу на:
P.S. Еще поясни плиз вот это:
В чистом ADO.NET из-за sql injection используют плейсхолдеры в виде @paramName. В CQ sql injection не возникают по своим внутренним причинам.
Я бы сказал, что в чистом ADO.NET по разным причинам рекомендуется передавать параметры запроса как параметры, а не как часть строки запроса. А в CQ как ты побеждаешь SQL injection? Обходишься без параметров?
Сразу отмечу, что в CQ используются SqlParameter-ы, вопрос лишь в том, как их легче создавать.
Использование имен параметров @ParamName дает следующие плюсы:
1. безопасность (sql injection
2. если параметр встречается несколько раз в запросе, то sql-параметр будет один.
Однако получается дополнительный слой мепинга: переменную из C#-а меппим на @ParamName, а потом @ParamName используем в запросе. Хочется без промежуточного слоя:

var name = Console.ReadLine;
var persons = connection.Query<Person>("SELECT Id, Name FROM Person WHERE Name = @name");

При использовании Query Lifting-а это можно сделать с помощью overloading методов QBuilder.Append:

new QBuilder("SELECT Id, Name FROM Person WHERE Name = ").Append(p.name)

public class QBuilder
{
public QBuilder Append(string s)
{
...
}

public QBuilder Append(Param<string> param)
{
var parameter = new SqlParameter {
ParameterName = "p" + command.Parameters.Count,
SqlDbType = SqlDbType.NVarChar,
Size = -1
};
...
}
}

Если делать QBuilder для ADO.NET, то такую overloading сделать не получится. Придется делать разноименные методы Append и AppendParam. Но тогда безопасность (от sql injection) не обеспечивается, поскольку очень легко вместо AppendParam(name) написать Append(name).
Если вдруг кому-то проблема дополнительного слоя мепинга @ParamName кажется не существенной, то история была такая. В начале прошлой недели я показал коллегам. Дополнительная всего одна строчка. Они сказали это конечно хорошо, но в предыдущей версии работать с параметрами было удобнее и читаемость лучше (не надо искать чему соответствует @ParamName). Поэтому QBuilder это реально удобно.
P.S. На последок сделаю замечание, оно не играет существенной роли, но всё же для чистоты. В твоем коде используется хак встроенный в Dapper. Там с помощью регулярного выражения проверяется, если в запросе нет @Name, то SqlParameter не создается. Но регулярка это хак, нужен полноценный разбор sql выражения. Вот такой пример не проходит: “SELECT * FROM Person --@Name”, параметр уходит на сервер, хотя “--@Name ” это комментарий. У Dapper-а для динамических запросов есть свой билдер. Правда с параметрами, встречающимися несколько раз в запросе, там как-то мутно; они внутри их мерджат
Оставить комментарий
Имя или ник:
Комментарий: