Здравствуйте, D. Mon, Вы писали:
DM>>>static if (__traits(compiles, x + 1)) DM>>>Где вместо "х + 1" может быть любое выражение. EP>>Это как раз SFINAE-like. DM>Я не вижу сходства. Выше уже сказали, что SFINAE нельзя использовать внутри функций, в отличие от static if.
Сходство в том, что можно узнать поддержку синтаксиса в comiple time, и сделать по результату compile time выбор.
Встроенного в функции static if нет, и скорей всего не будет (см. статью "static if considered"), но при желании внутри функции можно сделать и эту проверку, и compile time dispatch + добавить сахара.
А при наличии концепций, можно не запихивать всё в одну функцию с лапшой static if'ов, а сделать нормальные перегрузки
EP>>А мне интересно, можно ли поймать такое: EP>>
EP>>static if (__traits(compiles, some_function_with_static_assert_inside() ))
EP>>// ..
EP>>
DM>Да, может. Хоть static assert, хоть динамическая проверка.
FR>>>И не дай бог мелкую ошибку допустить и получим километровое сообщение, EP>>Против километровых ошибок помогут концепции. I>Концепции уже для того, нужны что бы просто из функции выйти.
И желательно, что бы эта хрень _не_ бросала исключения, иначе дебаг превращается в бедствие.
FR>>>>И не дай бог мелкую ошибку допустить и получим километровое сообщение, EP>>>Против километровых ошибок помогут концепции. I>>Концепции уже для того, нужны что бы просто из функции выйти.
EP>Подробнее.
Да всё ты понимаешь, не надо шлангом прикидываться.
Здравствуйте, Ikemefula, Вы писали:
I>И желательно, что бы эта хрень _не_ бросала исключения, иначе дебаг превращается в бедствие.
В смысле вообще не бросала? Так зачем ограничивать?
Или ты про реализацию CONTINUE/etc? Так там нет исключений
FR>>>>>И не дай бог мелкую ошибку допустить и получим километровое сообщение, EP>>>>Против километровых ошибок помогут концепции. I>>>Концепции уже для того, нужны что бы просто из функции выйти. EP>>Подробнее. I>Да всё ты понимаешь
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>И желательно, что бы эта хрень _не_ бросала исключения, иначе дебаг превращается в бедствие.
EP>В смысле вообще не бросала? Так зачем ограничивать? EP>Или ты про реализацию CONTINUE/etc? Так там нет исключений
Покажи реализацию без исключений, особенно return интересует
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>findSplit вообще возвращает три range, а нужны [first, find(first, last)) и [find(first, last), last), или например тоже самое с partition.
Если позарез нужны именно такие, то берем chain() и соединяем второй с третьим в один. Делов-то.
EP>>>2. find работает и для single pass range, а твой .save есть только начиная с forward — так? DM>>Так. Возврат левой части (от начала до найденного элемента) для не forward рэнджа смысла не имеет, элементы уже однажды прочитаны. D такую попытку здесь пресекает. А плюсы? Спокойно вернут итератор на протухшие данные?
EP>Покажи реализацию аналога std::find.
В каком смысле аналога? Если он будет с range'ами, он по-любому не будет полным аналогом. Если с разбиением на два рэнджа, то например так:
auto myfind(Range, T)(Range r, T t)
{
size_t n = 0;
auto left = r.save;
while(!r.empty && r.front != t) {
r.popFront();
n++;
}
return tuple(left.takeExactly(n), r);
}
А теперь ты покажи аналог, не требующий forward итератор.
EP>Тут согласен — в большинстве случаев будет не заметно. Я лишь показываю насколько абстракция неудобная, даже для простых случаев
Неудобная — это таскание и передача пар итераторов повсюду с постоянным риском их перепутать и разведением писанины. А дивные рэнджи и алгоритмы на практике крайне удобны в использовании.
EP>>>5. Это n, по сути является разжалованным итератором. Точно также, как я показывал выше, в Phobos'е, для алгоритмов по RandomAccess используются обычные целые, которые не знают откуда они пришли, и по сути те же итераторы, только менее удобные и безопасные. DM>>А какая есть безопасная альтернатива для Random access?
EP>В смысле? Вот ты реализуешь некий алгоритм который использует несколько random access range'ей — дёргаешь индексы туда-сюда, при использовании D Range нужно ещё не перепутать из какого range какой индекс, в том время как из самого итератора можно и читать/писать.
A такой итератор дает random access? Если нет, то причем тут индексы, а если да, то как он это делает без "опасных" индексов?
EP>>>8. У тебя возвращается либо один тип range (исходный), либо другой (то что вернёт takeExactly) — какой в итоге тип у твоего firstPart? DM>>Хороший вопрос. Почему-то компилятор в нем разобрался без моей помощи. EP>Как именно? Какой typeof у результата?
Оказалось, я его проверял на массивах, а для них в обоих ветках массивы получаются. С более другим контейнером вылезла ожидаемая ошибка.
Здравствуйте, D. Mon, Вы писали:
EP>>findSplit вообще возвращает три range, а нужны [first, find(first, last)) и [find(first, last), last), или например тоже самое с partition. DM>Если позарез нужны именно такие, то берем chain() и соединяем второй с третьим в один. Делов-то.
chain в общем случае возвращает range нового типа, с добавленным оверхедом, и увеличенным размером?
Не, когда range'и из одного контейнера, да ещё и смежные — такое не интересно
EP>>Покажи реализацию аналога std::find. DM>В каком смысле аналога? Если он будет с range'ами, он по-любому не будет полным аналогом. Если с разбиением на два рэнджа, то например так: DM>
DM>auto myfind(Range, T)(Range r, T t)
DM>{
DM> size_t n = 0;
DM> auto left = r.save;
DM> while(!r.empty && r.front != t) {
DM> r.popFront();
DM> n++;
DM> }
DM> return tuple(left.takeExactly(n), r);
DM>}
DM>
DM>А теперь ты покажи аналог, не требующий forward итератор.
Ну так вот же он:
template<typename I, typename T>
I find(I first, I last, const T& x)
{
while(first != last && *first != x)
++first;
return first;
}
Если input range — то возвращается только вторая часть, но во всех остальных — обе.
У тебя же .save, который как я понял — даст compile error на single pass.
Далее, что будешь делать, например, чтобы получить: [std::prev(find(first, last, x)), last) — опять chain overhead?
Простота реализации, мощность и эффективность — налицо
EP>>Тут согласен — в большинстве случаев будет не заметно. Я лишь показываю насколько абстракция неудобная, даже для простых случаев DM>Неудобная — это таскание и передача пар итераторов повсюду с постоянным риском их перепутать и разведением писанины. А дивные рэнджи и алгоритмы на практике крайне удобны в использовании.
Когда есть действительно пара итераторов — их не нужно таскать по отдельности. И есть соответствующие обвёртки над алгоритмами(Boost.Range, Adobe ASL):
Плюс это будет в будущих стандартах (хотя легко доступно в виде библиотек).
EP>>В смысле? Вот ты реализуешь некий алгоритм который использует несколько random access range'ей — дёргаешь индексы туда-сюда, при использовании D Range нужно ещё не перепутать из какого range какой индекс, в том время как из самого итератора можно и читать/писать. DM>A такой итератор дает random access? Если нет, то причем тут индексы, а если да, то как он это делает без "опасных" индексов?
В смысле?
Там где в STL будут два итератора:
*x++ = *y++;
В D-like range будет два range, и два индекса:
w[x++] = z[y++];
(move может быть намного сложнее ++).
EP>>Как именно? Какой typeof у результата? DM>Оказалось, я его проверял на массивах, а для них в обоих ветках массивы получаются. С более другим контейнером вылезла ожидаемая ошибка.
Ок, а то я, как один из вариантов, предположил что там автоматический type-erasure а-ля boost::any_range, со всем runtime overhead'ом.
Здравствуйте, Кодт, Вы писали:
I>>Покажи реализацию без исключений, особенно return интересует К>Фигня-война. Если return void, то делается довольно просто. К>С возвратом чего-то типизированного будет сложнее и грязнее. Но тоже ничего невозможного нет.
Это первый вариант. А второй в возвращении не просто has/no optional, а с enum {returned, breaked, continued, normal_exit}. Тогда break превращается в return breaked, и т.п.
Здравствуйте, include2h, Вы писали:
IO>>Зато есть вменяемая модель многопоточности. И модель памяти, которая с этой многопоточностью работает. IO>>И всё это безопасно — на уровне "управляемых платформ", но без их оверхеда.
I>Можете рассказать подробнее?
Параллельность реализована тасками, которые не имеют разделяемого состояния. Т.е. одновременный доступ к одной структуре данных невозможен.
Структуры данных можно передавать в/из тасков как сообщения (без копирования). Код, передающий сообщение при этом теряет доступ к объекту, который он передаёт
в другую таску. Это обеспечивается статической проверкой. Кстати это не требует сборки мусора — время жизни вычисляется статически.
На самом деле там модель более сложная, чем в С. Есть несколько типов указателей. Основные — это owned, которые могут, грубо говоря, существовать
только в одном месте в каждый момент, и managed, которые отслеживаются сборщиком мусора. Передавать между тасками можно только первые.
При этом всё сделано умно — т.е. не тип указателя не обязательно всегда и везде указывать точно. В одну и ту же функцию можно передавать указатели разных типов.
Откуда же его [независимый суд] взять, если в нем такие же как мы? (c) VladD2
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Но — из таких range всегда можно достать итераторы, и работать с ними, а вот из D-style range — нет. EP>То есть гибридное решение — итераторы + сахар в виде range вокруг них, вполне жизнеспособное.
Ты на логику свою посмотри. Ты выставляешь против языка некий паттерн и его библиотечную поддержку в стандартной библиотеке языка.
Язык то тут причем? Нравятся тебе итераторы? Напиши либу на их основе и радуйся жизни. В С++ они тоже не сразу появились.
Что до метапограммирования, то очевидно, что на D мета-код выглядит проще, работает существенно быстрее и выдает внятную диагностику. Единственная его проблема — есть решения куда более гибкие и мощные. А вы тут о чем-то спорите.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Ага. Только выглядит как деромокод и понять могут толко посвященные. Код на D я понял на раз.
Да ладно, в чём принципиальная разница
iota(0, 10).map!(x => x *x).filter!(x => x % 2)
// versus
irange(0, 10) | transformed(_1 * _1) | filtered(_1 % 2)
// if use same names:
iota(0, 10) | map(_1 * _1) | filter(_1 % 2)
?
C++ версию запостили одновременно двое, причём если не считать whitespace, код диапазона идентичен — и это уж точно никак не тянет на какие-то сакральные знания доступные "посвящённым"
Здравствуйте, VladD2, Вы писали:
EP>>Но — из таких range всегда можно достать итераторы, и работать с ними, а вот из D-style range — нет. EP>>То есть гибридное решение — итераторы + сахар в виде range вокруг них, вполне жизнеспособное. VD>Ты на логику свою посмотри. Ты выставляешь против языка некий паттерн и его библиотечную поддержку в стандартной библиотеке языка.
Ещё раз:
EP>Как известно, у C++ основным приоритетом является производительность.
EP>А в D спокойно могут пожертвовать производительностью ради других целей (об этом говорил A.A.). Взять например те же ranges — они проигрывают итераторам по производительности by-design
VD>Язык то тут причем? Нравятся тебе итераторы? Напиши либу на их основе и радуйся жизни. В С++ они тоже не сразу появились.
Я показываю то, что авторы языка D готовы жертвовать производительностью. Сегодня ranges. А кто знает чем именно они пожертвуют завтра?
У C++ zero-overhead (точнее don't pay for what you don't use) это практически главная цель — заменить его сможет только язык с такими же целями.
VD>Что до метапограммирования, то очевидно, что на D мета-код выглядит проще, работает существенно быстрее и выдает внятную диагностику.
Вот тут я как раз и не спорю — на данном этапе у него больше возможностей для метапограммирования.
Что, кстати, немного странно: казалась бы, в языке с лучшей поддержкой compile-time вычислений, compile-time dispatch, метапограммирования — должна быть супер эффективная, гибкая, стандартная библиотека — так ведь нет, STL явно выигрывает
Здравствуйте, Evgeny.Panasyuk, Вы писали: EP>Да ладно, в чём принципиальная разница EP>
EP>iota(0, 10).map!(x => x *x).filter!(x => x % 2)
EP>// versus
EP>irange(0, 10) | transformed(_1 * _1) | filtered(_1 % 2)
EP>// if use same names:
EP>iota(0, 10) | map(_1 * _1) | filter(_1 % 2)
EP>
?
Так уже лучше, хотя почему для организации цепочки вызовов используется оператор бинарного или все же не ясно.
Но тут встает другой вопрос. На D — это "гражданский" код, а на С++ магия метапограммирования которая в плюсах отнюдь не бесплатна. Проект забитый этим делом будет тормозить и выдавать фееричные сообщения в случае банальных ошибок.
В Ди и пытались сделать как в С++, но без приседаний.
ЗЫ
Ни разу не сторонник Ди, но объективности ради.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Так уже лучше, хотя почему для организации цепочки вызовов используется оператор бинарного или все же не ясно.
Вспомни консоль:
cat x | grep y | sort
VD>Но тут встает другой вопрос. На D — это "гражданский" код, а на С++ магия метапограммирования которая в плюсах отнюдь не бесплатна.
Да никакой тут магии нет — в "|" столько же магии, сколько и в "<<", а это уже
std::cout << "Hello, World!" << std::endl;
VD>Проект забитый этим делом будет тормозить и выдавать фееричные сообщения в случае банальных ошибок.
Каким именно делом? Где тормозить?
VD>Ни разу не сторонник Ди, но объективности ради.
D — хорошая попытка, мне даже нравится.
Но, во-первых — он пытается усидеть сразу на многих стульях. А во-вторых, такое впечатление что фичи добавляются направо и налево, и это наверное даже правильно пока мало пользователей, но реальная проверка на прочность начнётся когда появится большая пользовательская база и хотя бы несколько солидных компиляторов.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Ещё раз: EP>
EP>>Как известно, у C++ основным приоритетом является производительность.
EP>>А в D спокойно могут пожертвовать производительностью ради других целей (об этом говорил A.A.). Взять например те же ranges — они проигрывают итераторам по производительности by-design
Вот и читай свои же слова до просветления. Это не я путаю язык и библиотеку?
EP>Я показываю то, что авторы языка D готовы жертвовать производительностью. Сегодня ranges. А кто знает чем именно они пожертвуют завтра?
Ты показываешь то чего нет. Люди сделали библиотеку, а ты на этом основании делаешь выводы о языке.
Вот возьмем к примеру С. Чем он быстрее нежели Ди? Тем что с ним не идет такая же библиотека?
EP>У C++ zero-overhead (точнее don't pay for what you don't use) это практически главная цель — заменить его сможет только язык с такими же целями.
Про зеро ты сам только что придумал. Оверхед в С++ вполне себе реальный. Страуструп говорил, что мы не платим за то что не используем. Ну, так такой же принцип и тут.
VD>>Что до метапограммирования, то очевидно, что на D мета-код выглядит проще, работает существенно быстрее и выдает внятную диагностику.
EP>Вот тут я как раз и не спорю — на данном этапе у него больше возможностей для метапограммирования.
Данный этап длится около 30 лет.
Я плюсы бросил где-то в начале нулевых. По мне так лучше заплатить небольшим оверхэдом, чем всю жизнь жевать этот кактус.
EP>Что, кстати, немного странно: казалась бы, в языке с лучшей поддержкой compile-time вычислений, compile-time dispatch, метапограммирования — должна быть супер эффективная, гибкая, стандартная библиотека — так ведь нет, STL явно выигрывает
Чтобы что-то утверждать про эффективность нужно написать что-то значимое на том и на этом. А ты уперся в какие-то там итераторы и на их основе делаешь далеко идущие выводы.
Я как человек позанимавшийся метпаропграммированием скажу тебе, что твои рассуждения не о чем. При массовом использовании МП абстракции нижнего уровня вроде итеаторов и т.п. вообще значения не имеют. Мы тупо используем массивы и хардкорный код в стиле С на куда более высокоуровневом языке нежели Ди и С++ вместе взятые. И все потому что код этот мы руками не пишем. Мы его генерируем.
Я в Ди не силен, но думаю, что к нему это применимо точно так же.
Что до библиотек, то обычно 90% кода не критичны к производительности. А там где критично можно и на более низкоуровневый код перейти или создать библиотеки обеспечивающие абстракции с нужными характеристиками. Сам Ди вроде этому не мешает.
Так что по факту продвижению Ди больше мешают привычки и косность мышления нежели какие-то технические сложности.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Вспомни консоль: EP>cat x | grep y | sort
Меня от линуксовй консоли всегда мутило. Как-то объектная точка или функциональный оператор |> понятнее.
EP>Да никакой тут магии нет — в "|" столько же магии, сколько и в "<<", а это уже
А _1 тоже не магия?
EP>Каким именно делом? Где тормозить?
Бустом. Или что ты там использовал для получения недолямбд с параметрами _1, _2 и т.п.?
EP>D — хорошая попытка, мне даже нравится. EP>Но, во-первых — он пытается усидеть сразу на многих стульях. А во-вторых, такое впечатление что фичи добавляются направо и налево, и это наверное даже правильно пока мало пользователей, но реальная проверка на прочность начнётся когда появится большая пользовательская база и хотя бы несколько солидных компиляторов.
Ну, то что авторов несет периодически — это да. Но как замена для плюсов в задачах где нужно битовижимательство вполне потянет.
А критикуете вы его совсем не за то. Я бы вот скорее обратил внимание на то что МП в Ди тоже хиленький. Правильный МП должен позволять писать метапрограммы на том же языке что и обычные. В Ди с этим по лучше чем в С++ (где МП надо делать на побочных эффектах от материализации шаблонов), но все же не фирст-класс. За фиг нужен статик иф если есть обычный?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
EP>>Ещё раз: EP>>
EP>>>Как известно, у C++ основным приоритетом является производительность.
EP>>>А в D спокойно могут пожертвовать производительностью ради других целей (об этом говорил A.A.). Взять например те же ranges — они проигрывают итераторам по производительности by-design
VD>Вот и читай свои же слова до просветления. Это не я путаю язык и библиотеку?
Это стандартная библиотека языка, которая хоть немного но отражает общую идеологию языка. Эта библиотека которая будет использоваться по-дефолту, и соответственно по-дефолту тормозить.
Я выше давал ссылку на интервью, где один из авторов как раз и сказал, что они не загоняют себя в угол производительности и могут принимать решения, отдавая предпочтения другим качествам.
VD>>>Что до метапограммирования, то очевидно, что на D мета-код выглядит проще, работает существенно быстрее и выдает внятную диагностику. EP>>Вот тут я как раз и не спорю — на данном этапе у него больше возможностей для метапограммирования. VD>Данный этап длится около 30 лет.
По-твоему D 30 лет?
VD>Я плюсы бросил где-то в начале нулевых. По мне так лучше заплатить небольшим оверхэдом, чем всю жизнь жевать этот кактус.
Что конкретно не нравится?
VD>Чтобы что-то утверждать про эффективность нужно написать что-то значимое на том и на этом. А ты уперся в какие-то там итераторы и на их основе делаешь далеко идущие выводы.
На D можно писать столь же эффективно как и в C++ — это следует как минимум из сравнения ключевых фич, тут-то вообще не вижу смысла спорить
Алгоритмы и интервалы из стандартной библиотеки, это то, что используется в 99% программ. И если там тормоза by-design, то это значит что в 99% программах, будет платится за то, что может быть бесплатным
VD>Я как человек позанимавшийся метпаропграммированием скажу тебе, что твои рассуждения не о чем. При массовом использовании МП абстракции нижнего уровня вроде итеаторов и т.п. вообще значения не имеют. Мы тупо используем массивы и хардкорный код в стиле С на куда более высокоуровневом языке нежели Ди и С++ вместе взятые. И все потому что код этот мы руками не пишем. Мы его генерируем.
Surprise, на C++ нечто подобное доступно уже лет 16 — смотри MTL, Blitz++, Eigen
VD>Так что по факту продвижению Ди больше мешают привычки и косность мышления нежели какие-то технические сложности.