Pull to refresh

Comments 107

Мне как не программисту Go более понятен и читабелен.
К сожалению на HelloWorld примерах преимущества D над Go раскрыть не получится ну никак. Вот когда код начинает расти и появляется потребность гибко выражать свои мысли, то тут D равных нет. Как я уже много раз писал Go не простой, он примитивный. Вот Python простой и из-за этого с ним иногда бывает очень удобно.

А вот Go… больше всего тянет на хипстерское поделие с непонятными перспективами. В начале куча народу на него бросилось переходить, а потом оказалось, что что-то сложнее микросервисов на нем писать не удобно. Вон тот же DropBox с Go уже на Rust переписывать бросились. Тоже конечно сомнительный в плане продуктивности язык, но судя по тому как на него Сишники переходят свои задач решать позволяет.

Прочитайте лучше, почему они переписывать решили http://www.wired.com/2016/03/epic-story-dropboxs-exodus-amazon-cloud-empire/ Все дело в потреблении памяти, что для модели памяти Go объяснимо.

Как я уже много раз писал Go не простой, он примитивный

больше всего тянет на хипстерское поделие с непонятными перспективами

Я теперь начинаю понимать divan0 и его реакцию на комментарии от людей, которые явно не понимают то, о чем пишут. Перспективы у него более чем радужные. Он все таки же прекрасен для backend кода, где нужна конкурентность и простота работы с сетью. К сожалению, судя по возможностям D, я, так же как и C++, буду всеми способами обходить его стороной в этих задачах. Он не дает никаких преимуществ и возвращает нас в прошлое к колбекам для хоть какой-то асинхронности.
Про асинхронность не правда. Корутины есть (называются Fiber), кроме того есть целый фреймвёрк для асинхронного io: vibe.d
Я не говорил, что там ничего нет. Из того, что я видел, код получается такой же нечитабельный, как и раньше. Fibers, как бы, и раньше были в других языках. Проблема с ними, что пользоваться ими все так же неудобно, как и обычными потоками, плюс у них нет нормального планировщика и D тут не исключение. А для сокетов предлагают всю туже событийную модель и колбеки — нет уж спасибо. Мне этого ужаса уже хватало в прошлом. Спасибо, что пришли Go и C# с асинками, которые дают простой линейный сетевой код, который прекрасно использует конкурентность.
Не совсем понял, что вам не понравилось в файберах. Асинхронный код превосходно пишется линейно:

while(true) {
        c.write(buf.data);
        c.write(['\0']);
        buf.clear();
        c.readUntil(buf, ['\0'], SIZE);
    }
}

Это кусок одного очень простого теста, слегка усложнённый вариант эхо сервера. Пишет в сокет то, что получил. Что c.write, что c.readUntil — асинхронные операции, в которых произойдёт переключение волокон исполнения. С моей точки зрения Fibers — абсолютный эквивалент goroutines и, что уже субъективно, гораздо удобнее async из C#.
То есть придется для асинхронной работы искать библиотеки, которые её поддерживают? (как в питоне, там еще с вариациями под разные эвентлупы)
Fiber в стандартной библиотеке. Vibe.d поддерживает множество ивентлупов (libevent, libev, win32, собственная библиотека libasync). Большинство библиотек для асинхронных операций основываются на vibe.d, он стал почти стандартом, поэтому проблемы совместимости нет. Кроме того модель сопрограм такова, что если функция не выполняет асинхронных действий сама, то ей не требуется какая-то особая поддержка асинхронности. То есть любые синхронные библиотеки отлично работают в асинхронном коде.
Только могут заблокировать поток своими синхронными вызовами. Go же изначально затачивался на такую архитектуру, поэтому в нём все синхронные вызовы приводят к предварительмому перекидыванию ожидающих горутин на другие воркеры.
Странно. Оригинал пропал с сайта языка. Остались только "C to D" и "C++ to D".

А так, отсутствие нескольких возвращаемых значений печалит. Ремарки про "ручного жонглирования типами" вообще не понятны, а судя по исходникам рантайма, ничего дельного в плане поддержки конкурентности в рантайме не видно, что еще более печально. Судя по всему, для написания серверного кода Go так и останется более предпочтительным. В общем, все более укрепляется мнение, что D это C++ со словом import. Такой же сложный, напичканный всем чем можно язык, который не имеет четкого назначения. А как мне кажется, без чего-то эдакого он никуда не пробьется. У конкурентов в лице Go и Rust оно есть в полной мере.
А зачем вам несколько возвращаемых значений?

Ручное жонглирование — это про все эти ручные преобразования типов и копипаста одного и того же для разных типов.

Что именно вам не хватает для конкурентности?

Для разработки сервера есть VibeD.
Затем, чтобы возвращать несколько значений, не используя костыли в виде массивов и указателей/ссылок. Это удобно.

