Comments 75
assert(Map!((double x) {return x+1.0;},int,double)(ar) == [2.0,3.0,4.0,5.0], "wrong!");
Выглядит страшновато. На пару строк можно перенести?
Это пожелание к статье или вопрос про язык?
Если второе, то да, насколько я знаю, во всех языках в си подобным синтаксисом можно дробить выражения на несколько строк, а если первое, то не уверен, что будет сильно лучше.
Если второе, то да, насколько я знаю, во всех языках в си подобным синтаксисом можно дробить выражения на несколько строк, а если первое, то не уверен, что будет сильно лучше.
В этой строке много текста, но немного смысла. Зачем дробить тривиальное выражение на строки?
… эм, судя по минусам, я должен пояснить. Данное выражение содержит многабукв, но с позиции чистой вычислительной логики являет собой простую идиому, которая на языке Haskell, например, будет выглядеть примерно так:
Теперь видно, что ничего страшного тут нет. Если вы активно пользуетесь функциями высшего порядка в своём любимом языке программирования (будь то Haskell, Python или D), то беглое чтение таких идиом — даже на других, более многословных языках — не составляет для вас труда, и разбивка элементарных выражений на строки выглядит скорее вредной, чем полезной.
Уметь бегло читать такие выражения очень важно, поскольку очень важно уметь игнорировать синтаксис и сосредотачиваться на семантике, чтобы сразу же видеть в данном примере не map и \-функцию, а некорректное сравнение чисел с плавающей точкой на равенство.
assert (map (+ 1.0) ar == [2.0,3.0,4.0,5.0]) "good!"
Теперь видно, что ничего страшного тут нет. Если вы активно пользуетесь функциями высшего порядка в своём любимом языке программирования (будь то Haskell, Python или D), то беглое чтение таких идиом — даже на других, более многословных языках — не составляет для вас труда, и разбивка элементарных выражений на строки выглядит скорее вредной, чем полезной.
Уметь бегло читать такие выражения очень важно, поскольку очень важно уметь игнорировать синтаксис и сосредотачиваться на семантике, чтобы сразу же видеть в данном примере не map и \-функцию, а некорректное сравнение чисел с плавающей точкой на равенство.
Я думаю, я понимаю, почему людям не понравилось. Вы утверждаете мало смысла, а на самом деле:
-мы создаем лямбда-функцию
-тут же ее вызываем
-проверяем результирующее значение
-бросаем эксцэпшн с сообщением если не совпало.
Смысла как раз совсем не мало.
И не не надо сравнивать с хаскелом — он вообще чрезвычайно лаконичный язык, с ним что хочешь многословно выглядит :)
-мы создаем лямбда-функцию
-тут же ее вызываем
-проверяем результирующее значение
-бросаем эксцэпшн с сообщением если не совпало.
Смысла как раз совсем не мало.
И не не надо сравнивать с хаскелом — он вообще чрезвычайно лаконичный язык, с ним что хочешь многословно выглядит :)
Рискуя снова нарваться, скажу, что, согласно моему мнению, если язык абстрагирует некоторую деятельность, то нужно руководствоваться этими абстракциями (до тех пор, пока абстракция не сломается и не возникнет необходимость залезать вглубь). Приведу пример — реальную строчку из исходного кода ядра Linux:
— мы разыменовываем указатель на массив
— разыменовываем указатель на устройство и идентификатор устройства
— используем идентификатор как индекс массива
— разыменовываем ещё два указателя
— производим побитовый сдвиг
— хитро записываем результат в переменную.
Кошмар! Полэкрана ассемблерных инструкций в результате компиляции одной строчки кода!
И, в принципе, эту строчку можно отрефакторить. Например, вынести cmd->device и hostdata->busy в отдельные переменные. Но зачем? Только первокурснику будет легче читать результирующий код. Опытный системный программист читает эту строчку на одном дыхании и сразу понимает зафиксированные в ней идиомы, а всякие лишние определения переменных, наоборот, замедляют чтение, поскольку приходится расширять удерживаемый в кратковременной памяти контекст.
Некогда об этом же писал Стив Йегг, но о чём он только не писал :-)
hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
— мы разыменовываем указатель на массив
— разыменовываем указатель на устройство и идентификатор устройства
— используем идентификатор как индекс массива
— разыменовываем ещё два указателя
— производим побитовый сдвиг
— хитро записываем результат в переменную.
Кошмар! Полэкрана ассемблерных инструкций в результате компиляции одной строчки кода!
И, в принципе, эту строчку можно отрефакторить. Например, вынести cmd->device и hostdata->busy в отдельные переменные. Но зачем? Только первокурснику будет легче читать результирующий код. Опытный системный программист читает эту строчку на одном дыхании и сразу понимает зафиксированные в ней идиомы, а всякие лишние определения переменных, наоборот, замедляют чтение, поскольку приходится расширять удерживаемый в кратковременной памяти контекст.
Некогда об этом же писал Стив Йегг, но о чём он только не писал :-)
Не холивара ради, но такого рода кода из сабжевой строки получится немало. Ну и вообще, число смысла на единицу кода каждым воспринимается по-разному, я на ассемблере программировал, после этого для меня каждая строка кода на C/C++/D/etc наполнена смыслом по самое нехочу.
Строки, написанные мной кажутся мне в самый раз, и я, как автор статьи, имею право решающего голоса, и хоть я и соглашусь с вашими доводами, но те строки удовлетворяют моим эстетическим запросам, потому я так и оставил, но в самом деле. не могу же я всем угодить!
В дополнении замечу, что по-моему вы как-то своими «только первокурснику» и «опытный системный программист» принимаете несколько оскорбительный тон. Получается, все, у кого несовместимые с вашими взгляды — неопытные первокурсники. И именно поэтому вы «рискуете снова нарваться», а не из за отличного от других мнения.
Строки, написанные мной кажутся мне в самый раз, и я, как автор статьи, имею право решающего голоса, и хоть я и соглашусь с вашими доводами, но те строки удовлетворяют моим эстетическим запросам, потому я так и оставил, но в самом деле. не могу же я всем угодить!
В дополнении замечу, что по-моему вы как-то своими «только первокурснику» и «опытный системный программист» принимаете несколько оскорбительный тон. Получается, все, у кого несовместимые с вашими взгляды — неопытные первокурсники. И именно поэтому вы «рискуете снова нарваться», а не из за отличного от других мнения.
Конечно, «в дополнение», больше суток не спал, внимательность падает.
Строки, написанные вами, и мне кажутся в самый раз :-)
Ну да, признаю, оценочные суждения тут не совсем уместны, хотя я не ставил своей целью никого оскорбить. Написанное — это исключительно выводы из моего опыта общения с коллегами, я не настаиваю на обобщении моего мнения.
Вообще, на мой взгляд, есть ещё один важный вопрос. Если кому-то в команде, работающей над проектом, сложно читать код, то код нужно рефакторить вне зависимости от регалий всех остальных программистов. А при поддержке всей команды код можно осторожно усложнять. Важны не формальности, а люди и дифференцированный подход. В т. ч. по этой причине я не люблю замечания по форматированию кода в комментариях к статьям о программировании — объективной истины тут не достигнуть, но легко скатиться во вкусовщину и навязывание своего мнения.
Ну да, признаю, оценочные суждения тут не совсем уместны, хотя я не ставил своей целью никого оскорбить. Написанное — это исключительно выводы из моего опыта общения с коллегами, я не настаиваю на обобщении моего мнения.
Вообще, на мой взгляд, есть ещё один важный вопрос. Если кому-то в команде, работающей над проектом, сложно читать код, то код нужно рефакторить вне зависимости от регалий всех остальных программистов. А при поддержке всей команды код можно осторожно усложнять. Важны не формальности, а люди и дифференцированный подход. В т. ч. по этой причине я не люблю замечания по форматированию кода в комментариях к статьям о программировании — объективной истины тут не достигнуть, но легко скатиться во вкусовщину и навязывание своего мнения.
И, в добавок, я не понял, в чем вы несогласны со списком действий, приведенных мной выше. Я же полностью учел уровень абстракции языка, я же не заявлял пунктов наврод «создание функционального обьекта-указателя» или «спецификация шаблонной функции конкретными типами».
Упс, забыл еще: вызываем функцию с аргументом в виде этой лямбда-функции и еще одним.
С использование стандартной библиотеки и «настоящего» map будет выглядеть несколько лаконичнее:
Но и код библиотечного map несколько менее тривиален, чем требуется для этой статьи. Зато, как видите, можно указывать функцию не только лямбдой/делегатом, но и обычной строкой. Дополнительная плюшка — map высчитывается лениво.
P.S. equal, а не == т.к. идёт сравнение двух range, строго говоря, являющихся разными типами.
assert( equal( map!("a + 1.0")(ar) , [ 2.0, 3.0, 4.0, 5.0 ] ), "wrong!");
Но и код библиотечного map несколько менее тривиален, чем требуется для этой статьи. Зато, как видите, можно указывать функцию не только лямбдой/делегатом, но и обычной строкой. Дополнительная плюшка — map высчитывается лениво.
P.S. equal, а не == т.к. идёт сравнение двух range, строго говоря, являющихся разными типами.
Со вчерашнего дня, в DMD Git был добавлен синтаксис построения лямбда-функций. Теперь можно писать так:
assert(Map!(x=>x+1.0, int, double)(ar) == [2.0,3.0,4.0,5.0], "wrong!");
Если бы название языка начиналось с двоеточия, то он бы пользовался гораздо большей популярностью…
> для этого вызывается наша лямбда-функция [...]. Вызывается каждый раз.
Не правда, всё отлично инлайнится.
Не правда, всё отлично инлайнится.
Это возможно заилайнить. Но если там будет не лямбда в явном виде, а какой-нибудь указатель на функцию, то уже нельзя. Ну или сложно — вдруг кто-то этот указатель переприсвоил другой функции в рантайме? А в D полностью статическое вычисление обеспечено языком.
Еще добавлю: есть разница между «статическое вычисление обеспечено стандартом языка» и «понадеемся, что компилятор сообразит, что выражение можно вычислить статически».
А в стандарте написано что инлайнинг обязан произойти? И кстати, у D, насколько я знаю, нет стандарта…
А где я говорил про инлайнинг? Я говорил про гарантию статическово вычисления выражений. Инлайнинг просто после этого легко осуществить.
В свое время удивила реализация однобайтовых строк и отсутствие нативной поддержки юникода. Это до сих пор так?
UTF8 рулит. UTF16 всё равно содержит суррогатные пары, так что не даёт простоты. А UTF32 слишком неэкономный. Это к слову об однобайтовых строках. А вот отсутствие поддержки юникода — это плохо.
Давненько вы смотрели, однако (:
Когда я в ВУЗе 8 лет назад рассказывал про D, в языке уже была поддержка Unicode. Сейчас и подавно есть: модули std.utf и std.uni специально для работы с Unicode, а так, в общем и целом, все стандартные строковые функции работают с UTF-8, UTF-16 и UTF-32, и исходники тоже могут быть в этих кодировках, причём и в big-, и в little-endian.
Когда я в ВУЗе 8 лет назад рассказывал про D, в языке уже была поддержка Unicode. Сейчас и подавно есть: модули std.utf и std.uni специально для работы с Unicode, а так, в общем и целом, все стандартные строковые функции работают с UTF-8, UTF-16 и UTF-32, и исходники тоже могут быть в этих кодировках, причём и в big-, и в little-endian.
Да, вы определенно обладаете устаревшими данными. Со строками в D как раз все замечательно, мне даже больше чем в C++ нравится, учитывая, что строки не являются чем-то особенным, а просто immutable(char)[]. То есть они просто массивы неизменяемых символов, никаких новых сущностей не вводится. Это хорошо не только эстетически, а, например, тем, что моя реализация Map на них тоже будет работать.
Как там насчет многопоточности? Есть какой-то аналог green threads (легких нитей)?
Читайте официальный сайт, особенно раздел про стандартную библиотеку. Лёгких нитей нет, но обычные треды есть, через модуль core.thread. Я в во всей этой потоковщине не силён, но знаю точно, что по умолчанию все глобальные переменные помещаются в TLS (Thread Local Storage), а чтобы расшарить их между тредами, нужно писать shared (внезапно :)
Насчет потоков я еще глубоко не разбирался, но в язык встроена некоторая поддержка. Как выше написали: shared память, далее, syncronized вычисления, да и чистый функциональный стиль — большое подспорье.
Да, еще есть immutable память — такую вообще очень удобно между потоками перекидывать, так как она гарантированно неизменяема.
Там всё весьма хорошо с многопоточностью. Green threads нет и, насколько мне известно, не планируется. «D-way» многопоточности на данный момент — message passing. Конечно же поддерживаются и треды в стиле pthread, но в чистом виде к использованию не рекомендуются, т.к. слишком error-prone.
www.informit.com/articles/article.aspx?p=1609144 — опубликованная в открытый доступ глава из книги Александерсу «The D Programming Language», посвящённая concurrency вообще.
www.d-programming-language.org/phobos/std_concurrency.html — стандартная message-passing библиотека
www.d-programming-language.org/phobos/std_parallelism.html — symmetric multiprocessing (SMP)
Есть нативная поддержка в самом языке для синхронизации доступа к данным, основанная на системе типов. Тут парой линков не отделаться, гуглить сайт D по synchronized, shared, immutable
Если возникнут какие-то конкретные практические вопросы — с удовольствием отвечу.
www.informit.com/articles/article.aspx?p=1609144 — опубликованная в открытый доступ глава из книги Александерсу «The D Programming Language», посвящённая concurrency вообще.
www.d-programming-language.org/phobos/std_concurrency.html — стандартная message-passing библиотека
www.d-programming-language.org/phobos/std_parallelism.html — symmetric multiprocessing (SMP)
Есть нативная поддержка в самом языке для синхронизации доступа к данным, основанная на системе типов. Тут парой линков не отделаться, гуглить сайт D по synchronized, shared, immutable
Если возникнут какие-то конкретные практические вопросы — с удовольствием отвечу.
Есть Fibers
Какая у D изюминка, которая цепляет? Я давно еще смотрел этот язык, но так и не нашел той изюминки. Например, у Ruby это RoR, у Go есть routines, а Java, C/C++, Python просто занимают почетное место благодаря зрелости.
Думаю проблема низкой популярности D в отсутствии изюминки, а если она и есть то на неё не заостряют внимание и в итоге ни рыба ни мясо…
Думаю проблема низкой популярности D в отсутствии изюминки, а если она и есть то на неё не заостряют внимание и в итоге ни рыба ни мясо…
Это сугубо индивидуально для каждого, это не обьективная причина. Меня зацепил потрясающий дизайи и целостность языка.
Дизайн.
Нет не индивидуально, когда говорят RoR есть ассоциация с Ruby, когда говорят Go есть ассоциация routines, когда говорят Lua есть ассоциация c геймдевом, когда говорят D — ассоциации нет.
Ассоциации в принципе индивидуальны. У меня в lua, например, первая ассоциация — таблицы. И уже потом геймдев.
«Удобство C#, только на выходе не CIL, а нативный код» или Вам не нужен ответ и Вы в любом случае будете отстаивать своё мнение?
Если бы Брайт занялся инструментарием, поддержкой разных архитектур, я давным давно бы перелез с C++ на D.
Если бы Брайт занялся инструментарием, поддержкой разных архитектур, я давным давно бы перелез с C++ на D.
Удобный темплейтинг, компиляция в native код, compile time function evaluation, текстовые миксины. Вам этого мало?
Я бы сказал так, изюминка языка как раз в том, что он не заманивает какой-то особой изюминкой — это отлично спроектированный язык для практиков, созданный инженером, а не математиком :) Практичность, мощность и эффективность в нём ценятся выше какой-то Великой Концепции.
Это круто :)
Это круто :)
Метапрограммирование. В C++11 так и нет static if.
И кстати, такой изюминкой может стать поддержка Native Client, или поддержка любых других модных сегодня штук. Проблема языка в том, что никто не занимается его пиаром.
Для язык D существует FrontEnd в LLVM?
Не хватает еще статьи как линковать и использовать с С++.
Все ли сейчас возможно заменить С++ на D, например, при программировании под ARM?
Не хватает еще статьи как линковать и использовать с С++.
Все ли сейчас возможно заменить С++ на D, например, при программировании под ARM?
По поводу статьи — все будет. Есть фронтэнд к gcc.
Для язык D существует FrontEnd в LLVM?
Все ли сейчас возможно заменить С++ на D, например, при программировании под ARM?
Ну формально, имея ldc и gdc(фронтенд для gcc) можно собрать и под arm, но на практике еще не встречал упоминаний об успешном применении. У того же gdc в списке багов есть парочка с arm и они давно висят, до сих пор не пофикшены.
P.S. не так давно пробовал собрать gdc для android toolchain — не вышло
Все ли сейчас возможно заменить С++ на D, например, при программировании под ARM?
Ну формально, имея ldc и gdc(фронтенд для gcc) можно собрать и под arm, но на практике еще не встречал упоминаний об успешном применении. У того же gdc в списке багов есть парочка с arm и они давно висят, до сих пор не пофикшены.
P.S. не так давно пробовал собрать gdc для android toolchain — не вышло
А как насчет фрэймвроков, IDE, компиляторов и прочей инфраструктуры, делающей из абстрактного языка реальный инструмент? Расскажите?
Второй вопрос — RTTI
Второй вопрос — RTTI
В комментария к прошлой статье я уже обещал сделать статью о инструментарии. Обещал — сделаю. Может, даже следующая будет именно об этом, раз очень активно люди на это упирают.
Всем охота посмотреть вживую, прикинуть для своих задач. Многие избалованы умными IDE и привыкли пользоваться библиотеками/фреймворками для решения типовых задач — писать в «блокноте», компилировать и собирать в консоли, работать с API ОS/DE напрямую никого не заставишь даже на очень хорошем языке :)
А вот это неправда! Я на haskell программировал в gedit. И не хэлловорды, а вполне нормальные, хоть и не очень большие программы.
Да не то, чтобы даже «умными» :) Я вот visual studio при всем желании умной назвать не могу :) Привычными, скорее. Поэтому я VisualD использовал, а более «умные» плагины для Eclipse — нет.
Охохо, извините, но похоже я нашел гораздо более потрясающую воображение тему для статьи, чем скучный рассказ об инструментах :)
Так что не уверен, что будет дальше. Однако, инструменты обязательно будут, может только не в следующий раз.
Так что не уверен, что будет дальше. Однако, инструменты обязательно будут, может только не в следующий раз.
RTTI есть:
Выхлоп:
Или интересует что-то более конкретное?
// test.d
import std.stdio;
class Base {}
class Deriv : Base {}
void main()
{
Base o = new Deriv();
writeln( typeid(o) );
}
Выхлоп:
test.Deriv
Или интересует что-то более конкретное?
Пример для sort на C++ неправельный. Он не будет компилироваться, потому что auto arr = {...}; выводит тип arr как const int[]. Ну и у массива нет методов begin и end. Поэтому лучше использовать свободные функции begin/end. Учитывая всё это, правельный пример может выглядеть так:
std::vector arr{1, 2, 3, 4};
std::sort(begin(arr), end(arr), [](int a, int b) { return a > b && a < 42; });
Кстати, лямбда здесь инлайнится очень даже хорошо. Лямбда разкрывается в класс с operator(). Но современные компиляторы инлайнят даже если им дать указатель на функцию, когда функция известна во время компиляции.
Это напомнило мне одну презентацию Walter Bright'а, в которой он рассказывал про то как D лучше чем C++. У него был пример на C++ с вектором в несколько строчек, где было допущено 3 ошибки.
Форматирование полностью покоцало мой код. Попробуем еще раз:
std::vector<int> arr{1, 2, 3, 4}; std::sort(begin(arr), end(arr), [](int a, int b) { return a > b && a < 42; });
Да, знаю, что неправильный, я так написал, чтобы не зашумлять код техническими деталями и сконцентрировать внимание на смысле. Как сортировать массивы я знаю :)
Кстати, если уж об этом, будь я разработчиком стандарта, я бы предложил, чтобы auto в таких случаях дедуцировало именно vecror. Было бы удобнее. Все равно все гайдлайны говорят использовать его вместо сишных массивов.
Я не знаю, насколько тебе знаком С++11, но в этом случае (vector<int> arr = {1, 2, 3, 4}), вектор инициализируется через так называемый initialization list. Вектор не единственный класс, который можно инициализировать таким образом. Нет никаких причин, почему именно vector должен дедуцироваться в этом примере.
Нужно стараться всегда быть точным насколько это возможно. Иначе как можно доверять технической информации от человека, который принципиально неточен в деталях?
А про инлайнинг: аргумент про «достаточно умный компилятор» хорошо всем известен. Суть моего сравнения в том, что в C++ это нетривиально. Вернее, не так тривиально, как в D.
> в C++ это нетривиально
Ну, я бы так не сказал. Объекты классов с operator(), или по-другому, функторы, инлайнились всегда особенно хорошо (а лямбда в этом примере превращается именно в функтор).
Ну, я бы так не сказал. Объекты классов с operator(), или по-другому, функторы, инлайнились всегда особенно хорошо (а лямбда в этом примере превращается именно в функтор).
Те люди, которые возмущаются моим недоверием к C++, если это не повод похоливарить, а желание обьяснить мне истину — я знаю, что все замечательно инлайнится. Другое дело, что я всегда в таких ситуациях пытаюсь представить себя на месте человека, который пишет оптимизатор для уже готового компилятора. В D мне «дадут» уже гарантированно вычисленное на стадии компиляции выражение — я его проинлайню без дополнительных умственных усилий. В C++ же мне придется попотеть — как минимум доказать, что это можно инлайнить + мне самому придется вычислять код статически.
В C++ описание шаблонов делается в .h файле, таким образом при создании библиотек все равно можно использовать шаблонные функции, так как с библиотекой прилагается и сам .h файл. В D определение и объявление делается в одном файле. При создании библиотеки мне нужно прилагать файл с исходным кодом для использования шаблонной функции?
Sign up to leave a comment.
Язык программирования D — продолжение