Pull to refresh

Comments 140

Какае-то хрень, говорить что он похож синтаксисом на C. Какая разница на что он похож синтаксисом?
Либо ты поддерживаешь синтаксис С, и таким образом тонны С-кода компилируются и исполняются как надо. Либо ты говоришь что я убрал эти С-родовые травмы, и теперь мой язык D-верх совершенства.
А то что, что-то на что-то другое похоже. Да какая вообще разница? эти синтаксисы учаться за 2-4 недели, а дальше ты лобаешь код с той же скоростью. Или ты нанимаешь 100500 девелоперов betterC-режима, и они тебе лабают код с той же скоростью.
Лучше описали бы, плюсы и минусы этого синтаксиса.
И да, я не увидел паттерн-матчинг. Так сразу вопрос, доколе? :)

Чем проще синтаксис, тем меньше шансов сделать ошибку. Да и вообще код проще понимается.

Ответ неверен. Синтаксис асемблера проще некуда, вот только ошибки там не отлавливаются в прицнипе.
Язык это такая клетка для программиста.
С одной стороны он должен позволять выразить максимум.
С другой стороны он должег позволять выразить это одним единственным способом.
Требования противоречивы.

Про паттерн матчинг мой вопрос был в том, что это очень полезная штука которая из коробки должна быть в языке. Когда её нет, то твой код вместо линейного становится разветвленным, а это очень такая хорошая причина для всяких ошибок.

Вы сами себе противоречите.

Ответ неверен. Синтаксис асемблера проще некуда, вот только ошибки там не отлавливаются в прицнипе.

Отлавливаются и еще как! Посмотрите например как исчезают ошибки в FASM, когда их находят. Буквально за 2..3 часа. В моих проектов на ассемблере, тоже никогда не были проблемы с отловом ошибок.

И где тут время отлова ошибки?

В 22:32 Томаш написал "попробуйте то то, чтобы мне легче было", то есть он в тот момент не знает как исправить бага, а в 22:37 пишет:


Anyway, I gave it a shot. Please try with 1.71.63 and see if the produced files are correct.

Тоесть он в эти 5 минут, открыл сорс, исправил ошибку, перекомпилировал все и загрузил на сайт новую исправленную версию. Чем не отлов ошибки?

Это исправление известного бага. Сколько времени прошло с момента зарождения ошибки до её обнаружения неизвестно, и уж сильно вряд ли 5 минут. Исходная реплика была о том, что отсутствуют инструменты для превентивного поиска ошибок.

И какие такие "превентивные инструменты" есть в других языках. Дайте пример, а то совершенно непонятно. По моему баги всегда впервые проявляются, а потом исправляются. И никак иначе.


Кстати, перечитал, о "превентивного поиска" не нашел:


Синтаксис асемблера проще некуда, вот только ошибки там не отлавливаются в прицнипе.
И какие такие "превентивные инструменты" есть в других языках

По сравнению с ассемблером в языках типа C/Rust/D есть типизация,
которая на этапе компиляции позволяет предотвратить запись скажем целого числа на место числа с плавающей запятой. Я думаю имелось это ввиду.

Там эти ограничения нужны, потому что, уровень абстракции таков, что все делается одинаково. Например то же присвоение, всегда через оператор "=".
В ассемблере, такую ошибку, кроме как намеренно, совершить нереально. По крайней мере, мне не встречалась никогда.

А у D точно проще синтаксис?
Самый простой синтаксис у Лиспа. И его же постоянно обвиняют в плохой читаемости.
Это потому, что у лиспов, по сути, нет синтаксиса. Достаточно простой и при этом читаемый синтаксис, например, у ML-языков.
Он тривиальный, но я бы не сказала, что его вообще нет.
Он даже не тривиальный, он как бы вырожденный (я поэтому сказал, что «по сути») — это практически готовое AST-дерево, в более-менее человеко-читаемом виде.

При этом, судя по опросу выше не все любят ML-подобный синтаксис Rust'а (правда, ещё с ощутимой примесью угловых скобок в шаблонах).

Справедливости ради, когда-то он был намного более подобным (и более приятным на мой вкус); я как-то уже писал об этом:
[...] раст [для многих] выглядит [всё более] привлекательным именно за счет того, за что его ругают функциональщики — если на первых порах он был скорее больше похож на «ML с плюсами», то сейчас всё чаще говорят о «C++ done the right way». Спорить с этим тяжело, ибо история показывает, что императивно-ориентированный синтаксис — очень сильный буст к популярности. (Тут уместно вспомнить ремарки о чутье Гвидо по поводу того, как должен выглядеть питон.)