Ручное жонглирование. Мне тоже так казалось раньше. Реальный код что-то у меня вообще нигде не требует этих преобразований повсюду и копипасты. Я не библиотеки пишу на все случаи жизни, а реальный код. Дженерики мне пока ниразу не понадобились. Вот вообще.

Конкурентности. D возвращает нас в прошлое, где конкурентность реализована в какой-то убогой библиотеке и вообще всем на нее пофиг, сам как-нить разбирайся. А хочу то, что смогли Go, C# и иже с ними — предельная простота конкуретного кода. Предельная простота работы с сетью. Ты просто пишешь так, как требует того логика. Пришел запрос — сразу его отправил, сразу получил статус отправки, сразу обработал ошибки. Мне не хочется снова пробираться сквозь колбеки и обработчики входящих событий. Пока что я не видел на D простых примеров. Все даже самые примитивные примеры это портянки на весь экран.

Поэтому я выбрал Go для последних backend проектов и ниразу не пожалел. Получилось настолько просто, понятно, а главное, все это заработало буквально сразу. Вот честно, не нарадуюсь. Поэтому я совсем не удивлен, почему этот язык так полюбился в этой области.
"Сложный" пример предлагал чуть выше, а за совсем простыми можете обратиться сюда. Пример оттуда:

auto conn = connectTCP("time-c.nist.gov", 13);
logInfo("The time is: %s", conn.readAllUTF8());

В одной строке подключились, в другой уже читаем. Не знаю, как можно это сделать ещё проще.
Это не пример, а просто порнография какая-то, я видел его и не посчитал уместным даже считать примером. Другие примеры были на HTTP, что тем более примитив. Простой пример — это написание маломальски простого протокола обмена между сервером и клиентом на TCP. И мне нужно, чтобы операции с сетью не блокировали все приложение, чтобы код оставался линейным, чтобы были простые механизмы прерывания блокирующих операций по запросу или таймеру.

Вот к примеру, на C# отправка email сообщения полностью асинхронно на голом TCP без блокировок с таймаутами и отменой влезает в одну небольшую функцию с полностью процедурным кодом. А до этого было уродство с тучей колбеков и необходимостью локов для защиты данных.
Go — будет еще проще и яснее, потому что весь сетевой стек построен вокруг зеленых потоков. В рантайме выделены отдельные потоки (network poller), которые сидят на select функции и управляют всеми read/write операциями в приложении. Закрой в любом потоке сокет и все заблокированные потоки разблокируются и вернут ошибку. Асинхронность ведь придумана от того, что блокирующие операции требуют использовать слишком много ОС потоков, что делать нельзя. В Go горутины практически бесплатны, а значит и все костыли событийной модели и колбеков не нужны. Можно просто писать код, наконец-то. Наконец-то можно создавать поток на каждое подключение и на каждую фоновую операцию и не бояться, что это сожрет всю память.

И вот из того, что я видел, в D предлагают опять эти уродливые события и колбеки, которые просто невозможно читать. Встречал даже попытки обернуть во что-то похоже на Go, но выглядит это все так же нечитабельно. И мне опять вспоминается C++ с вечными попытками сделать так же красиво, как могут другие, ведь у нас же такой мощный язык. Но получается нечитабельная хрень, которую потом задолбаешься поддерживать в рабочем состоянии.
И вот из того, что я видел

Мне кажется проблема в том, что асинхронность в D вы не видели.

Наконец-то можно создавать поток на каждое подключение и на каждую фоновую операцию и не бояться, что это сожрет всю память.

Это точно так же можно делать в D. Fiber по легковесности эта та же самая горутина, можете создавать десятками и сотнями тысяч.

в D предлагают опять эти уродливые события и колбеки

Я вас не понимаю, честно. Найдите в vibe.d хоть один колбек. Кроме разве что onConnection и onTimer. Но они и инициируются не кодом, а некой третьей стороной, для них нет линейного кода. Так проиходит и в Go и в C#.
А для чтения, записи, подключения к кому-то нет никаких колбеков или событий. События есть для общения между сопрограммами, но это тот же самый select в Go, только гораздо универсальнее.
Бывают ситуации, где предлагается альтернатива: колбек или возвращаемое значение. Просто так получилось, что чистые колбеки работают чуть-чуть быстрее и это API оставлено. Но код всё равно можно писать синхронно.
Всё сделано так чтобы было удобно писать именно линейный код: предоставляются асинхронные линивые диапазоны для чтения/записи, автоматически закрываются соединения при выходе из скоупа и тд, и тп. Чего-чего, а callback hell, это точно не про vibe.d
Вы какие-то глупости говорите. VibeD, Go, MeteorJS и тп — одного поля ягоды, в том плане, что везде есть "зелёные потоки", они же "волокна", они же "сопрограммы", они же "файберы", они же "корутины", везде на каждый запрос создаётся отдельная задача, везде есть пул воркеров, которые эти задачи выполняют, везде задачи могут блокироваться в ожидании событий, позволяя воркеру заняться тем временем другими задачами, везде код этой задачи является синхронным, без каких-либо колбэков. Откуда вы взяли уродливые колбэки в D?

