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

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

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 с поддержкой всеми инструментами.

Если не сложно, поправьте в статье про «отсутствие аналогов».

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

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

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


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

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

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

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

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

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

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

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

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

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

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

Да, там будут проблемы с «гигиеничностью» (смешиванием имен из блока B и имен из самой функции foo), но все вполне решаемо.
Какие существуют большие проекты (не библиотеки) на D и кто его использует, кроме Facebook и Digital Mars?

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

P. S. С D я совершенно не знаком, все выводы сделаны исключительно на основании данной статьи.
Я так понял речь идет о последнем разделе. В этом языке функцию тоже можно передавать не только в 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
А есть ли в D аналог rvalue-ссылок как в C++11? Актуально для случая, когда внутри map вызываются вложенные функции f(g(x)), а сам x имеет составной тип. Хотелось бы для g(x) не создавать временный объект.
Ссылки есть, есть и спецификаторы параметров функции, определяющие, что параметр rvalue. Приведите пример, попробуем его переписать в синтаксисе D.
Вот пример где 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));
}

Это вы на С++ или на 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);
}
Я хотел, чтобы D удивил и сам развернул вычисление y=f(g(1)) в y = 1+1 во время компиляции.
Темплейтом это можно сделать полагаю, но зачем, это будет не гибко?
Сформулирую вопрос по другому: если есть два массива — X, Y, то можно ли отдельно определить свою функцию f так, чтобы затем в коде писать Y = f(X) (без foreach c вызовом Y[i] = f(X[i]). Хочется максимально приблизиться к математической нотации.
Оно?

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]));
}
Кстати, по поводу rvalue, не будет ли в этом примере лишнего копирования временного массива?
Копируется указатель на первый элемент и количество элементов. Элементы массива не копируются.
Запишутся ли указатель с длиной сразу в rvalue или лишний раз скопируется — не уверен.
Принцип «лишь бы не как у всех», разработчиков D не заботит и это хорошо.
int delegate(int X) power2 = delegate(int X) { return X * X; };

Как раз и получилось «лишь бы не как у всех». Большая избыточность определения.
Сравните со Scala:
val power2 = (x: Int) => x*x
можно даже слово delegate опустить, такая запись тоже корректна:
auto power2 = (int X) { return X * X; };

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

Конечно, если сравнивать полную форму запись с короткой.
Сравнивайте короткую с короткой, пример вам привели.
Оффтоп, но надеюсь, что кому-нибудь будет интересно.
Я разрабатываю 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

С тех пор как я с товарищами писали статьи правда ничего не изменилось? Вроде это всё уже было лет пять назад. 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.
Lisp это Lisp. На нем можно все, но для интерпрайса не годится. Весь интерпрайс закончится с уходом ведущих программистов :) Фактический каждый Липс программист уровнем выше среднего пишет на своем собственном языке, не согласны? :) Erlang — да, но специфика узкая.
D старается следовать принципу zero overhead, но пока это у C++ получается всё же лучше

Ну если у вас инфа более двух годичной давности, советую опять посмотреть на D, ситуация на мой взгляд изменилась в лучшую для D сторону.
Only those users with full accounts are able to leave comments. Log in, please.