Назвать синтаксис Rust ML-подобным это довольно оригинально. Если сравнивать с хаскеллем и С++, он больше всего похож именно на плюсы — двойные двоеточия, фигурные скобочки, угловые скобки шаблонов и т.п. Наличие же let и указания типа после имени отнюдь не делает язык ML-подобным, как по мне.

Сперва он действительно был таким (вообще, до того, как раст стал self-hosted, его компилятор был написан на окамле), но с течением времени стал куда больше похож на C++ чем на ML (собственно, чуть выше я об этом и пишу).
Просто не все наблюдали Rust с момента рождения.
Просто не все наблюдали Rust с момента рождения.

Вот как то не очевидно сравнить поклонников языка с консилиумом педиатров, наблюдающими за языком с роддома и искренне интерсующимися его анализами "на кровь, кал, мочу" и прививками...:)
Интересно — фанаты обидятся или обрадуются?

Как уже написали выше, до 1.0 он был более ml-подобным по синтаксису и erlang-подобным в смысле семантики. Сейчас от вещей, роднящих с ml'ами всякие let, вывод типов, match.


Но меня получившийся результат вполне радует. Правда, я на синтаксис смотрю во вторую очередь. Разве что экстремальные варианты типа pascal/ada/modula-подобности не очень приемлемы: он бы стал слишком громоздким ради нужной семантики. Или, наоборот, apl/j/k-подобность сильно повышает плотность информации до уровня неудобоваримости.

Уже 16 лет мучаются, а он так никому и не нужен.
UFO just landed and posted this here
а альтернативы — или древние сишка с плюсами, в которых чуть ли не каждая изначальная фича успела стать deprecated, или наркоманский руст.
Nim, Crystal, Julia?
UFO just landed and posted this here
UFO just landed and posted this here

Эта вольный перевод статьи D is a Better C, но нужно было хотя бы перевести её полностью, там описаны некоторые важные тонкости.

К слову о важных тонкостях, там "as" а не "is" в заголовке.

Согласен, забавно получилось (когда заметил, уже нельзя было редактировать). Спасибо :)
UFO just landed and posted this here
Вы читаете готовые выводы, даже не пытаясь пройти по их логической цепочке. Возьмите ВВЕДЕНИЕ в язык и прочитайте, там обязательно будут отсылки «чем это лучше Си». Тогда все выкрики с задних рядов «ясамниваюсь» уйдут в никуда.
И да, Ди — это лучшее продолжение Си-подобных языков.
UFO just landed and posted this here

Замена C со сборкой мусора? Не думаю!

Most obviously, the garbage collector is removed, along with the features that depend on the garbage collector. Memory can still be allocated the same way as in C – using malloc() or some custom allocator.


Как то так

Ага, и минус exceptions, strings и большая часть стандартной библиотеки. Хотя libc останется доступна, да.

А в C++ при этом есть RAII и все библиотеки работают и доступны.

Вообще то тут разговор про C и D в режиме betterC.
В чистом C тоже нет RAII.
В будущем ничто не мешает адаптировать часть библиотек под этот режим.
Комментарий Уолтера Брайта:
RAII in D currently has a soft dependency on the GC. A fix for this is in the works, and then RAII will be available for D as better C.
В чистом C тоже нет RAII.
Согласен. Но оно вполне реализуемо, даже в рамках современных компиляторов. Взять для примера GCC: __attribute__((cleanup (...))). Думаю, что в VS, clang что-то подобное тоже может быть. Это не призыв использовать, просто констатация: было бы желание, а реализовать, не ломая язык, вполне возможно.
Нельзя привязывать возможности конкретных компиляторов к стандарту. Выше отписали, что как отвяжут RAII от GC, так он появится для нового режима.

Не не, я немного не про то. Я про то, что даже для C, если комитет вдруг захочет стандартизовать атрибуты, то вполне себе реализуется аналог RAII.


По поводу привязки конкретных возможностей компиляторов. По сути, это поведение может выступать как Proof of Concept при рассмотрении следующего стандарта.

IAR тоже многое не по стандарту дает делать. Я не думаю, что это многое войдет в стандарт.
GCC позволяет такое впихивать за счет своего бэкенда, как и clang.

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


Кстати, вопрос к знающим Ди. А как там теперь с обработкой ошибок, после того как выкинули на мороз исключения? Потому что C-style коды ошибок и libc в голом виде низводят бОльшую часть преимуществ в ноль.

Тут более каверзный вопрос — а как у stdlib с error handling через него?
И есть ли сахар "unpack-or-return"?

Да никак, нужно обёртки писать.

Это крайне печально.
К тому же, я подозреваю, Phobos завязан на исключения. А значит, перевести его на variant-совместимый подход — переписать хорошим куском. Либо руками обёртки над libc. Пока напрашивается вывод — в betterC фактически нет стандартной библиотеки.

Ну, эти обёртки написать — довольно тривиальная задача. Я тут не вижу особой проблемы. Особенно в сравнении с собственно С.