Держите простой пример в стиле го:

auto go( alias task , Arg )( Arg arg... )
{
    return runTask({
        task( arg );
    });
}

void say( string s )
{
    for( int i = 0 ; i < 5 ; ++i ) {
        sleep( 100.dur!"msecs" );
        writeln( Thread.getThis().id , " " , s );
    }
}

shared static this()
{
    go!say( "world" );
    say( "hello" );
    setIdleHandler({ exitEventLoop; });
}

25764 hello 
25764 world 
25764 hello 
25764 world 
25764 hello 
25764 world 
25764 hello 
25764 world 
25764 hello 

В данном примере я намеренно запускаю задачи на одном и том же воркере, чтобы показать их кооперативность.
Всё же не очень понятно чем не угодили массивы и структуры — специально предназначенные для группировки сущности. Можно хотя бы один наглядный пример? Кроме возврата ошибок — это тема отдельного холивара.

Не покажете реальный код? А то у меня без дженериков не получается ничего.
ok, countBytes, err := writeTo(...);

А дженерики — это в любом случае, замедление в real-time, ну или отказ от типобезопасности. Я лично ставлю на кодогенерацию, как, с одной стороны, возможность обобщений, а с другой высокая производительность.
Не очень понял, что означает ok, но err в D принято бросать исключением, так что код получится такой, если использовать столь же низкоуровневые средства:

auto countBytes = core.sys.posix.unistd.write(fd, buffer.ptr, size)

Или даже такой, при высокоуровневых:

"output.text".write( "hello!" )

Есть два варианта реализации дженериков:

  1. Как в C# или Java, где для любых типов реализауется один машинный код. Это даёт малый размер бинарника, но не даёт его толком оптимизировать.
  2. Как в D и C++, где для каждой комбинации типов генерируется свой машинный код. Это даёт максимальную производительность ценой увеличения бинарника.

Кодогенерация в Go — это фактически менее эффективная реализация второй стратегии.
Я так понял, речь шла о примере с множественными результатами функции. ok — это признак успешной записи, count — количество записанных байт, err — ошибка. Суть в том, что возможность вернуть множество результатов позволяет совершенно по другому композировать функции (а конкретно не плодить их), но не является священной пулей. И многие оценили по достоинству такую возможность.
«Кодогенерация в Go — это фактически менее эффективная реализация второй стратегии. „
Основной принцип go: “явное лучше неявного», здесь все то же самое, вместо кучи сложных настроек компилятора (по сути изучение еще одного языка) — примитивный подход, и это оправдывает себя.
Разве отсутствие ошибок не является признаком успешности записи?

Функции со множеством возвращаемых значений как раз сложнее композировать. Например, с кортежем я могу написать так:

writeln( getStat().total )

Вместо такого:

_ , _ , total := getStat()
fmt.Println(total)

Более того, я могу вернуть структуру с ленивыми полями, так что не нужные мне данные даже не будут вычислены.

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

Возьмём, например, кодогенератор stringer. Это более 600 замысловатых строчек, которые по такому определению:

type Pill int

const (
    Placebo Pill = iota
    Aspirin
    Ibuprofen
    Paracetamol
    Acetaminophen = Paracetamol
)

Генерируют следующую реализацию интерфейса Stringer:

const _Pill_name = "PlaceboAspirinIbuprofenParacetamol"

var _Pill_index = [...]uint8{0, 7, 14, 23, 34}

func (i Pill) String() string {
    if i < 0 || i+1 >= Pill(len(_Pill_index)) {
        return fmt.Sprintf("Pill(%d)", i)
    }
    return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]
}

В D мало того, что все энумы и так умеют выдавать своё текстовое представление, так и добавить чего-то своего во все энумы не составляет труда:

string toPrettyString( Value )( Value value ) if( is( Value == enum ) )
{
    return Value.stringof ~ ":" ~ value.to!string;
}

Тут мы в трёх строчках добавили всем значениям всех энумов метод toPrettyString, который возвращает строку вида "Pill:Paracetamol".
«Разве отсутствие ошибок не является признаком успешности записи?»
Ну вот, а кто-то не хотел тему ошибок поднимать) Другой пример: x, err:= getA() и возвращаемое значение, в x — пустая структура, а в err — ошибки. Кому нужно — игнорирует ошибки, а кого-то не устроит значение по умолчанию.
«Функции со множеством возвращаемых значений как раз сложнее композировать. Например, с кортежем я могу написать так:»
Не увидел сложности композиции, увидел разное количество кода, причем в примере с go у вас явно игнорируются ошибки (_), т.е. код не равнозначный. И никто не мешает в go точно так же вернуть структуру с полем Total.
«Все остальные языки вводят высокоуровневые абстракции, которые инкапсулируют в себе некоторые паттерны, и неявно для прикладного программиста разворачиваются компилятором/рантаймом в машинный код.»
Все верно, и чем больше таких абстракций, тем сложнее масштабировать код — выше порог входа, меньше программистов, больше число сочетаний возможных решений. Это хорошо для небольших команд, где можно собраться в одной комнате и объяснить друг другу сложнейшие архитектуры, но плохо для больших. Поэтому, никто и не призывает использовать go всегда и везде, зато, в определенный момент времени, он становится очень удобен.

