Фишки языка D

    Очень радует, что на Хабре появляются статьи о языке D. Но, на мой взгляд, переводы хелпа и статей для чуть больше, чем для новичков не дают ничего в плане популяризации языка. Думаю, искушённой публике лучше представлять, пусть более сложные, но какие-то интересные вещи — фишки. Большинство из того, что можно назвать фишками D, есть и в других языках, но многое в D реализовано более эффектно и эффективно, на мой вкус во всяком случае. В D есть много интересного, о чем стоит рассказать, и начну я в этой статье с функций, но не совсем обычных.

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

    Анонимные функции они же делегаты, замыкания


    С анонимными функциями в D все хорошо. Реализация в стандарте полностью соответствует, тому что принято называть словами «анонимные функции» или «делегаты». Хорошо проглядывается сходство с удачными реализациями из других языков. Принцип «лишь бы не как у всех», разработчиков D не заботит и это хорошо.
    import std.stdio;
    
    int do_something_with_x(int X, int delegate(int X) func)
    {
    	return func(X);
    }
    
    void main() {
    	int Y = 10;
    
    	int delegate(int X) power2 = delegate(int X) { return X * X; };
    	auto powerY = delegate(int X) { return X ^^ Y; };
    
    	int mulY(int X) {
    		return X * Y;
    	}
    
    	writefln("power 2: %d", power2(4));
    	writefln("power 3: %d", (int X) { return X * X * X; }(4));
    
    	writefln("do_something_with_x; power2: %s", do_something_with_x( 2, power2 ));
    	writefln("do_something_with_x; powerY: %s", do_something_with_x( 2, powerY ));
    	writefln("do_something_with_x; muxY: %s", do_something_with_x( 2, &mulY ));
    	writefln("do_something_with_x; anon: %s", do_something_with_x( 2, (X){ return X*(-1); } ));
    }
    

    Что здесь? Переменной power2 присвоен делегат, определенный по месту присваивания. Переменной powerY с автоматическим определением типа присвоен делегат, который использует в расчете локальную переменную Y, то есть делегат в D, это еще и замыкание. А mulY — это просто замыкание, не делегат, которая по причине этого факта передается в функцию do_something_with_x по ссылке. Кстати, функция do_something_with_x у нас функция высшего порядка. Столько модных слов в одном небольшом банальном примере, клево, правда же?
    Первый writefn банальный. Во втором writefn у нас определена анонимная функция и сразу же вызвана, это очень популярный ход, например в JavaScript. Ну и интересна последняя строка с writefn. Там в параметре функции do_something_with_x, определена анонимная функция, причем не указан тип параметров. Это нормально, так как тип анонимной функции или, если хотите делегата, четко прототипирован в определении функции do_something_with_x.

    Partial functions (Частичное применение)


    Как уже писал, с делегатами все просто, они представлены в синтаксисе языка напрямую. Теперь немного другое. Прямой реализации в синтаксисе языка фичи, вынесенной в заголовок, нет, есть реализация в стандартной библиотеке, но не такая как представлена ниже. В библиотеке задействованы фишки, которые будут приведены в последнем разделе статьи. А здесь мы пойдем другим путем, путем змеи :). Как известно, в Python любая функция — это объект, а любой объект, если в его классе определен метод __call__ может быть вызван как функция. Язык D предоставляет нам аналогичную возможность для объектов с помощью метода opCall. Если этот метод определен в классе, то экземпляр класса приобретает свойства функции. Есть методы с помощью которых, например, можно взять индекс (типо: obj[index]) и много чего еще. Таким же способом переопределяются операторы. В общем, это тема для отдельной большой статьи. Хочется сказать, что это было подсмотрено в Python, но знаю что эта концепция гораздо старше. Итак, частичное применение:
    import std.stdio;
    
    int power(int X, int Y) {
    	return X ^^ Y;
    }
    
    int mul(int X, int Y) {
    	return X * Y;
    }
    
    class partial
    {
    	private int Y;
    	int function(int X, int Y) func;
    
    	this(int function(int X, int Y) func, int Y) {
    		this.func = func;
    		this.Y = Y;
    	}
    
    	int opCall(int X) {
    		return func(X, Y);
    	}
    }
    
    int do_partial_with_x(int X, partial func) {
    	return func(X);
    }
    
    
    void main()
    {
    	auto power2 = new partial(&power, 2);
    	auto mul3 = new partial(&mul, 3);
    
    	writefln("power2: %d", power2(2) );
    	writefln("mul3: %d", mul3(2) );
    
    	writefln("do_partial_with_x: %d", do_partial_with_x(3, power2) );
    	writefln("do_partial_with_x: %d", do_partial_with_x(3, new partial(&mul, 10)) );
    }
    

    В примере есть две обычные функции, обобщенный случай возведения в степень и умножения. И есть класс, объекты которого благодаря спец методу opCall могут быть использованы как функции, поведение которых задается при создании объекта. Класс у нас получился со свойствами функции высшего порядка, принимает параметром функцию, определяющую поведение. А так же со свойствами частичного применения, один из параметров определяется в момент создания объекта-функции.
    Таким образом, созданы два объекта-функции, одна возводит во вторую степень число, вторая умножает число на три. Практически все, что можно делать с обычными функциями, можно делать и с объектами такого типа, как далее в примере — передавать их в функцию высшего порядка.

    Обобщённые функции, шаблоны, миксины


    Ну и напоследок, как водится, самое забавное. Представте себе, что стоит такая задача, написать функции, первая будет применять ко всем элементам массива чисел с плавающей точкой такую формулу: «sin(X) + cos(X)», а вторая для массива целых чисел такую: "( X ^^ 3) + X * 2". И небольшой сразу нюанс, что от релиза к релизу формулы будут меняться. Что на это ответит программист на D? «Да не вопрос, сколько угодно формул», и напишет одну обобщенную функцию.
    import std.math;
    import std.stdio;
    
    T[] map(T, string Op)(T[] in_array) {
    	T[] out_array;
    	foreach(X; in_array) {
    		X = mixin(Op);
    		out_array ~= X;
    	}
    	return out_array;
    }
    
    void main() {
    
    	writeln("#1 ", map!(int, "X * 3")([0, 1, 2, 3, 4, 5]));
    	writeln("#2 ", map!(int, "(X ^^ 3) + X * 2")([0, 1, 2, 3, 4, 5]));
    
    	writeln("#3 ", map!(double, "X ^^ 2")([0.0, 0.5, 1.0, 1.5, 2.0, 2.5]));
    	writeln("#4 ", map!(double, "sin(X) + cos(X)")([0.0, 0.5, 1.0, 1.5, 2.0, 2.5]));
    }
    

    Могу ошибаться, но прямого аналога нет, во всяком случае в популярных, компилируемых языках. Макросы С наиболее близко, но там это не будет так красиво выглядеть. Здесь задействованы сразу две фишки D: шаблоны и миксины, дающие в паре очень элегантную реализацию обобщенной функции. Шаблоны достаточно обычные, разве что выглядят не так пугающе как в С++. А миксин, хоть и напоминает макрос, но реализован по другому. За формирование результирующего кода ответственен не препроцессор, а сам компилятор, и поэтому строка миксина может быть вычислена во время компиляции.
    Вообще D может делать очень многое во время компиляции. И миксины в паре с шаблонами представляют очень мощный инструмент, который широко задействован в стандартной библиотеке (phobos) языка. Большинство простых функций реализовано именно так. Это конечно имеет побочный эффект, для новичка в языке просмотр их исходного кода равносилен чтению «филькиной грамоты». Но позже, когда становится понятна суть этого метода, остается только одна эмоция — восхищение.

    На этом я, пожалуй, откланяюсь. Буду рад если кто-то продолжит тему фишек в D, там их еще немерено :)
    Поделиться публикацией
    Комментарии 63
      0
      >> Представте себе, что стоит такая задача, написать функции, первая будет применять ко всем элементам массива чисел с плавающей точкой такую формулу: «sin(X) + cos(X)», а вторая для массива целых чисел такую: "( X ^^ 3) + X * 2".
      >> И небольшой сразу нюанс, что от релиза к релизу формулы будут меняться.
      >> Могу ошибаться, но прямого аналога нет, во всяком случае в популярных, компилируемых языках.
      В C# (и вообще .net) это (а) делается из коробки с помощью LINQ-операции select и (б) легко реализуется самостоятельно с помощью generics и делегатов.
        –1
        В C# (и вообще .net) это (а) делается из коробки с помощью LINQ-операции select и (б) легко реализуется самостоятельно с помощью generics и делегатов.

        Когда я работал с С# такого еще не было. Это происходит в compile time? Приведите пример кстати, сравним элегантность решения.
          +1
          (пишу по памяти)

          new [] {0, 1, 2, 3, 4, 5}.Select(x => Math.Pow(x, 3) + x*2);
          new [] {0.0D, 0.5D, 1.0D, 1.5D, 2.0D, 2.5D}.Select(x => Math.Sin(x) + Math.Cos(x));

          Ну и да, это строгий compile-time с поддержкой всеми инструментами.

            +1
            Да весьма похоже и не уступает по читабельности.
              +1
              Если не сложно, поправьте в статье про «отсутствие аналогов».

              (ну и насколько я знаю, в функциональных языках — том же F# — это обязательная функциональность)
                +1
                там есть оговорка «могу ошибаться» и «буду рад на указание неточностей и оспаривание моих утверждений в комментах» :)
              +2
              Но ведь в D формула берется из строки, которую может вернуть функция, сделав произвольную обработку, лишь бы можно было вычислить на этапе компиляции. Разные по силе инструменты.
                0
                Произвольная обработка на этапе компиляции?

                Вероятно, это действительно разные инструменты, но то, что описано в посте — легко реализуется так, как я описал.
                  +1
                  Кстати да, пример на С# больше соответствует записи через лямбду
                  map!(x => x^^2)([1,2,3]);
                  
                  которую привел ниже Monnoroch
                0
                И тут вспоминаем про haskell…

                map (^2) [0, 1, 2, 3]
                map (\a -> sin a + cos a) [0, 1, 2, 3]
                


                Весьма элегантно, не правда ли?
                  0
                  Да не плохо, а есть возможность вычисленное в compile time выражение подставить вместо лямд?
                    0
                    В haskell ленивые вычисления (говорю о runtime).

                    А считать что-то в compile time можно с помощью template haskell.
              +2
              Не хочу себя рекламировать (хотя кого я обманываю, хочу конечно :), но статьи про D появляются на хабре уже много лет (вот мой цикл: habrahabr.ru/post/135720/), и на мой взгляд тема вводных статей про фишечки себя уже давно исчерпала.
              Гораздо интереснее было бы почитать статьи от гуру про современные best practices и дизайн языка.
              Так что, хоть я и рад популяризации языка, статья ничего нового не рассказала.
                +3
                Неплохие статьи, почему забросили это дело?
                статья ничего нового не рассказала

                Цели рассказать что-то новое хорошо знакомому с языком D разработчику и не ставилось. В интро конкретно написано, что статья направлена на популяризацию языка.
                  0
                  почему забросили это дело?

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

                  Я имел ввиду именно то, что уже интро-статей было полно, и не только мой цикл, были и еще.
                    +1
                    Ну пока язык не станет популярен хотя бы на уровне, Ruby, популяризирующие статьи не помешают. Цель то, не достигнута.
                      0
                      После долгих лет слежения за языком я все больше убеждаюсь в необходимости централизованной популяризации. Как у Rust, Java, C#, Go. Должна быть группа людей, очень яростно спамящая фултайм во все щели про свой язык. D-сообщество такого не делает, а в вин без этого я не верю.
                        +1
                        От D немного отпугивает личность Александреску, избравшего этот язык в качестве полигона для обкатки своих безумных фантазий.
                          +1
                          А меня наоборот он и привлек =)
                          Его фантазии кажутся безумными только когда они выражены на C++, который не предназначен. А в D они как раз хорошо вписаны, мне очень понравилось.
                            0
                            Плюсую мнение, особенно когда проходит первый шок от смеси темплейтов и миксинов придуманное им начинает даже восхищать :)
                          0
                          avoid success at all costs — это девиз Haskell популярность которого растет))
                  +4
                  Мне вот в этой связи интересны ответы на 2 вопроса:
                  1. Какие существуют крупные проекты, написанные преимущественно на D, которые можно применить при развертывании реальных приложений (веб-сервера, сервера очередей, MTA и т.п.)?
                  2. В связи с растущей популярностью Go и Rust — как языков для системного программирования на замену C, является ли D языком из той же самой ниши или же это что-то более высокоуровневое ближе к Java \ C#? Если является, то в чем могут заключаться его конкурентные преимущества по сравнению с Rust \ Go?
                    +3
                    В каждой статье по Rust его сравнивают с Go. А теперь еще и в статьях про D.
                    Нельзя эти языки сравнивать, у них совершенно разные ниши. Это как сравнивать си и эрланг.
                    А D еще не определился с нишей. Официальная позиция — хорош для всего, но он совершенно точно хуже, чем раст для системного программирования и совершенно точно хуже, чем го для асинхронных серверов. Я бы сказал, что D — это улучшенная нативная Java.
                      0
                      и совершенно точно хуже, чем го для асинхронных серверов.

                      Почему?
                        +2
                        Я писал на обоих и такое мое впечатление.
                        Обьективно потому, что у Go все, что нужно, встроено в рантайм, а в D нужно использовать фреймворк vibe.d, который не встроен в язык, и поэтому doesn't feel native, по сравнению с Go. Плюс, в Go весь сторонний код оформлен, как библиотеки, а не как фреймворк, а фреймворки я терпеть не могу, мне кажется, что меня загоняют в рамки, и это противно :)
                        Плюс, go get, ах, как я его обожаю! dub для D появился не так давно и он несколько сложнее в использовании и слегка с багами (как и vibe.d сам, да и как и D тоже).
                          0
                          хз, я тут рискнул нечто подобное (асинхронные сетевые сервисы) пилить, правда это скорее event-driven движок. А асинхронные сетевые сервисы он дает в комплекте с генераторами питона. И в принципе уже кое-что работает, и без vibe.d, что характерно. Хотя и не вижу препятствий с помощью фиберов похожее полностью на D сделать. Если есть акк на битбакете дал бы доступ на репу, почитать мнение.
                      0
                      Go — это всё таки не замена C, вот Rust — да, претендует на эту роль, а у Go другая область применения, которая как раз ближе к Java \ C# чем к C и C++.
                      +1
                      А в чем разница между двумя синтаксисами?

                      int delegate(int x) d = ...
                      int function(int x) f = ...
                      
                        +1
                        Функция — это просто функция: sizeof(function) == sizeof(void*). Делегат — это функция + контекст. Фактически, замыкание.
                          0
                          Спасибо, теперь понятно!
                          –2
                          В первом случае определение типа делегата, во втором определение типа функции, похожее на то как определяется ссылка ну функцию в C.
                          Например: int function(int x) func;
                          Аналог: typedef inf(*func)(int);
                          +1
                          Идея миксинов очень правильная, но реализация (строки в кавычках) мне концептуально не нравится — так как теряется возможность подсветки синтаксиса в IDE (нет возможности отличить обычную строку от фрагмента кода). Лучше как в Nemerle — в специальных скобках для квазицитирования.
                          Хотя в данном случае и квазицитирования не надо, достаточно разрешить передачу в шаблоны блоков кода в фигурных скобках. Такое кстати можно и в С++ ввести с минимальными изменениями в синтаксисе.
                            0
                            Считайте, что mixin(" и ") — специальные скобки.
                            достаточно разрешить передачу в шаблоны блоков кода в фигурных скобках.

                            Это не правда. У миксинов не только то применение, что указано в статье. Оно, кстати, глупое и уже неофициально deprecated — никто так не пишет. Теперь для целей, который в статье используют именно то, что вы сказали — лямбды.
                            А миксины совсем для другого, почитайте мою статью: habrahabr.ru/post/135720/.
                              0
                              Оно, кстати, глупое и уже неофициально deprecated — никто так не пишет

                              Читал что хотят заменить чем то более продвинутым, но пока не вижу, как собственно и глупости такого подхода.
                                0
                                Уже года два с половиной как передают лямбды вместо строк. А строки оставили для обратной совместимости.
                                map!(x => x^^2)([1,2,3]);
                                

                                А глупость описал оригинальный комментарий: никакой подсветки и поддержки тулзами.
                                  0
                                  Да это выглядит более правильно, но теряется фишка миксинов — вычисление подставляемой строки во время компиляции. Хотя не приходит на ум где это реально может пригодиться.
                                    0
                                    О применении миксинов можно почитать в TDPL, в моих статьях и продвинутые вещи — в бесчисленных темах на форумах. В основном, это параметрическая кодогенерация.
                                0
                                Я кстати имел в виду кстати не лямбды, а именно блоки кода, включаемые на этапе компиляции. Отличие в том, что лямбды вызываются (то есть генерируется код вызова функции), а при прямой подстановке никаких вызовов не происходит. Я занимался embedded программированием, там в условиях очень ограниченных ресурсов приходилось писать такие вещи с помощью обычных макросов.

                                Например, в шаблоны С++ можно передавать целые числа; они передаются не через стек в рантайме, а напрямую встраиваются в код во время компиляции. Я считаю, что совершенно аналогично можно сделать передачу произвольных сущностей времени компиляции, в частности блоков кода
                                template<block B>
                                void foo()
                                {
                                   // do something
                                   B; // это не вызов, а именно подстановка кода напрямую
                                   // do something
                                }
                                // ...
                                foo<{ bar1(); }>();
                                

                                Да, там будут проблемы с «гигиеничностью» (смешиванием имен из блока B и имен из самой функции foo), но все вполне решаемо.
                                  0
                                  Лямбды всегда инлайнятся, если компилятор решит, что это лучше.
                                0
                                Для этого есть литералы токенов q{}. Всё что в кавычках станет стокой. Идеально для кода в миксинах. dlang.org/lex.html Token Strings
                                0
                                Какие существуют большие проекты (не библиотеки) на D и кто его использует, кроме Facebook и Digital Mars?

                                + Какие есть учебные курсы, основной язык которых — D?
                                  0
                                  Пока не густо, здесь небольшой список wiki.dlang.org/Current_D_Use
                                  Курсов похоже нет, как и альтернативе книге Александреску.
                                    0
                                    Да ладно. Есть как минимум две платные книги по D и одна бесплатная.
                                    Платные:
                                    Andrei Alexandrescu — The D Programming Language (в том числе перевод на русский)
                                    Adam D. Ruppe — D Cookbook
                                    Бесплатная:
                                    Ali Çehreli — Programming in D
                                  +1
                                  Прочитав эту статью я так и не понял в чём смысл этих шаблонов и миксинов для реализации map? Чем это лучше чем просто передавать нужную функцию в map как это обычно и реализуется в других языках? Я пока вижу только недостатки:
                                  1. Необходимость указывать тип при вызове map, у вас он один, хотя вообще говоря для map возвращаемый тип не обязательно совпадает с принимаемым. Непонятно что мешает компилятору вывести этот тип из типа передаваемого позже массива.
                                  2. Странный синтаксис с кавычками из-за чего формула воспринимается как строка, а не как код.
                                  3. В строке присутствует параметр с названием X, хотя то что он должен называться именно X определено внутри функции map. Если я правильно понял, в этой строке можно использовать любые переменные, которые определены в скоупе где вызывается mixin, к каким проблемам это может привести думаю не стоит говорить.
                                  Да и map тут какой-то не очень обобщённый — работает только для массивов.

                                  P. S. С D я совершенно не знаком, все выводы сделаны исключительно на основании данной статьи.
                                    0
                                    Я так понял речь идет о последнем разделе. В этом языке функцию тоже можно передавать не только в map, но и в любую другую, в первых двух разделах это как раз и использовано. В третьем же просто хотелось привести пример с одним из вариантов использования mixin. Вариант с передачей в map лямбды есть в комменте habrahabr.ru/post/246623/#comment_8194283.
                                    1) Функция map в примере может быть использована для разных типов, в примере показан int и double, поэтому применен темплейт и тип указан, что бы компилятор знал для каких типов построить версии темплейтированной функции в данном случае для int и double.
                                    2) Да странный, одна из особенностей, и наверно будет заменена на что-то более однозначное.
                                    3) Это и есть миксин :) Компилятор просто вместо строки mixin(Op); подставит то что было передано как Op. То есть результирующая строка, которая пойдет на компиляцию для Op = «sin(X) + cos(X)» будет выглядеть например так:
                                        foreach(X; in_array) {
                                            X = sin(X) + cos(X);
                                            out_array ~= X;
                                        }
                                    

                                    Какие в этом проблемы? Да не вижу особых, если есть понимание как это работает.
                                    Да и map тут какой-то не очень обобщённый — работает только для массивов.

                                    map в примере чисто моя отсебятина лишь для илюстрации статьи. Реализация настоящего map и много чего из этой оперы выполнена господином Александреску на самом высоком уровне :) dlang.org/phobos/std_algorithm.html#map
                                      0
                                      А есть ли в D аналог rvalue-ссылок как в C++11? Актуально для случая, когда внутри map вызываются вложенные функции f(g(x)), а сам x имеет составной тип. Хотелось бы для g(x) не создавать временный объект.
                                        0
                                        Ссылки есть, есть и спецификаторы параметров функции, определяющие, что параметр rvalue. Приведите пример, попробуем его переписать в синтаксисе D.
                                          0
                                          Вот пример где g переводит скаляр в вектор, а f — вектор в скаляр.

                                          double f(double [] x) {
                                          	return x[0] + x[1];
                                          }
                                          
                                          double [] g(double x) {
                                          	return [x, x];
                                          }
                                          
                                          int main()
                                          {
                                          	double y = f(g(1));
                                          }
                                          

                                            0
                                            Это вы на С++ или на D написали? :) Если на C++, то на D этот же код вернет тот же результат 2, что и на С++. И я не вижу в нем r-value передаваемое в функцию. Может мы друг друга не правильно поняли, я ожидал нечто такое, чему эквивалент на D был бы такой:
                                            void sqr(ref int x)
                                            {
                                            	x = x * x;
                                            }
                                            
                                            void main()
                                            {
                                            	int x = 2;
                                            	sqr(x);
                                            	assert (x == 4);
                                            }
                                            
                                              0
                                              Я хотел, чтобы D удивил и сам развернул вычисление y=f(g(1)) в y = 1+1 во время компиляции.
                                                0
                                                Темплейтом это можно сделать полагаю, но зачем, это будет не гибко?
                                                  0
                                                  Сформулирую вопрос по другому: если есть два массива — X, Y, то можно ли отдельно определить свою функцию f так, чтобы затем в коде писать Y = f(X) (без foreach c вызовом Y[i] = f(X[i]). Хочется максимально приблизиться к математической нотации.
                                                    +1
                                                    Оно?

                                                    import std.algorithm;
                                                    
                                                    auto Sqr(int[] a)
                                                    {
                                                        return map!(x => x * x)(a);
                                                    }
                                                    
                                                    void main()
                                                    {
                                                        int[] X = [0, 1, 2, 3, 4];
                                                        auto Y = Sqr(X);  // ... так, чтобы затем в коде писать Y = f(X)
                                                        assert(equal(Y, [0, 1, 4, 9, 16]));
                                                    }
                                                    
                                                      0
                                                      Да! Спасибо
                                                        0
                                                        Кстати, по поводу rvalue, не будет ли в этом примере лишнего копирования временного массива?
                                                          0
                                                          Копируется указатель на первый элемент и количество элементов. Элементы массива не копируются.
                                                          Запишутся ли указатель с длиной сразу в rvalue или лишний раз скопируется — не уверен.
                                          0
                                          Принцип «лишь бы не как у всех», разработчиков D не заботит и это хорошо.
                                          int delegate(int X) power2 = delegate(int X) { return X * X; };

                                          Как раз и получилось «лишь бы не как у всех». Большая избыточность определения.
                                          Сравните со Scala:
                                          val power2 = (x: Int) => x*x
                                          
                                            0
                                            А если так?

                                            auto power2 = delegate(int X) { return X * X; };
                                            
                                              +1
                                              можно даже слово delegate опустить, такая запись тоже корректна:
                                              auto power2 = (int X) { return X * X; };
                                              

                                              И даже такая:
                                              auto power2 = (int X) => X * X;
                                              
                                              0
                                              Большая избыточность определения.

                                              Конечно, если сравнивать полную форму запись с короткой.
                                              Сравнивайте короткую с короткой, пример вам привели.
                                              +2
                                              Оффтоп, но надеюсь, что кому-нибудь будет интересно.
                                              Я разрабатываю GUI библиотеку на D — DlangUI
                                              • Кроссплатформенная, с поддержкой ускорения отрисовки с помощью OpenGL.
                                              • API напоминает Android UI.
                                              • Не враппер — написано на D, поэтому легко расширять, добавлять новые виджеты.
                                              • Внешний вид можно значительно изменять с помощью тем (похожи на темы Android).
                                              • Unicode и поддержка нескольких языков интерфейса.
                                              • Адаптация интерфейса под разные размеры и разрешения экрана.

                                              Демо, нужны GIT, DUB и DMD:
                                                  # Скачать исходники с GitHub
                                                  git clone https://github.com/buggins/dlangui.git
                                                  cd dlangui
                                                  # example 1 - демо, в котором используется большинство виджетов
                                                  dub run dlangui:example1 --build=release
                                                  # tetris - демо, игра TETRIS
                                                  dub run dlangui:tetris --build=release
                                              

                                                0
                                                С тех пор как я с товарищами писали статьи правда ничего не изменилось? Вроде это всё уже было лет пять назад. habrahabr.ru/post/75451/

                                                > Могу ошибаться, но прямого аналога нет, во всяком случае в популярных, компилируемых языках.
                                                В копилку Common Lisp. Не популярный, да. Компилирется в некоторых реализациях, например самой популярной — SBCL. Вообще map уже есть, но у вас он шаблонный, а не функциональный (с лямбдой), так что вот тоже шаблонный:

                                                (defmacro tmap (op array)
                                                  `(loop for x in ,array collect ,op)

                                                (defun main ()
                                                  (format t "#1 ~A~%" (tmap (* x 3) '(0 1 2 3 4 5)))
                                                  (format t "#2 ~A~%" (tmap (expt x 3) '(0 1 2 3 4 5)))

                                                  (format t "#3 ~A~%" (tmap (* x 3) '(0.0 0.5 1.0 1.5 2.0 2.5)))
                                                  (format t "#4 ~A~%" (tmap (expt x 3) '(0.0 0.5 1.0 1.5 2.0 2.5))))

                                                В SBCL есть кое какой решатель типов. Если он сообразит какого типа списки (на буквальные не реагирует, должно быть объявление типа хоть где-нибудь), то в скомпилированном коде окажутся вызовы машинных команд для * и expt (где есть), если не сообразит — то вызовы функций для произвольных типов.

                                                Конечно, D старается следовать принципу zero overhead, но пока это у C++ получается всё же лучше. Компромисс.: ) Помню, в своё время он мне и нравился как золотая середина между перформансом и удобством, потом обнаружил себя либо за задачами где перформанс не важен, тогда годится Common Lisp (с перформансом всё равно на уровне компилируемых), либо за энтерпрайзнутыми серверными кластерами, где лучше Erlang.
                                                  0
                                                  Lisp это Lisp. На нем можно все, но для интерпрайса не годится. Весь интерпрайс закончится с уходом ведущих программистов :) Фактический каждый Липс программист уровнем выше среднего пишет на своем собственном языке, не согласны? :) Erlang — да, но специфика узкая.
                                                  D старается следовать принципу zero overhead, но пока это у C++ получается всё же лучше

                                                  Ну если у вас инфа более двух годичной давности, советую опять посмотреть на D, ситуация на мой взгляд изменилась в лучшую для D сторону.

                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                  Самое читаемое