Хм… Хренею я с обоих языков. На одном языке вывод одной единственной строчки текста — 23Кб, на другом — 194 Кб. Да Вы зажрались!!!
Надо будет qBasic с компилятором найти на старых дисках и посмотреть, сколько у него готовая прога займёт.
А потом вспомнить, как это делается на ассемблере. :-)
Интересно чем они собирали. У меня hello world собранный GCC5.4.0 занимает 8 килобайт. Стрипнутый — 6 килобайт. В основном — секции с метаданными и информацией для динамической линковки.

В секциях кода — в основном инициализация/деинициализация libc.
Короче, ничего такого страшного. Можно выкинуть всё и дернуть write() руками. Но зачем?

objdump -x для тех, кому интересно
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 0000001c 0000000000400298 0000000000400298 00000298 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000060 00000000004002b8 00000000004002b8 000002b8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000003d 0000000000400318 0000000000400318 00000318 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 00000008 0000000000400356 0000000000400356 00000356 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 0000000000400360 0000000000400360 00000360 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 00000018 0000000000400380 0000000000400380 00000380 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.plt 00000030 0000000000400398 0000000000400398 00000398 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 0000001a 00000000004003c8 00000000004003c8 000003c8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000030 00000000004003f0 00000000004003f0 000003f0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt.got 00000008 0000000000400420 0000000000400420 00000420 2**3
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 00000192 0000000000400430 0000000000400430 00000430 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 00000009 00000000004005c4 00000000004005c4 000005c4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 00000010 00000000004005d0 00000000004005d0 000005d0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame_hdr 00000034 00000000004005e0 00000000004005e0 000005e0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .eh_frame 000000f4 0000000000400618 0000000000400618 00000618 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .init_array 00000008 0000000000600e10 0000000000600e10 00000e10 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .fini_array 00000008 0000000000600e18 0000000000600e18 00000e18 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .jcr 00000008 0000000000600e20 0000000000600e20 00000e20 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .dynamic 000001d0 0000000000600e28 0000000000600e28 00000e28 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .got 00000008 0000000000600ff8 0000000000600ff8 00000ff8 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .got.plt 00000028 0000000000601000 0000000000601000 00001000 2**3
CONTENTS, ALLOC, LOAD, DATA
24 .data 00000010 0000000000601028 0000000000601028 00001028 2**3
CONTENTS, ALLOC, LOAD, DATA
25 .bss 00000008 0000000000601038 0000000000601038 00001038 2**0
ALLOC
26 .comment 00000034 0000000000000000 0000000000000000 00001038 2**0
CONTENTS, READONLY
SYMBOL TABLE:
no symbols


Похоже компилировали этим. Как эта фича будет реализована в GDC, тогда можно будет честно сверять.
В статье пишется, что оба варианта привета транслируются в одинаковый бинарный код.
Но если отключить новую фичу — получается 194кб. Да, и дело то не в языках, а компиляторах.

В оригинальной статье был ещё небольшой раздельчик, что выкинули. В частности:


  • RAII
  • Полиморфные классы (а значит, весь полиморфизм будет ручками на указателях)

Вопрос — зачем он теперь такой нужен?

Некоторый полиморфизм в D есть и у структур:


import std.stdio;

void sayA( A a ) {
    writeln( "a = " , a );
}

void sayB( B b ) {
    writeln( "b = " , b );
}

struct A {
    int a = 1;
}

struct B {
    A a = A(2);
    alias a this;

    int b = 3;
}

void main(){
    A().sayA; // a = A(1)
    B().sayA; // a = A(2)
    B().sayB; // b = B(A(2), 3)
}

Я вас огорчу, это не полиморфизм. Это, по сути:


struct A {
    int a = 1;
};

struct B: public A {
    B()
    {
        a = 2;
    }

    b = 3;
};

void sayA(A const& a)
{
    std::cout << "a = " << a.a << "\n";
}

void sayB(B const& b)
{
    std::cout
        << "a = " << b.a << "\nb = " << b.b << "\n";
}

int main(int, char**)
{
    sayA(A()); // a = 1
    sayA(B()); // a = 2
    sayB(B()); // a = 2 b = 3
}

Интересно, что же вы такое вкладываете в понятие полиморфизма?

Эмм… оперирование разнородными типами через некий единый интерфейс?


trait WhoAmI {
  fn say(&self);
}

struct Cat;
struct Dog;

impl WhoAmI for Cat {
  fn say(&self) { println!("I'm a cat! I say Meow!"); }
}

impl WhoAmI for Dog {
  fn say(&self) { println!("I'm a dog! I say Woof!"); }
}

fn identify_one<T: WhoAmI>(someone: &T) {
  someone.say();
}