К чему был пример не понял.
"в примере с go у вас явно игнорируются ошибки (_)"

А с чего вы взяли, что там ошибки? :-)

"Другой пример: x, err:= getA() и возвращаемое значение, в x — пустая структура, а в err — ошибки. Кому нужно — игнорирует ошибки, а кого-то не устроит значение по умолчанию."

Как я уже сказал, в D используются исключения, так что этот пример показывает лишь полезность возврата множественных значений в Go, но не в других языках. И то, что вы приняли игнорируемое значение за объект ошибки, говорит о том, что других полезный применений множественным возвращаемым значениям нет.

" чем больше таких абстракций, тем сложнее масштабировать код — выше порог входа, меньше программистов, больше число сочетаний возможных решений."

Как раз таки наоборот. JS — весьма абстрактный относительно железа язык, но какой бешенной популярностью он пользуется. А Python, Ruby, PHP в конце концов.

Пример был к ущербрости кодогенерации перед лицом метапрограммирования.
«А с чего вы взяли, что там ошибки? :-)»
Я все еще думал, мы рассматриваем мой пример. Да и суть не изменилось, в примере с go явно игнорируются два результата функции, т.е. примеры не эквивалентны.
«И то, что вы приняли игнорируемое значение за объект ошибки, говорит о том, что других полезный применений множественным возвращаемым значениям нет.»
С учетом того, что вы проигнорировали мой пример со множественными значениями, а взяли другой, показывающий возможность возврата одновременно и ошибки и значения — то да. Суть в том, что результатов не обязательно должно быть два и не обязательно одним из результатов является ошибка. В целом, в go точно так же, как и в сильно-функциональных языках удобно работать с I/O на границах, а внутри оперировать чистой моделью без error.
Исключения в go тоже есть, там где и должны быть, не для описания логики, а для критических (в пределах данного пакета) ситуаций, когда пакет просто не знает, что с этим делать.

«Пример был к ущербрости кодогенерации перед лицом метапрограммирования.»
Я увидел пример, но не увидел аргументов. Я уже написал, что чем больше можно «добавить» такого, что потом придется гадать всем миром, тем хуже. И да, документация — это тяжелый труд, который тоже нужно автоматизировать.
ok означает признак успеха операции, true/false. Вторая переменная — результат или ошибка. В Гоу любая IO-операция возвращает пару ok, result_or_error.
Кодогенерация, кстати, в D простая и изящная. И с проверкой при компиляции.
Макросы, скажем, clojure еще круче. Макросы Elixir весьма неплохи, но это все же недо-лисп.
Всё же не очень понятно чем не угодили массивы и структуры — специально предназначенные для группировки сущности.

Вот именно, что для группировки. Кто сказал, что возвращаемые данные необходимо группировать? Сначала группируем, потом разбираем, лишние телодвижения, которые к тому же ухудшают читаемость и докумментируемость кода.
Если провести аналогию, то зачем нам множество входящих параметров в функцию, ведь есть массивы и структуры?
Проиллюстрирую кодом на JS:

function makeElement({ tagName , textContent }) {
    var el = document.createElement( tagName )
    el.textContent = textContent
    return el
}

function log( v ) {
    console.log( v )
    return v
}

var { html : outerHTML , ns : nameSpace } = log( makeElement({ tagName : 'div' , textContent : date }) )
document.body.innerHTML = ns + '<br/>' + html

Тут принимается и возвращается некоторая структура, но синтаксический сахар позволяет довольно удобно с этим работать.
Без типов все выглядит красиво) Но даже в таком случае, это читаемее:

html, ns := log(makeElement("div", date))

А как это выглядело бы в D?
А вот так уже не читаемее:

html, ns, attrs, childNodes, id, offset, size, scrollPos := log(makeElement("div",date,"datepicker",null,null,true,0,document.body))
Ну и что мешает в таком случае вернуть массив или структуру? Каждый инструмент для своего случая. Не обязательно во всем выходить на крайности.
В том-то и дело, что обычно хватает 2 параметров и 2 возвращаемых значений, только в разных местах разных параметров и разных возвращаемых значений.

html, ns := log(makeElement("div", date))
parentNode, previousSibling := log(makeElement("lalala", "div", "datepicker"))
Сначала группируем, потом разбираем, лишние телодвижения, которые к тому же ухудшают читаемость и докумментируемость кода.
Ну если в языке есть сахар для кортежей, то результат выглядит вполне прилично:

fn test() -> (i32, f64) {… }
let (a, b) = test();
К тому же, разбиение возвращаемого значения функции на отдельные результат/ошибка не сказать чтобы так уж красиво. Использование ATD выглядит логичнее.
Для возврата несколько значений Александреску советует использовать Tuple из std.typecons.
Хорошо у него с производительностью. Сравнивал простые HTTP сервер и клиент с Go, получилось примерно одинакого. Причём масштабируются и Go и D одинакого хорошо. С учётом обработки и всяких парсингов JSON D оказывается быстрее. Есть мнение, что самый быстрый JSON как раз написан на D. Бенчмарк конечно не совсем честный, но точно претендент на лидерство.
В бенчмарке по вашей ссылке vibe.d есть, правда работал в один поток (была взята старая версия фреймвёрка с досадным багом). PR с нормальной многопоточной версией был отправлен вовремя, но почему-то его не приняли. Ждём следующего запуска, чтобы увидеть правильные результаты.
Теперь понятно, почему такая производительность. Я уже начал думать, что у D совсем все плохо.
Было бы интересно увидеть JSON в этом же бенчмарке, тогда D должен выбиться в лидеры в "JSON serialization".
Ждем 13 раунда, там как раз fasthttp с prefork будет.
Спасибо за ответ
Емнип, там ещё и сборка была дебажная, а не релизная, ибо в релизе почему-то не собиралось.Может уже и пофиксили, конечно.
Сначала все бежали на noSQL, потом назад. Потом все бежали на Go, теперь назад. Как хорошо что я ленивый и никуда не бегал…
Так если подумать перед тем как бежать, оно будет сразу видно с чем вернутся.
Очень люблю трололо каменты. Никто никуда не бегал, многие следуют одному правилу хорошему «всему свой инструмент», поэтому когда нужно тогда и бегают куда нужно. В вы дальше используйте то что знаете и не изучайте ничего нового, это очень профессионально в наше время!
> Очень люблю трололо каменты.

Анналогично

> поэтому когда нужно тогда и бегают куда нужно

Поздравляю, Вы доросли до моего уровня.

> В вы дальше используйте то что знаете и не изучайте ничего нового

А вот сейчас обидно было. =(

Все бегали в nosql, затем к go. теперь все знают когда и как ими пользоваться, и только те кто не бегал будут в любых случаях пользоваться тем что знал раньше. Так что ничего обидного, я не говорил что вы что-то плохо знаете. Был я в одной фирме которая все задачи решала только одним способом, зачем бегать куда-то, если у нас уже есть одно решение.
Жаль только, что до графовых субд мало кто добежал.
Это узконаправленная ниша, там своих людей и реализаций хватает,
Как раз наоборот. Графы — наиболее естественное представление для большинства доменных моделей. А вот SQL вечно не хватает и приходится изобретать велосипеды в виде ORM, NestedSets и тп.
Я работаю в вебе, и поэтому говорю от имени веба =) Для большинства задач нужна банально таблица данных, и с этим идеально справляется SQL и реляционные базы данных, остальное зависит от непосредственных задач.
Это плохая статья, очень глупо выбирать задачи для которых таблицы не нужны, и говорить что реляционные бд — отстой. Выше я писал что для каждых нужд свои задачи, для дерева nosql отлично подходит, для таблиц — реляционные бд. Есть еще куча задач для которых эти обе бд не подойдут вообще.
Именно таблиц в предметной области нет практически никогда, кроме очень специфических областей. Каждый раз, когда выводится таблица, необходимо приджойнивать к ней справочники, для вывода пользователю осмысленного текста, а не набор идентификаторов. При этом джойны многократно увеличивают объём выдаваемых данных из-за дублирования.
Давайте не сыпать мантрами типа "каждой задаче свой инструмент", "ваша статья плохая, негодная", "да ваши графы никому не нужны". Приведите конкретный пример распространённой предметной области, где таблицы справляются лучше графов. Не "мне лично хватает и таблиц", а именно "графы объективно будут лишними".
Я ещ ераз повторю, вы выбрали условие при котором таблицы не работают (дерево, друзья итд). А если мне просто нужен список пользователей? Тут таблицы идеально подходят. Если у меня нет на проекте ни друзей ни дерева категорий? У меня есть список документов, у меня есть список пользователей. У меня списки. Для такой ситуации таблица — идеальный выбор.
Ок, вам надо показать список пользователей. Для каждого необходимо показать список его интересов. Внезапно плоский список превращается в иерархический.
Вы снова подставляете несвойственные для таблицы задание. Я указал где нужны таблицы а не говорил что они везде нужны. Мне часто нужен просто список какой-то сущности, особенно в админке. Причем очень часто. Точно так же часто как и иерархические данные. Это разные структуры и не стоит лепить их друг на друга, они отвечают разным требованиям, и где-то применимы одни, где-то другие. Я не прошу у вас варианты чтоб завалить табличные типы данных.
Ок, пусть будет админка. Вам нужно вывести список товаров. Для каждого товара необходимо вывести название, код, цену и селект с выбором поставщика. Для каждого товара, разумеется, свой список поставщиков.
> только те кто не бегал

