Здравствуйте, VladD2, Вы писали:
C>>Вот именно это можно сделать в макросе linq.
VD>В одном слове — нельзя.
Жаль.
C>>Нет, они встроенные. Потому что option — не коллекция.
Тут я лоханулся. Но ума не приложу, почему так решили сделать.
C>>Лямбды должны инлайнится
VD>Это очень не просто, особенно если функция в которую они передаются является библиотечной. Ведь для инлайнинга лябд нужно инлайнить и фукнцию которая их получает. Плюс при инлайнинге возникает очень много вопросов. Ведь язык императивный (хотя бы на половину). Так что многие предположения могут оказаться неверны.
Это до меня уже дошло
VD>Это и есть мифическая чистота. Макрос вроде foreach, when и т.п. тоже бесполезны, если смотреть на макросы в таком ключе. Однако на практике их используют значительно чаще чем аналогичные функции, так как это проще, быстрее и удобнее. В общем, если макрос решает некоторую проблему лучше чем функция, то он имеет право на жизнь. И на мой взгляд тут макрос find ничем не отличается от макроса foreach, к примеру. И то и другое автоматизация паттерна программирования.
Ну, проблема довольно специфична: найти первый элемент в последовательности и выполнить с ним какое-то действие, а если его в последовальности нету, то выполнить другое. Я могу тут ошибаться, но вряд ли она того уровня распостраненности, чтобы её решать в стандартной библиотеке. К тому же дважды.
VD>>>Мы поддерживаем уже кучу макросов и как-то не испытываем от этого неудобств.
VD>>>Меня больше заботит простота чтения кода и эффективность получаемых программ. Именно это я и имею в виду под словом "оверхэд".
Давайте сравним простоту чтения для существующего реального кода (из
macros/assertions.n), и предполагаемой замены:
def existing =
NList.Find (ty.LookupMember ("_N_invariant"), fun (x : IMember) {
x.DeclaringType.Equals (ty) && x is IMethod
});
match (existing) {
| None =>
def methods = ty.GetMethods (BindingFlags.Public %|
BindingFlags.Instance %|
BindingFlags.DeclaredOnly);
foreach (m :> MethodBuilder in methods)
{
def newBody = Util.locate(m.Body.Location,
<[ InvariantExpose (this, $(m.Body)) ]>);
m.Body = newBody;
}
ty.Define ( <[ decl:
public mutable _N_invariant_lock : bool;
]> );
ty.Define ( <[ decl:
public virtual _N_invariant () : void
{
assert ($body, "The class invariant has been violated.")
}
]> );
| Some (m) =>
def m = m :> MethodBuilder;
def newBody = Util.locate(m.Body.Location, <[
$(m.Body);
assert ($body, "The class invariant has been violated.");
]>);
m.Body = newBody;
}
С макросом find:
find (m : IMember when x.DeclaringType.Equals (ty) && x is IMethod in ty.LookupMember("_N_invariant"))
{
def m = m :> MethodBuilder;
def newBody = Util.locate(m.Body.Location, <[
$(m.Body);
assert ($body, "The class invariant has been violated.");
]>);
m.Body = newBody;
}
otherwise
{
def methods = ty.GetMethods (BindingFlags.Public %|
BindingFlags.Instance %|
BindingFlags.DeclaredOnly);
foreach (m :> MethodBuilder in methods)
{
def newBody = Util.locate(m.Body.Location,
<[ InvariantExpose (this, $(m.Body)) ]>);
m.Body = newBody;
}
ty.Define ( <[ decl:
public mutable _N_invariant_lock : bool;
]> );
ty.Define ( <[ decl:
public virtual _N_invariant () : void
{
assert ($body, "The class invariant has been violated.")
}
]> );
}
Тут улучшение читаемости практически незаметно (на мой взгляд). Единственный плюс — более быстрый код. Но для более быстрого кода можна и цикл foreach написать.
В общем, я понимаю ситуацию так. У программиста Nemerle есть два подхода к выполнению некоторой задачи: императивный и функциональный-декларативный. Императивный неудобный (ну и вообще хреновый), а декларативный медленный и плохо поддерживается отладчиком. Макрос find позволяет выполнить задачу, генерируя императивный код, основанный на декларативном синтаксисе.
Если функциональный оверхэд (или разницу в читаемости императивного/декларативного стиля) мы считаем незначительным, тогда этот макрос просто не нужен.
Если find действительно окупается в плане эффективности и читаемости, то возникает вопрос: почему только find? Ведь для остальных операций (те же map, fold, group и т. д.) так же можно написать макросы, которые генерировали бы быстрый код, оставляя синтаксис декларативным. Потому-то я и писал про вариант изменить макрос linq так, чтобы он, где возможно, специализировался для массивов, списков. Тогда у пользователя языка осталось бы три варианта: императивщина, функциональщина и linq, причем linq объединял бы лучшие стороны первых двух вариантов.
А вне общего набора подобных макросов, отдельный find выглядит как-то не очень по-библиотечному.