fn main() {
  identify_one(Cat); // cat's phrase
  identify_one(Dog); // dog's phrase
}

То, что вы показали — по сути использование объекта-потомка вместо объекта-предка. Только в Ди реализованное через алиасинг. Вот если бы у вас


    B().sayA; // a = B(A(2), 3)

это был бы полиморфизм

Тогда вы должны просто обожать PHP.

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

Да ладно?
Освежите ваши знания для начала)
  • Ad hoc polymorphism: when a function denotes different and potentially heterogeneous implementations depending on a limited range of individually specified types and combinations. Ad hoc polymorphism is supported in many languages using function overloading.
  • Parametric polymorphism: when code is written without mention of any specific type and thus can be used transparently with any number of new types. In the object-oriented programming community, this is often known as generics or generic programming. In the functional programming community, this is often shortened to polymorphism.
  • Subtyping (also called subtype polymorphism or inclusion polymorphism): when a name denotes instances of many different classes related by some common superclass.[3] In the object-oriented programming community, this is often referred to as simply Inheritance.



Также прошу не забывать, что в PHP есть функционал позволяющий делать нечто вроде перегрузки функций в классах.

Вы, пожалуйста, не выдавайте ваше личное видение полиморфизма, за общепринятое.


Вот если бы у вас

Да пожалуйста:


import std.stdio;

void say( A )( A a ) {
    writeln( a );
}

struct A {
    int a = 1;
}

struct B {
    int b = 2;
}

void main(){
    A().say; // A(1)
    B().say; // B(2)
}
Результаты голосования лучше говорят, чем сама статья.

Просто 90% проголосовавших на JavaScript пишет..))

К примеру я пишу на плюсах.
Переходить на D не планирую. Может быть буду использовать там, где он будет лучшим инструментом.
Синтаксис Rust меня внешне не радует — слишком непривычно. Но при наличии свободного времени надо будет поковырять поближе, может и пойдет.

Непривычность синтаксиса — минорщина, не стоящая большого внимания. Существенные отличия в семантике и системе типов ломают мозг и производят куда больше wtf/s.


Тот же borrow checker и lifetimes для многих является эффективно непреодолимым препятствием, хотя в си и плюсах про lifetimes тоже надо думать и держать/проверять их в голове, что куда более трудоёмко, чем поручить это компилятору.

У меня пока не получилось выделить какое либо время, чтобы углубиться в процесс.
Был момент, когда что то пробовал, но с тех пор Rust уже изменился. После того как он стабилизировался, вроде как легче должно быть… но времени нет. Как будет разгрузка, так и займусь, ибо в столе лежат некоторые концепции, под которые Rust может оказаться самым подходящим инструментом.

Не ради саморекламы (там 300 строчек кода, плюнуть и растереть)
https://github.com/target-san/httpdl/tree/step-by-step
Простейший загрузчик файлов по HTTP, конкретно в той ветке что я указал он строится пошагово, в т.ч. с переходом на "идиоматичную" обработку ошибок. Пример не ахти какой, без асинхронки — но маленький, а значит понять должно быть легко.

Если не секрет, что именно непривычно? Я, например, в основном тоже на плюсах пишу — и Rust мне вполне даже зашёл. Очень многие вещи выглядят как раз знакомо.

Как все выглядит. Как я сказал, мне надо погрузиться, все пощупать, но времени пока нет. За языком слежу с момента анонса.
UFO just landed and posted this here
Получилось куда более читабельно и коротко.

По-моему, почти ничем не отличается.

На hello-world примерах сложно увидеть разницу.

Потому что в оригинальной статье речь идёт про похожесть кода («It looks much the same...»), а не о том, о чем пишет автор перевода.

В оригинале, автор говорит что код похож, а затем отмечает важные нюансы.
Оригинал
It looks much the same, but some things are worthy of note:

  • extern ( C ): means use the C calling convention.
  • D normally puts static data into thread local storage. C sticks them in global storage. __gshared accomplishes that.
  • foreach is a simpler way of doing for loops over known endpoints.
  • flags[] = true; sets all the elements in flags to true in one go.
  • Using const tells the reader that prime never changes once it is initialized.
  • The types of iter, i, prime and k are inferred, preventing inadvertent type coercion errors.
  • The number of elements in flags is given by flags.length, not some independent variable.


And the last item leads to a very important hidden advantage: accesses to the flags array are bounds checked. No more overflow errors! We didn’t have to do anything
in particular to get that, either.