Вы ошибаетесь в том, что не считаете обучением наблюдение за другими. А ведь для этого даже поговорка есть: «Дурак учится на своих ошибках. Умный на чужих.». Так что урок выучен заочно. Без бесонных ночей и копании что там не так. Причем что не так разложена по косточкам здесь же, на хабре.

Естественно я изучаю то, что считаю нужным. Суть вопроса в том, что много народу бегает за мейнстримом. Они все думают что им дадут серебрянную пулю.
Не помню спрашивал у вас или нет, какие преимущества у D перед Nim, Crystal, Julia?
К сожалению, не работал с ними. От слова совсем. Так что ничего сказать не могу.
Забавно, никогда не подумал бы что Go и D похожи.
D меня сразу зацепил с первого взгляда, а Go смотрел пару лет назад и совершенно он у меня не пошел. А здесь смотришь — код один в один.
Это свойство всех C-like языков — базовый синтаксис у всех примерно одинаков. :-)
Разве что только препроцессора не хватает. Магию #ifdef'ов заменили на магию static if, статические конструкторы/деструкторы и т.п.
Магия #ifdef — причина очень медленной сборки C++ проектов и трудноуловимых багов, так что молодцы, что заменили. Какой именно функциональности препроцессора вам не хватает?
Нет, тут, скорее, разрыв шаблона «язык для разработки + язык для языка». Поначалу немного непривычно.
Немного имхо по сравнению языков:

Указание названия функции вместе с названием пакета гораздо более читабельнее, сразу понятно откуда функция. Меня бесит ситуация, когда вверху куча импортов, а внизу какой-то набор функций, и что к чему не понятно. Я думаю это наиболее актуально для изучаемых языков. Так что тут я на стороне Go.

Определение функции всегда должно быть четким и однозначным с указанием всех входящих и возвращаемых типов. Модификатору auto тут не место. При вызове функции — пожалуйста.

Неявного приведения типов в Go нет, потому что это источник сложно находимых ошибок, и это тоже правильно.
А вообще, мне больше Rust нравится :)
Вы всегда можете воспользоваться средствами IDE, чтобы уточнить откуда взялась та или иная функция.

Почему это оно должно? Если возвращаемое значение зависит от входных параметров, то вы не можете заранее указать тип возвращаемого значения — оно определяется в месте вызова функции, а не в месте её определения. Характерный пример — функция add из начала статьи.

Если приведение из uint в int, то да, источник ошибок, но если это приведение из short в int, то никаких ошибок тут быть не может.
Не согласен, если вам необходим или хотя бы всерьез нужен IDE чтобы разобраться с кодом, вы пишете уже не на языке а на некоей комбинации язык+IDE. На мой взгляд такой подход — безусловное зло.
То же самое с возвращением auto из функции — да, без него в D никак, но читаемость кода это безусловно ухудшает. Если документация необходима чтобы разобраться с интерфейсом — она становится частью кода, со всеми вытекающими.
Однако в D с этим начинает вырисовываться любопытная концепция — при правильном проектировании вам часто просто не нужно знать детали возвращаемого типа, достаточно знать что это например range, или нечто с именованными полями .x и .y известного типа. Подход совершенно для меня новый и открывает очень интересные варианты, время покажет насколько это изменит стиль программирования.
Как мне в Go по голому коду получить простой список методов с сигнатурами, не выискивая их по всему файлу вперемешу с реализациями? В стародавние времена эта задача решалась вручную написанием заголовочных файлов. Сейчас так уже никто не делает. Не вижу никакого смысла держаться за компромисы прошлого и не использовать современные инструменты. IDE мне необходима, как минимум, чтобы быстро переходить от места использования к месту определения.

Похожая ситуация — CheckedExceptions в Java. Вместо того, чтобы допилить IDE, чтобы она анализируя исходники могла в любом месте выдавать список возможных в этом месте исключений, авторы языка ввели ручное указание в сигнатуре функции всех исключений, которые может бросить как она сама, так и любая косвенно вызываемая ею функция. Ни к чему хорошему это не привело — кода стало существенно больше, поддерживать его стало сложнее, а заставить программиста думать над тем, что не имеет для него значения, так и не получилось.

Ну, утиной типизации сто лет в обед :-)
Как мне в Go по голому коду получить простой список методов с сигнатурами, не выискивая их по всему файлу вперемешу с реализациями?

