Comments 140
Либо ты поддерживаешь синтаксис С, и таким образом тонны С-кода компилируются и исполняются как надо. Либо ты говоришь что я убрал эти С-родовые травмы, и теперь мой язык D-верх совершенства.
А то что, что-то на что-то другое похоже. Да какая вообще разница? эти синтаксисы учаться за 2-4 недели, а дальше ты лобаешь код с той же скоростью. Или ты нанимаешь 100500 девелоперов betterC-режима, и они тебе лабают код с той же скоростью.
Лучше описали бы, плюсы и минусы этого синтаксиса.
И да, я не увидел паттерн-матчинг. Так сразу вопрос, доколе? :)
Чем проще синтаксис, тем меньше шансов сделать ошибку. Да и вообще код проще понимается.
Язык это такая клетка для программиста.
С одной стороны он должен позволять выразить максимум.
С другой стороны он должег позволять выразить это одним единственным способом.
Требования противоречивы.
Про паттерн матчинг мой вопрос был в том, что это очень полезная штука которая из коробки должна быть в языке. Когда её нет, то твой код вместо линейного становится разветвленным, а это очень такая хорошая причина для всяких ошибок.
Вы сами себе противоречите.
Ответ неверен. Синтаксис асемблера проще некуда, вот только ошибки там не отлавливаются в прицнипе.
Отлавливаются и еще как! Посмотрите например как исчезают ошибки в FASM, когда их находят. Буквально за 2..3 часа. В моих проектов на ассемблере, тоже никогда не были проблемы с отловом ошибок.
Вот пример: https://board.flatassembler.net/topic.php?t=19985
Баг репорт: 20:41
Автор прочитал репорт: 22:32
Исправленная версия: 22:37
В 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 минут, открыл сорс, исправил ошибку, перекомпилировал все и загрузил на сайт новую исправленную версию. Чем не отлов ошибки?
И какие такие "превентивные инструменты" есть в других языках. Дайте пример, а то совершенно непонятно. По моему баги всегда впервые проявляются, а потом исправляются. И никак иначе.
Кстати, перечитал, о "превентивного поиска" не нашел:
Синтаксис асемблера проще некуда, вот только ошибки там не отлавливаются в прицнипе.
И какие такие "превентивные инструменты" есть в других языках
По сравнению с ассемблером в языках типа C/Rust/D есть типизация,
которая на этапе компиляции позволяет предотвратить запись скажем целого числа на место числа с плавающей запятой. Я думаю имелось это ввиду.
При этом, судя по опросу выше не все любят ML-подобный синтаксис Rust'а (правда, ещё с ощутимой примесью угловых скобок в шаблонах).
[...] раст [для многих] выглядит [всё более] привлекательным именно за счет того, за что его ругают функциональщики — если на первых порах он был скорее больше похож на «ML с плюсами», то сейчас всё чаще говорят о «C++ done the right way». Спорить с этим тяжело, ибо история показывает, что императивно-ориентированный синтаксис — очень сильный буст к популярности. (Тут уместно вспомнить ремарки о чутье Гвидо по поводу того, как должен выглядеть питон.)
Назвать синтаксис Rust ML-подобным это довольно оригинально. Если сравнивать с хаскеллем и С++, он больше всего похож именно на плюсы — двойные двоеточия, фигурные скобочки, угловые скобки шаблонов и т.п. Наличие же let и указания типа после имени отнюдь не делает язык ML-подобным, как по мне.
Как уже написали выше, до 1.0 он был более ml-подобным по синтаксису и erlang-подобным в смысле семантики. Сейчас от вещей, роднящих с ml'ами всякие let
, вывод типов, match
.
Но меня получившийся результат вполне радует. Правда, я на синтаксис смотрю во вторую очередь. Разве что экстремальные варианты типа pascal/ada/modula-подобности не очень приемлемы: он бы стал слишком громоздким ради нужной семантики. Или, наоборот, apl/j/k-подобность сильно повышает плотность информации до уровня неудобоваримости.
Эта вольный перевод статьи D is a Better C, но нужно было хотя бы перевести её полностью, там описаны некоторые важные тонкости.
И да, Ди — это лучшее продолжение Си-подобных языков.
http://dlang.ru/faq
Замена 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 тоже нет 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 что-то подобное тоже может быть. Это не призыв использовать, просто констатация: было бы желание, а реализовать, не ломая язык, вполне возможно.
Не не, я немного не про то. Я про то, что даже для C, если комитет вдруг захочет стандартизовать атрибуты, то вполне себе реализуется аналог RAII.
По поводу привязки конкретных возможностей компиляторов. По сути, это поведение может выступать как Proof of Concept при рассмотрении следующего стандарта.
Меня смущает, что сам по себе язык очень сильно завязан на сборку мусора. Многие вещи могли бы без проблем работать, сидя на стеке или кастомных аренах. Те же классы с наследованием, интерфейсы и т.п. Однако — увы.
Кстати, вопрос к знающим Ди. А как там теперь с обработкой ошибок, после того как выкинули на мороз исключения? Потому что C-style коды ошибок и libc в голом виде низводят бОльшую часть преимуществ в ноль.
Тут более каверзный вопрос — а как у stdlib с error handling через него?
И есть ли сахар "unpack-or-return"?
Да никак, нужно обёртки писать.
Это крайне печально.
К тому же, я подозреваю, Phobos завязан на исключения. А значит, перевести его на variant-совместимый подход — переписать хорошим куском. Либо руками обёртки над libc. Пока напрашивается вывод — в betterC фактически нет стандартной библиотеки.
Надо будет qBasic с компилятором найти на старых дисках и посмотреть, сколько у него готовая прога займёт.
А потом вспомнить, как это делается на ассемблере. :-)
В секциях кода — в основном инициализация/деинициализация libc.
Короче, ничего такого страшного. Можно выкинуть всё и дернуть write() руками. Но зачем?
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
Но если отключить новую фичу — получается 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)
это был бы полиморфизм
Я как раз к языкам со слабой типизацией стараюсь пореже подходить.
По поводу же конца моего комментария — суть в том, что функция не меняет поведения в зависимости от полного входного типа. Поэтому полиморфизмом там как бы и не пахнет.
Освежите ваши знания для начала)
- 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 мне вполне даже зашёл. Очень многие вещи выглядят как раз знакомо.
Получилось куда более читабельно и коротко.
По-моему, почти ничем не отличается.
На 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 книги?
Я не прочь поиемть в языке полнофункциональные аналоги всех мыслимых с точки зрения теории jx/jnx
И тут мы приходим к довольно неплохой интеграции низкоуровневого ассемблера и выскоуровнего 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.
А как там драйвера пишут тогда? Оо
Вынимают весь ассемблер в отдельные функции и (с учетом того, что соглашение о вызове известно) вызывают из потом из кода на С.
Такой подход, кстати, лучше вставок, т.к. и целевые машины, и ассемблеры бывают разные, и потому можно иметь несколько реализаций одной и той же функции в проекте и переключать их через систему сборки, а не через препроцессор, как это пришлось бы делать со вставками.
Я не знаю, как компилятор может быть хуже языка :-)
#define true 1
#define false 0
char flags[sizepl];
Язык C с версии C99 поддерживает тип bool: en.cppreference.com/w/c/types/boolean. const в C тоже есть.
Код на C и на D практически не отличается, что должен показать этот пример — непонятно.
#comment_10384058
Если для распространненых сочетаний архитектура/ОС вы получаете рантайм вместе с компилятором, то для менее распространенных комбинаций это не так. Никто не помешает вам реализовать эти библиотеки, но если ABI не стабилен, то никто не помешает создателям компилятора сломать ваши библиотеки и весь ваш проект вместе с ним.
Так D генерирует ABI совместимый с C при использовании extern©.
Чем больше фич язык включает тем больше таких вопросов возникает. В этом смысле сравнительно небогатый на фичи C просто не создавал таких проблем, и поэтому использовать его было довольно просто.
В D по умолчанию структуры выравниваются так же как в 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 в этой области вовсе не продиктован его синтаксисом и не синтаксис оставнавливает от перехода на другие языки. Что вы хотите сказать своими комментариями?
А что в C происходит при попытке прочитать данные из нулевого указателя?
Но вот чего там не происходит, так это если компилятор не попросить, то он не будет вставлять в код проверки и если эти проверки не проходят вызывать какие-то функции, которые никто не определял. Как это по вашему мнению происходит в D.
Всякие SIMD и TLS требуют какой-то поддержки
А simd-то с какого перепугу требует рантайм? Если, конечно, нет реализации соответствующего intrinsic'а, то будут циклы. Ну так simd-инструкции не везде есть. К сишным libgcc
/libimf
это относится в той же мере. А если есть sse4/avx/avx2/neon/whatever, то соответствующие 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 можно реализовать ядро ОС. Ваше определение не добавляет ясности, отчасти потому что оно не определяет, что может называться ядром ОС, а что нет. Даже приведенный вами список компонентов ядра вызывает вопросы: известны примеры ядер ОС, которые прекрасно жили/живут без потоков и/или без процессов и/или файловых систем и/или сетевого стека в ядре.
За всё это время D проделал очень большой путь и уже не похож на то, что было в начале. Даже за последние несколько лет изменений в лучшую сторону очень много, идёт потихоньку отказ от сборщика мусора.
После недели кодинга на D вы не захотите возвращаться к C++, потому что у D ОЧЕНЬ богатая стандартная библиотека, прекрасные шеблоны, много фишек самого языка(такие как генерация кода в процессе компиляции; constexpr и рядом не стоял), отсутствует UB, размеры всех типов стандартизированы, есть менеджер пакетов и другие удобные вещи, при этом он ближе к C++, чем Rust и Go и вызывает меньший разрыв шаблона при переходе.
Я говорю это вам как тот, кто написал 3D движок и игру полностью на D.
Если у кого-то есть вопросы по самому языку — могу развёрнуто ответить.
отсутствует UB
dlang.org/dlangspec.pdf «undefined behavior» найдено неоднократно.
размеры всех типов стандартизированы
В C++ есть int32_t и остальное из cstdint.
прекрасные шеблоны, много фишек самого языка(такие как генерация кода в процессе компиляции; constexpr и рядом не стоял)
Nim имеет столько фишек, что D и рядом не стоял. :)
пару примеров того что в Nim реализовано лучше чем в D?
1. Макросы. Вы можете создать свой DSL средствами самого языка. Так, например, в Nim реализованы юниттесты. Или макрос scanf, который на этапе компиляции преобразуется в вызовы соответсвующих функций и поэтому парсинг работает очень быстро.
2. Конвертеры
3. Концепты
Для разработчиков игр/графики, думаю, будет интересно взглянуть на opengl-sandbox. Демо.
Конвертеры
Т.е. если у меня функция принимает bool а я передаю int, то это все будет работать?
Я бы не сказал, что прям удобной. Всё из перечисленного есть как в 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? Гонки данных ловит?
Для таких вещей нужен большой хайп на старте.
Лучше чем C