Перевод
Выглядит весьма похоже, но некоторые вещи заслуживают отдельного внимания:

  • extern ( C ): означает использование C-конвенции вызовов.
  • Обычно D помещает статические данные в локальное хранилище потока. C привязывает их к глобальному хранилищу. __gshared позволяет достичь этого.
  • foreach это более простой способ описания циклов на заданном диапазоне значений.
  • flags[] = true; устанавливает все элементы flags в true одним махом.
  • Использование const говорит читателю, что prime не измениться после инициализации.
  • Типы iter, i, prime и k выводятся автоматически, что помогает предотвратить ошибки, связанные с непреднамеренным приведением типов.
  • Количество элементов в flags берётся из flags.length, а не из независимой переменной.


Последний пункт приводит к очень важному скрытому преимуществу: доступ к массиву flags осуществляется с проверкой границ массива. Больше никаких ошибок переполнения! И нам даже не пришлось ничего ради этого делать.


Два последних пункта: автоматический вывод типов, и доступ к массиву с проверкой границ, являются наиболее существенными, на мой взгляд.
З.Ы. перевод может быть не очень точным, делался на скорую руку.
iBolit# dmd -h

-betterC omit generating some runtime information and helper functions

Все таки хотелось бы в посте побольше информации, хотя бы столько сколько приведено в оригинальной статье.
С книгой тоже неясно — это перевод или кто-то сам пишет? Чем она лучше чем существующие 2.5 книги?

Этот язык в нише системного программрования. не взлетит никогда. Просто потому, что он не содержит ничего низкоуровневого из того, что есть в C. Системное программирование опирается на абстрактную ISA-архитектуру с одной стороны и конкретную с другой. Все остальное, включая семантику и управление ресурсами — лишнее. Именно поэтому в язык си не вошло ничего из того, что можно безболезненно для производительности и разумности реализовать в виде библиотек. Единственно, чего не хватает в си, так это битовых операций, типа разных сдвигов и прямого управления из языка регистрами — все вычсистемы имеют регистры и кеши. Все это сегодня работает одинаково везде, но отсутствовало тогда, когда создавался язык. Поэтому в язык не вошло. Жаль, что комитет занимается херней, типа немых индексов, вместо того, чтобы как положено распердолить сдвиги во все четыре стороны и обеспечить широкий и полный доступ к состоянию процессора на уровне языка.
Я не прочь поиемть в языке полнофункциональные аналоги всех мыслимых с точки зрения теории jx/jnx
C — это первая (или не первая?) попытка реализации кроссплатформенности. Почитайте его историю. Завязка на регистры убьёт эту его особенность окончательно. И да, платформы могут очень сильно отличаться. Тот же переход не всегда делается по регистрам.
UFO just landed and posted this here

И тут мы приходим к довольно неплохой интеграции низкоуровневого ассемблера и выскоуровнего D: https://dlang.org/spec/iasm.html


struct Foo   // or class
{
    int a,b,c;
    int bar()
    {
        asm
        {
            mov EBX, this   ;
            mov EAX, b[EBX] ;
        }
    }
}
void main()
{
    Foo f = Foo(0, 2, 0);
    assert(f.bar() == 2);
}

А чем gcc или clang хуже? А немного макромагии и будет работать и с visual studio.

в visualstudio только в x86, в x64 никакого inline asm.

А как там драйвера пишут тогда? Оо

Вынимают весь ассемблер в отдельные функции и (с учетом того, что соглашение о вызове известно) вызывают из потом из кода на С.
Такой подход, кстати, лучше вставок, т.к. и целевые машины, и ассемблеры бывают разные, и потому можно иметь несколько реализаций одной и той же функции в проекте и переключать их через систему сборки, а не через препроцессор, как это пришлось бы делать со вставками.

В D это делают так:


version(A) {
  // ...
}
version(B) {
  // ...
}

Переключать версии можно при сборке, можно в коде.

Я не знаю, как компилятор может быть хуже языка :-)

#define true 1
#define false 0
char flags[sizepl];

Язык C с версии C99 поддерживает тип bool: en.cppreference.com/w/c/types/boolean. const в C тоже есть.
Код на C и на D практически не отличается, что должен показать этот пример — непонятно.
Мне немного непонятно, в статье упоминается про системное программирование, но, например, нет ни слова про рантайм. Всякие SIMD и TLS требуют какой-то поддержки, как это запускать на голом железе? Какие функции нужно подложить компилятору, чтобы это взлетело? Особенно, если железо отличается от чего-то очень широко распротранненого? Я не думаю, что причина, по которой некоторые системные программисты не редко работают с C, заключается в его синтаксисе.
Когда эта фича перетечет в GDC, вопрос сам по себе отпадет.
С чего бы это? Что gcc для таких вещей вдруг не требует поддержки рантайма? Или магическим образом умеет TLS на всех платформах, для которых он может генерировать код?
Это значит, что все поддерживаемые архитектуры получат возможность разработки на этом сабсете. В том числе и кросскомпиляцию. И никто не помешает туда включить все расширения для создания кода под конечную платформу.
Под рантаймом имеются ввиду библиотеки, которые линкуются с вашей программой (и прочие вспомогательные средства), чтобы помогать компиялтору реализовывать фичи языка (такие как TLS или например код, который вызывает main с нужными argc и argv, или код которые вызывает конструкторы и деструкторы глобальных объектов и тд и тп), для которых компилятор не может просто сгенерировать код.