Просто к слову. Я бы почти уверен, что для этих целей есть специальная консольная утилита, но не смог найти её. Учитывая, что есть "стандартная" консольная утилита для переименования методов, утилиту для отображения списка методов соорудить на её основе не составит труда.
О том и речь, только удобнее, когда такие утилиты встроены в IDE.
В мире Go людей учат документировать код, чтобы godoc все эти функции вам сразу показал. Плюс документировать так, чтобы grep или какой другой инструмент поиска запросто нашел нужную функцию. Ну и синтаксис языка помогает — func всегда будет первым слово. Люди в команде Go вон в виме без подсветки синтаксиса рантайм пишут спокойно. Поэтому и Go делали таким, чтобы он работал без огромной IDE. Что только дает плюсы в конечном итоге. IDE действительно работает как бонус, а не необходимость.
Для тех, кому лень читать статью:
D совсем как Go, но всё, чего нет в D — всего лишь сахар, он ненужен. Все, чего нет в Go — добавляет выразительности, читаемости и удобства в D, без этого никак. D рулит.
Само собой, такие статьи пишутся с большой долей восхищения. Было-бы интересно почитать симметричный ответ от разработчиков на Go.
Симметричный ответ от маркетологов Go? :)

Тут не может быть нехоливарного ответа, но динамика развития и внедрения Go говорит в его пользу.

Go действительно хорош, как сказал один человек на митапе: "как language энтузиаст я не люблю Go, но как CTO я в восторге". Вот у многих такие чувства.

Go сильно ломает шаблоны, особенно тем кто привык к широким возможностям ООП, и поэтому вызвает отторжение.
Мне Go напоминает эдакий коммерческий продукт. Он создан для решения конкретных бизнес задач. Он не создан для статей, которые восхищаются конструкциям и абстракциями, которые можно написать просто потому что их можно написать. Вот D сейчас пиарят именно в этом ключе — смотрите сколько синтаксических конструкций, сколько всего красивого можно написать. А как Go пиарят — берется конкретный кейс, конкретная реальная задача и просто решается средствами языка. И Rust тоже больше в эту сторону тяготеет, потому что и мозила его создает не для того, чтобы language-гики могли восторгаться, а потому что есть конкретная проблема и ее нужно как-то решить, в чем может помочь новый язык.
D при близком знакомстве вызывает что-то вроде восхищения (не у всех разумеется), поэтому люди в таком тоне и пишут.
Я всю жизнь писал на C++, иногда, для быстрых набросков и в характерных случаях, там где надо что-нибудь быстренько распарсить и т.д., добавлял Perl. Сейчас я пересматриваю несколько своих домашних проектов и осознаю что обе части — и C++ и Perl, могут быть целиком переписаны на D с той же практически скоростью и эффективностью.
Почему же нету самого интересного?

package main

import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y)
}
Это будет в главе по сопрограммы :-)
void mySum(int[] r, Task tid) {
    int result = r.sum;
    tid.send(result);
}

void example() {
    auto s = [7, 2, 8, -9, 4, 0];

    auto c = Task.getThis;
    runTask(toDelegate(&mySum), s[0..$/2], c);
    runTask(toDelegate(&mySum), s[$/2..$], c);
    int x = receiveOnly!int();
    int y = receiveOnly!int();

    logInfo("%d %d = %d", x, y, x+y);
}

Проект, который можно запустить, здесь.
Это почти полный эквивалент. По крайней мере делает ровно то же самое, и я постарался сделать код максимально похожим, чтобы прослеживались параллели.
Основное отличие — отсутствие канала. Вместо этого посылается сообщение. Полной аналогии типизированных каналов в vibe.d нет, есть две альтернативы:

  • Сообщения. Буферизированые в очереди, типизированные, но посылаются в сопрограмму, а не в отдельный объект. Минимальная шаблонная обёртка и это будет полная аналогия каналов
  • TaskPipe. Ведёт себя как канал, можно буфферизировать, можно не буфферизировать, но предназначен только для данных. То есть только ubyte[]

Цикл суммирования я тоже убрал, это слишком много бессмысленного кода. Благодаря нормальным шаблонам в D есть нормальные обобщённые алгоритмы. Их не надо писать каждый раз заново, при этом они не теряют в производительности. Это как раз та область, где D нет равных. Go тут тоже нет равных, но в обратном смысле — этой фичи нет вообще и без нормальных шаблонов быть не может.
runTask(toDelegate(&mySum), s[0..$/2], c);

Лучше заменить на:

runWorkerTask(&mySum, s[0..$/2], c);

Но конкретно в данном случае проще использовать async.
Вы его не убрали, а вынесли в отдельную функцию.
Если быть точнее, то в метод. Синтаксически получается почти то же самое, вызов метода sum на массиве.
Однако, верно то, что для других типов — float, int64 и т.д. придётся копировать код или городить костыли из рефлексии или типа interface.
Самое интересное это "select" и то, как в Go устроена кооперативная многозадачность.
UFO just landed and posted this here
Тут на D реализуются идиомы Go. Если попытаться перенести идиомы D на Go, то всё будет куда хуже.
Всё прочитал и мне Go кажется более логичным. Наверное, примеры слишком простые или go-оптимизированные.

Но основное в новом языке — это то, что не знаешь как на нём сделать привычные вещи и ищешь на StackOverflow.
А там Go почти в 7 раз популярнее, чем D (хотя в 60 раз менее популярный, чем PHP и в 70 раз — чем Java).
не знаешь как на нём сделать привычные вещи и ищешь на StackOverflow
А документация и книги нынче не в тренде?

А там Go почти в 7 раз популярнее, чем D (хотя в 60 раз менее популярный, чем PHP и в 70 раз — чем Java).
Про D, внезапно, обсуждения ведут на forum.dlang.org
А документация и книги нынче не в тренде?

по книжкам та же тенденция, поищите книги по D и по PHP/Java на Amazon — каких больше?
я молчу о том, что не все программисты не знают свободно английский, а на ozon книжек о D на русском нет. я то пойму, а вот как те, кто работает со мной?
поэтому по документации и книгам — это такой же минус D как и сообщество.
плюсы наверняка есть, но это не они.
Чтобы разобраться в предмете достаточно и одной хорошей книги. Например, есть замечательная книга "Язык программирования D" от Андрея Александреску. Угадайте на каком языке :-)

Я не в восторге ни от того, ни от другого, но вот мои мысли:
AliasSeq! очень костыльно смотрится. Ну и жутко бесят отступы от скобочек при перечислении параметров. Зачем они?
Go создал сильно больше Buzz-а, чем D, также, я не знаю ни одного проекта на D.
AliasSeq! очень костыльно смотрится.

В D вообще не принято возвращать несколько параметров.

Ну и жутко бесят отступы от скобочек при перечислении параметров. Зачем они?

Вы совсем не на том акцентируете своё внимание.

Go создал сильно больше Buzz-а, чем D

А вы попсу слушаете или что-то другое?

я не знаю ни одного проекта на D

http://wiki.dlang.org/Current_D_Use
UFO just landed and posted this here
А что для вас удобство деплоя?
Всё те же use case, только без копипасты.
Все продолжаете про копипасту сочинять?
Фантазер. Я уже написал, что ваши хеловорлд примеры мало годятся, разве что как доказать правоту в споре. В реальном коде минимально количество кейсов, когда эти примеры актуальны. Поэтому и дженерики я не знаю когда последний раз использовал там, где без них вот прям нельзя было бы. Везде одни только структуры данных, которые в Go и так любой тип могут содержать. А их сортировкой займется куда лучше меня база данных или еще кто.
UFO just landed and posted this here
D — противоположность Go для больших проектов. Я бы даже сказал, что он заточен под большие проекты. Множество привычных фич, которых нет в Go, в других языках были придуманы для масштабирования проекта (ООП, шаблоны, compile time, возможность своих DSL), и D поддержал и развил эти идеи. Из-за этого он выглядит нагруженным, но зато поддерживает кучу разных подходов и парадигм (от процедурной до ООП и функциональной).
По первому вопросу всё непросто. D гораздо привычнее для программистов с других языков, но при этом он существенно сложнее Go. Для С++ программистов D покажется простым, и среднего программиста можно сажать писать код почти сразу.Для Java программистов — очень знакомым, но с кучей новых вещей.
С какого бы языка человек не пришёл он найдёт в D знакомые концепции, но это бывает минусом. Всё же проект должен быть однородным и все должны писать одинаково. И вот для обучения D стилю времени уйдёт точно больше чем в случае с Go.
Не поверите, но Go именно для больших проектов и команд и был создан. Google немаленькая таки компания и их желание переписывать с С++ на Go свои проекты продиктовано именно этими целями — быстрый вход среднему разработчику и простота поддержки и разработки больших проектов. В этом он и снискал успех, который пока не достижим никому другому из волны новомодных языков, в которой Go обычно соседствуют с D и Rust. И если у последнего все кажется будет хорошо, то с первым все так же мутно все.
Не менее быстро.
Не менее удобно.

D имеет не плохие механизмы повышения выразительности кода. Не такие крутые, как у Lisp, но тем не менее. Кроме того он предоставляет различные гарантии, что особенно важно в крупных проектах. Например, вы не можете случайно расшарить изменяемое состояние между потоками.
Голословные утверждения, прикольно. На D нет толком ниодного нормально примера работающего приложения, а вы уже решили на вопросы отвечать. Go имеет уже кучу успешных внедрений именно в плане того, чтобы было быстро и удобно. Этим он, отчасти, и завоевал популярность, до которой D пока что очень и очень далеко. Когда дорастет, тогда может актуальны станут эти вопросы.
add(42, 13) в go можно сделать чуть более наглядным с помощью "ручного жонглирования типами"

type MyInt int
func main() {
var x MyInt = 42;
fmt.Println(x.add(13))
}
func (x MyInt) add(i int) int {
return int(x) + i;
}

Редактор комментов ужасен
код:

type MyInt int
func main() {
    var x MyInt = 42;
    fmt.Println(x.add(13))
}
func (x MyInt) add(i int) int {
    return int(x) + i;
}
Sign up to leave a comment.

Articles