Если для распространненых сочетаний архитектура/ОС вы получаете рантайм вместе с компилятором, то для менее распространенных комбинаций это не так. Никто не помешает вам реализовать эти библиотеки, но если ABI не стабилен, то никто не помешает создателям компилятора сломать ваши библиотеки и весь ваш проект вместе с ним.

Так D генерирует ABI совместимый с C при использовании extern©.

extern вероятно задает конвенцию вызова, а речь не только о ней, а о поддержке рантайма. Например, в статье упоминается лучшая чем у C memory_safety, я полагаю, что это включает среди прочего проверку границ массивов. Что D делает, когда проверка не проходит? Какие функции нужно ему подложить, чтобы это все собралось? Как D представляет массивы в памяти, чтобы можно было настроить передачу данных между D и каким-нибудь firmware?

Чем больше фич язык включает тем больше таких вопросов возникает. В этом смысле сравнительно небогатый на фичи C просто не создавал таких проблем, и поэтому использовать его было довольно просто.

В D по умолчанию структуры выравниваются так же как в C. Массивы хранятся так же единым куском. После строк помещается нулевой байт на случай работы с ними из сишных функций. Выход за границы массива приводит к исключению.

Если массивы хранятся так же как в C, то откуда берется размер массива?
Исключения могут требовать поддержки рантайма и полезно указать в чем она заключается, а не про синтаксис близкий к C рассказывать. И просто конвенция вызова «как в C» мало что дает.
Элементарно, если исключение не поймать, то программа должна по идее завершиться, но как завершать программу зависит от ОС. А что если у вас нет ОС?
Если массивы хранятся так же как в C, то откуда берется размер массива?

Статические массивы имеют фиксированный размер. Динамический массив — это фактически структура из указателя и длинны.


Элементарно, если исключение не поймать, то программа должна по идее завершиться, но как завершать программу зависит от ОС. А что если у вас нет ОС?

Как минимум можно всю программу завернуть в try-catch и завершать как считаете нужным. Я не спец в системном программировании, но подозреваю, что в D так же как и в C дёргается abort: https://dlang.org/library/core/stdc/stdlib/abort.html

Как минимум можно всю программу завернуть в try-catch и завершать как считаете нужным.
И если я так сделаю, компилятор перестанет втыкать вызовы рантайма в код? Или отсутсвие их реализации перестанет приводить к ошибке линковки?
Я не спец в системном программировании, но подозреваю, что в D так же как и в C дёргается abort: dlang.org/library/core/stdc/stdlib/abort.htmll
В C нет исключений, так что не также как в C. В C если вы не вызовете abort явно, то компилятор его за вас вызывать не будет только потому, что вы решили работать с массивами, что полезно, если никакой реализации abort у вас нет.

Мой поинт был в том, что если разговор зашел о системном программировании, то есть гораздо более актуальные вещи для обсуждения чем синтаксис похожий на C. И выбор языка C в этой области вовсе не продиктован его синтаксисом и не синтаксис оставнавливает от перехода на другие языки. Что вы хотите сказать своими комментариями?
seh на некоторых платформах есть и в C, это не стандарт, но это реализуемо.
И? То что это реализуемо как-то гарантирует мне стабильность рантайма? И что создатели компилятора не сломают его в очередном релизе? Это как-то меняет то обстоятельство, что статья ни слова об всем этом не говорит и с чего собственно обсуждение и началось?

А что в C происходит при попытке прочитать данные из нулевого указателя?

Зависит от архитектуры, ОС, отображения памяти и много чего другого. Но строго говоря с точки зрения стандарта, то это undefined behavior.

Но вот чего там не происходит, так это если компилятор не попросить, то он не будет вставлять в код проверки и если эти проверки не проходят вызывать какие-то функции, которые никто не определял. Как это по вашему мнению происходит в D.
Всякие SIMD и TLS требуют какой-то поддержки

А simd-то с какого перепугу требует рантайм? Если, конечно, нет реализации соответствующего intrinsic'а, то будут циклы. Ну так simd-инструкции не везде есть. К сишным libgcc/libimf это относится в той же мере. А если есть sse4/avx/avx2/neon/whatever, то соответствующие simd-инструкции не требуют специфичного рантайма, но только кодогенерации и оптимизаций.

SIMD инструкции могут требовать инициализации соответсвующих расширений процессора. Если вы опираетесь на ОС, то она возможно сделает это за вас (если умеет), а если вы с голым железом работаете, то придется это делать как часть инициализации.

Если компилятор для core фичи генерирует код использующий SIMD (прямо или через intrinsic-и), то это проблема, если это вынесено в библиотеку, использование которой опционально, то проблемы нет.

Так никто и не заставляет компилятор генерировать simd инструкции, если не указаны -march/-mtune и подобные вещи. Если dmd целится в системное программирование и ведёт себя не так — то это странно.


Вспомнить тот же x86_64, до нормальной работы нужно переключиться в защищенный режим, а потом и в long mode. Если компилятор не позволяет сгенерировать бинарник для всех этих режимов, то на мой взгляд он не очень пригоден для системного программирования.


Меня какое-то время назад пытались убедить, что go и js — языки программирования системного уровня.


P. S. Почти каждый раз, когда собираюсь поставить плюс в word-must-not-be-named, оказывается, что уже ставил. Что ж это такое xD

Так никто и не заставляет компилятор генерировать simd инструкции, если не указаны -march/-mtune и подобные вещи. Если dmd целится в системное программирование и ведёт себя не так — то это странно.
Я не знаю, как себя ведет D, то что я знаю это то, что статья начинается с упоминания системного программирования (и про какое-то мифическое отсутствие выбора), а потом говорит про синтаксис и фичи ни слова не сказав про ABI и рантайм нужный для поддержки этих фич, я молчу об их стабильности.

Вспомнить тот же x86_64, до нормальной работы нужно переключиться в защищенный режим, а потом и в long mode.
Если вам хватает Real Mode, то в защищенный режим переключать не надо (но тут у вас могут появиться проблемы при использовании, например, gcc), если вам хватает защищенного режима, то в long mode переключаться необходимости нет. Если компилятор поддерживает нужный вам режим, то он вполне пригоден, даже если он не поддерживает все на свете.

Меня какое-то время назад пытались убедить, что go и js — языки программирования системного уровня.
В зависимости от того, что они подразумевали под системным программированием оба языка могут являться таковыми. Мне не известно про какое-то оффициальное стандартизованное законно зафиксированное понятие системного языка программирования. Даже если в контексте данной статьи ограничиться расплывчатым смыслом «близости к железу», то оба языка можно с определенной долей усилий сделать системными создав минимальный рантайм.
Если вам хватает Real Mode, то в защищенный режим переключать не надо (но тут у вас могут появиться проблемы при использовании, например, gcc), если вам хватает защищенного режима, то в long mode переключаться необходимости нет.

Только если вам не надо на bare metal запуститься и потом использовать long mode) Тогда нужны и все предыдущие. Или, например, на старых arm для использования thumb mode нужно было сначала сделать bx для переключения из arm mode в thumb mode, что требует понимания компилятором обоих режимов.


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

Для некоторых портирование рантайма может оказаться сравнимо с написанием ядра ОС (менеджер памяти, процессов, реализация атомиков, api для файлов, сокетов и т. д. и т. п.).


Если системным называть язык на котором может писаться системное ПО (т. е. не прикладное, не предназначенное для использование пользователем системы), то любой язык, на котором может быть реализован компилятор/транспайлер/вспомогательное ПО для системы, т. е. любой тьюринг-полный окажется системным, что приводит к полной бессмысленности такого определения. Поэтому я склоняюсь к определению системного языка, как такого, на котором может быть реализовано, например, ядро ОС.

Для некоторых портирование рантайма может оказаться сравнимо с написанием ядра ОС (менеджер памяти, процессов, реализация атомиков, api для файлов, сокетов и т. д. и т. п.).
Это очевидно зависит от того, как рантайм определен и от того кто его реализует. Если вы для JS VM определите рантайм из пары функций (например, аллоцировать память и освободить память), то его портирование не должно составить проблем. Отсюда и появился мой вопрос про рантайм.

Поэтому я склоняюсь к определению системного языка, как такого, на котором может быть реализовано, например, ядро ОС.
Я уверен, что на JS и Go можно реализовать ядро ОС. Ваше определение не добавляет ясности, отчасти потому что оно не определяет, что может называться ядром ОС, а что нет. Даже приведенный вами список компонентов ядра вызывает вопросы: известны примеры ядер ОС, которые прекрасно жили/живут без потоков и/или без процессов и/или файловых систем и/или сетевого стека в ядре.
UFO just landed and posted this here

Тогда, вероятно, я не прав. Мне казалось, что icc вполне умеет обращаться с такими вещами.


Как минимум, оптимизация для простых циклов и при использовании cilkplus активно использовала avx/avx2 при их наличии и сваливалась до sse4 при отсутствии. Но сильно подробно не разглядывал.

UFO just landed and posted this here
UFO just landed and posted this here
в Rust уже активно пилят новую реализацию чекера над графом потока управления — он станет позволять намного больше без потери строгости

Где об этом можно почитать?
>а вот это в Rust пока не завезли, и непонятно когда завезут

SIMD как и остальные LLVM интринзики можно использовать на найтли через llvmint, плюс есть настойчивое желание получить SIMD на стабильном Расте, так что, думаю, в 2018 мы увидим работу в данном направлении.
UFO just landed and posted this here
Как много хейтеров D в комментах, которые даже не разобрались в самой теме вопроса, а посмотрели, что D уже 16 лет, и решили ещё раз его похоронить.
За всё это время D проделал очень большой путь и уже не похож на то, что было в начале. Даже за последние несколько лет изменений в лучшую сторону очень много, идёт потихоньку отказ от сборщика мусора.
После недели кодинга на D вы не захотите возвращаться к C++, потому что у D ОЧЕНЬ богатая стандартная библиотека, прекрасные шеблоны, много фишек самого языка(такие как генерация кода в процессе компиляции; constexpr и рядом не стоял), отсутствует UB, размеры всех типов стандартизированы, есть менеджер пакетов и другие удобные вещи, при этом он ближе к C++, чем Rust и Go и вызывает меньший разрыв шаблона при переходе.
Я говорю это вам как тот, кто написал 3D движок и игру полностью на D.
Если у кого-то есть вопросы по самому языку — могу развёрнуто ответить.
отсутствует UB

dlang.org/dlangspec.pdf «undefined behavior» найдено неоднократно.
размеры всех типов стандартизированы

В C++ есть int32_t и остальное из cstdint.
Все найденные способы связаны с cast'ом immutable в mutable, а также сборщиком мусора. Ни первого, ни второго в C++ нет.
Оно-то есть, но не «из коробки», а в виде костылей. Как и большинство остального, унаследованного из C.
прекрасные шеблоны, много фишек самого языка(такие как генерация кода в процессе компиляции; constexpr и рядом не стоял)

Nim имеет столько фишек, что D и рядом не стоял. :)
Можно хоть пару примеров того что в Nim реализовано лучше чем в D?
Я некоторое время экспериментировал с D, сделал пару PR, но затем эти nogc, pure, system, etc. стали просто бесить.

пару примеров того что в Nim реализовано лучше чем в D?

1. Макросы. Вы можете создать свой DSL средствами самого языка. Так, например, в Nim реализованы юниттесты. Или макрос scanf, который на этапе компиляции преобразуется в вызовы соответсвующих функций и поэтому парсинг работает очень быстро.
2. Конвертеры
3. Концепты

Для разработчиков игр/графики, думаю, будет интересно взглянуть на opengl-sandbox. Демо.
Конвертеры

Т.е. если у меня функция принимает bool а я передаю int, то это все будет работать?
Всякие scanf, printf и даже регулярные выражения в D тоже парсятся на этапе компиляции и преобразуются в вызовы. Это как раз сделано за счёт удобной генерации кода в процессе компиляции.

Я бы не сказал, что прям удобной. Всё из перечисленного есть как в Nim, так и в D.
Макросы — mixin() и mixin template, трудно сказать где лучше, они просто разные.
Конвертеры — opCast(), но в D есть ограничение — перегрузки возможны только "изнутри" типа, но нельзя добавить их "снаружи", как в Nim. Хорошо это или плохо — сложно сказать.
Концепты — конструкция if в объявлении шаблонов. Тут D куда мощнее, так как позволяет реализовывать самые дикие условия. Например: "скомпилируется ли код, если использовать такой-то тип в таком-то выражении".
Ну а юниттесты в D являются частью языка, что избавляет от проблем вида "как протестировать приватный метод" и тому подобных.

Аналог borrow checking из Rust через scope pointers (scope T*), scope slices (scope T[]) и scope references (scope ref T)

А что с mutable aliasing? Гонки данных ловит?

Я считаю что D не плох, просто он попал в цикл непопулярности — «люди туда не идут потому что не популярный, не популярный потому что люди туда не идут», со всеми вытекающими — нет работы, нет развитых тулзов (относительно), нет многих либо от комьюнити.
Для таких вещей нужен большой хайп на старте.
«Нравится ли вам синтаксис Rust?» — не согласен с формулировкой опроса. Синтаксис — одна из последних вещей, которая важна в языке, во всяком случае, если говорить о языках системного программирования. Лучше бы просто спросили «Годится ли Rust как замена C?»
Неправильный акцент в статье, зачем появился этот режим компиляции

Кроме оригинала от Брайта еще могу посоветовать почитать раздел Editorial тут

+1 к тому что D лучшая ветка из C-подобных. Хотя у него полно своих проблем, betterC режим — как раз попытка решения одной из
Sign up to leave a comment.

Articles