Не осилил Rust и написал свой язык программирования

«Конечно! Вот текст для поста на Пикабу:»

Привет, Пикабу! Данный пост изначально задумывался для Хабра, но я подумал что там мне напихают не глядя и не в панамку, поэтому начну отсюда :)

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

> Преамбула
Прежде чем перейти к сути, накину контекста: я не программист и каких-либо полноценных проектов я никогда не писал. Иронично, но я преподаю программирование для детей и подростков лет 12-14. По серьезному: не "давайте на Scratch сделаем так, чтобы котик двигался влево-вправо", а на Python задачки на алгоритмы, библиотеки, пишем игры на фреймворке Pyxel (ранее сидели на Arcade). PyGame дичь, не используем. В общем чужими руками я написал гораздо больше, чем своими. А еще я чуть чуть дизайнер и тридешник. Несмотря на это всё я 17 лет так или иначе "трогал" кучу языков: C, C++, C#, JS, Lua, PHP, HTML+CSS, даже ASM, так что знаю "как бывает".
Это как жить в семье музыкантов - сам, может, на скрипке играть и не умеешь, но ноты знаешь. Обвинения в профанации, мол "как ты можешь преподавать без знаний" - не принимаются. У нас кружок робототехники, и мы в первую очередь работаем с микроконтроллерами, так что если вы сами не знаете что такое АЦП, как он работает на "железном" уровне, что такое прерывания и конечный автомат, кружок бисероплетения на три блока ниже. Так что база у нас на языке C, а Python для кругозора.

> Теперь к сути:

T: -45d
Сижу, мучаю нейронку на тему концепций, которые так и не смог понять: Лямбды, Декораторы, вот такое. Вроде как суть ясна, но не понятна проблема, решением которой выступают эти инструменты.

Т: -41d
Пришёл к выводу что мне не близки концепции с высокой долей "магии".

T: -33d
Нейронка настоятельно советует попробовать плеяду "современных языков системного программирования": Rust, Go, ZIg, Odin.

T: -31d
Установлен Rust, изучаем базу. Cargo очень нравится, прям кайф. Первую программу свайбкодил, читаю. Очень больно. А это что за бред? М, понятно, но зачем делать это так... Круто что всё так быстро собирается, плохо, что код плохо читается. Обработка возвращаемых ошибок и значений это абзац:

Rust:

match read_file_content("hello.txt") {
Ok(text) => println!("Содержимое: {}", text),
Err(e) => eprintln!("Ошибка: {}", e), }

T: -30d
Пробую писать сам, спотыкаюсь на каждом символе, мало того что куча всяких <> :: => !, семантика страдает, короче пахнет C++, который я сильно недолюбливаю (C люблю). Ну и да, корни у Rust очень сильно растут в C++. Спасибо, я пошёл.

T: -27d
Пробую Zig, Odin, но и там видны рога от C++, который "испортил" С, который родом их 70-х. Да, прикольные модели управления памятью, но чет фу (ну зачем := делать???). Хочется чтобы читалось ненапряжно как Python, но компилировалось, было явно и без сборщика мусора.

Т: -25d
Понимаю, что толком никто не занимался нормально UX языков программирования. Да, вроде как существуют всякие Ruby, но как будто когда заканчивались базовые средства "разметки" кода, в ход шло всё подряд:

:: < > & * # @ !

И я не про оператор НЕ (!) а про макрос из примера выше.

То есть какой то умный дядька (явно умнее меня!), решил, что брать аккорды на клавиатуре по 3 раза за строку это нормально. Ну логика ясна: символов меньше, значит и писать тоже меньше, всё же верно? Верно ведь?

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

Я создам свой язык программирования. Без блэкджека.

T: -24d
Ну начнем с базы, что мы выкинем в первую очередь?
; в конце каждой строки. Этот символ не играет роли, всё равно писать по 3 выражения в строку это моветон.
А вот { } для блоков кода оставим, это база. Уровни вложенности из Python засчет отступов иногда сложно считываются.
Ну и ( ) вокруг условий тоже пожалуй не особо нужны, если кто захочет - пожалуйста, в рамках логического/математического выражения они всё равно валидны.

А вот дальше интереснее.
Если if / else if / else мы не трогаем, всё логично, то вот первым на очереди на рестайлинг у нас идет switch / case . Вот к нему у меня много вопросов. Во первых в C его синтаксис чужероден (или я один это замечаю?):

С Language:

switch (x) {

case 1:

printf("Один");

Простите, с каких пор в C у нас блоки кода идут просто после двоеточия?

Но самая главная проблема - семантике.

Семантика — это смысл, значение языковых единиц (слов, фраз, конструкций), в отличие от их звучания (фонетики), формы (морфологии) или порядка слов (синтаксиса).

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

Case означает "случай" / "футляр". Очевидно, речь подразумевается "в случае".

Однако вместе оно как-то... Ну, не бьется, что ли. Понятно, что все давно привыкли. Но изначально это была надстройка над goto уши от чего торчат до сих пор (кто нибудь когда нибудь видел goto вживую? по мне это как легенда из старых ужастиков). Таким образом по факту case заменяет метку для прыжка. В Rust заменили на match ("сопоставить"). Ну, допустим.

Итак, как нам это заменить? Начнём с наших намерений: мы хотим написать нечто вроде многоступенчатого else if , только лаконично. Как это произнести алгоритмически?

Когда X
равен Y, тогда ...
равен Z, тогда ...

"равен" можно выкинуть, нет смысла писать сравнение на каждой строке. Но стоит иметь некий визуальный маркер. Поэтому использую is ("является"):

when x {
is 1 { ... }
is 2,3 { ... }
else { ... }
}

Мне - нравится. Как продолжение фраз "Пока" и "Если".

Кого дальше потрогаем?
while - оставляем как есть, претензий нет. А вот loop как оператор бесконечного цикла прямая замена while True - тоже забираем из Rust/Zig, это хорошая идея.

А вот for придется "переодеть". Как ни крути, все варианты цикла for , кроме foreach обладают сомнительной семантичностью. Как в Python?

for x in array:

Как это прочитать? Для каждого элемента ИКС в МАССИВЕ (делаем: ...). Короче вроде и норм, но мой вариант:

iterate array as x {...}

Итерировать / Перебирать МАССИВ как ИКС.

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


*вздох*

enum .

Что мы тут нумеруем? Буковки? Мы именно пронумеровать хотим? Или нам всё таки нужны читаемые оболочки над числами для использования в качестве читаемых статусов/состояний?

label - лучше отражает суть происходящего, ИМХО.


Теперь к вещам посущественнее: синтаксис объявления функции.

Rust

fn sum_positive(xs: &[i32]) -> i32 {...}

Вы тоже это видите? к fn нет вопросов - кратко, лаконично. Лучше, чем в С. А вот что дальше пошло не так?

: &[ ] ->

Это всё буквально мусор. Тут только в C++ сделали, как мне кажется, хуже.
-> должен показывать что "возвращаем такой то:", но это буквально нужно рисовать стрелочку.
Про амперсанд и прочее в аргументе пока молчу.

Zig показывает как надо:

Zig

fn sumPositive(xs: []const i32) i32 {...}

Ну ладно, давайте представим что хотим вернуть структуру?
Тогда пишем явно, гордо, твердо и четко: returns . А чтобы не разводить внутри ( ) свалку, воспользуемся технологией древних: просто сделаем как в C: тип, затем имя. Вместо & , если хотим передать значение по ссылке, пишем прямо: direct .

Получаем:

fn sum_positive(i32 List xs) i32 {...}

(в лучшем случае (не пишем returns для простых типов))

fn find_min_max(direct List i32 myarr, bool x) returns struct {i32 min, i32 max} {...}

(в худшем случае (ну почти))

Ой, плаки-плаки, всё слиплось, непонятно. Конечно, мы же на пикабу, а не в IDE, где всё подсвечено. Зато читаемо и без мусора.

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

Давайте к чему то попроще.

i++ / i-- - вообще то это хорошая вещь. Если её запретить использовать внутри других выражений, только как отдельное выражение. Краткая запись i += 1

Как создавать переменные? Когда я только-только изучал C где то в 13 лет, я думал что int в начале строк это что то типа инициализировать (init). Но нет, с этого (указания типа), у нас начинается объявление. В Python не парились: присваивание новому имени? Значит переменная. В других языках где-то let , где то var .

var не имеет семантики. Это огрызок от variable.
let меня вообще меня фрустрирует. Оно с LISP, с 1958 года родом.

new i32 x = 0

Новое? Новое. Вопросы? Конструктор объектов не обеднеет.

Кстати о них. Структуры с методами - мой выбор. Никакого наследования и т.д.
Обращение к полю через my . Хотим спрятать поле - hide . Хотим спрятать функцию - local .

Пространства имен: никогда небыло проблем просто с обращением через точку типа

module.object.method()

Давайте опустим пока управление памятью и многопоточность, потому как там можно совсем завязнуть, но yield , async и await тоже заслуживают камня в огород.

Давайте еще чуток пофантазируем. Допустим, функция потенциально возвращает ошибку. Почему бы не маркировать её сразу как danger ? И другие функции тоже имеют право её вызывать только имея эту метку.

danger fn read_file(Path file) returns Text {...}

Ок, а обработка ошибок?

txt = read_file(file) on error {output("не смогли прочитать файл :( ")}

Ничего страшного, парсер не подавится - on error это единый токен. Можно докрутить в плане явности типа ошибки, но пока так. В ту же калитку тогда почему бы не:

on event // для игровых циклов или embedded, часть многопоточности

on interrupt // сладость для embedded

Кстати, вы задумывались над print( ) ? Это же буквально "печатать". На бумаге. Это легаси со времен, когда терминал был печатной машинкой в натуре.

//Высокоуровневое (ввод/вывод из консоли):
input( )
output( )

//Низкоуровневое (запись, чтение файла/потока):

read( )

write( )

Кратко про типы:


// Базовые (оч понравилось у Rust)
i8, i16, i32, i64
u8, u16, u32, u64
f32, f64
bool
char

// Алиасы
Int // Алиас для i64 (зависит от архитектуры)
Float // Алиас для f64 (зависит от архитектуры)
Byte // Алиас для u8
Word, DWord, QWord // На всякий случай

// Составные и расширенные
[T; N] // Статический массив
List T // Динамический массив
Vec2, Vec3, Vec4 // Нужны примерно везде
String // "Эффективная" строка (просто массив символов)
Text // "Мощная" строка (с методами и т.д.)
Path // Специальный тип для путей
Angle // Специальный тип для углов
Range // Диапазон чисел
struct // Структура
label // Вместо enum

+ литералы для размеров данных и времени: 3kb, 500ms

Да, прямо в ядре. В бинарник это тащить не нужно, если не используется, не вижу проблемы.
Вообще, почему math вместе со всей тригонометрией и даже корнями отдельно? Боитесь засорить namespace? Кстати не вижу никаких проблем в оператора в духе and , or , а значит и для xor , тогда ^ можно законно вернуть возведению в степень. А можно пойти дальше и использовать idiv и mod (вместо %) как inline операторы.

Так и что дальше?

T: -12d
Берём в охапку Codex и вайбкодим транспилятор всего этого добра на C. И сверху менеджер проектов в духе Cargo. Как назовём? Пускай пока будет Skadi.

https://github.com/eoshipnyagov/Skadi-Language/tree/master

T: -3d
Глядя на получившийся проект, пусть в стадии идейного демонстратора, я бы сказал что у меня получился некий учебный язык для gamedev, cli и embedded (удивительно (нет)). Нет, он не станет популярным. Не убьет Rust. Но, возможно, станет маленькой крупинкой взгляда со стороны на устоявшиеся стандарты индустрии.


T: -1d
Зачем это делать, если слон в комнате в виде вайбкодинга дышет в ухо? Ну. Тогда тем более читаемость выходит на первый план "простоте записи". Нейронки хвалят получившийся синтаксис, хотя справедливо указывают на некоторые конфликты с устоями и проблемы однозначности.

Важно, что я не пытался сделать код "псевдоанглийским", я просто немного причесал UX на свой лад.

T: -3m


«Если нужна более строгая формулировка — дайте знать, я перепишу в академическом стиле»

Пример кода на Scadi

Пример кода на Scadi

Лига программистов

2.3K постов12K подписчика

Правила сообщества

- Будьте взаимовежливы, аргументируйте критику

- Приветствуются любые посты по тематике программирования

- Если ваш пост содержит ссылки на внешние ресурсы - он должен быть самодостаточным. Вариации на тему "далее читайте в моей телеге" будут удаляться из сообщества

0
Автор поста оценил этот комментарий

"Мой" пример: после чтения файла идет блок кода который выполнятся при ошибке.

То есть делает то же самое. Но в Rust на уровне языка нет бестолковой синтаксической конструкции on error) Ну серьезно, зачем вам она, если можно без?

что то там разворачивает

Назовите функцию так, как вам нравится. Пусть будет... on_error 😁

с пустым (!) замыканием (!!!)

О, БОЖЕ! ЗАМЫКАНИЕ БЕЗ СТЕЙТА - ЭТО Ж ОБЫЧНАЯ ФУНКЦИЯ, ДА?!?!

и макросом (!!!!!)

Ну макрос и макрос. Поменяйте на обычную функцию - суть не изменится.

так одно и то же, да.

Что и требовалось...

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Ладно, здесь на самом деле моя вина, Rust это больше функциональный язык программирования, а функциональщину я вообще никак не перевариваю. Мне нужен "новый C", а не "новый C++" и здесь Zig ближе. А я просто "повелся на маркетинг"

0
Автор поста оценил этот комментарий

О, ну если очевидные символы, вроде стрелок и амперсандов, заменить английскими словами - сразу заживем!


&[i32]

Ссылка на массив, внутри которого инты. Разве это не прекрасно? Разве это не лаконично?


Zig показывает как надо

Нужно добавить мусорный const? Ок. А этот const относится к массиву или элементам массива?



ЗЫ Найдите 10 отличий:

txt = read_file(file) on error {output("не смогли прочитать файл :( ")}


vs


let txt = read_file(file).unwrap_or_else(|_| eprintln!("не смогли прочитать файл :( ");

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

"Мой" пример: после чтения файла идет блок кода который выполнятся при ошибке.


Пример из rust: от результата работы функции мы вызываем метод, который что то там разворачивает или что-то еще (буквальный перевод) с пустым (!) замыканием (!!!) и макросом (!!!!!)


Ну а так одно и то же, да.

показать ответы
0
Автор поста оценил этот комментарий

Я об этом и написал. Синтаксис можно сделать любой под разные идеи и задачи (сравните Хаскель и Го), если есть хороший компилятор.

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Ну компилятор-то у меня есть (от языка C пока что). А вот синтаксис мне нужен свой, в том числе потому что я в посте не описывал модель памяти, управление многопотоком и еще пару мелких фич, которые разбросаны по разным языкам.

1
Автор поста оценил этот комментарий

Получился у вас гибрид Алгол 68 и Питона.


Смысл языка в компиляторе и получаемом машинном коде. Синтаксис не имеет особого значения.


Поэтому и придумывают новые языки, чтобы дать новую абстракцию, которая может быть удобной для человека. С YACC, можно было делать языки еще 30 лет назад "на коленке".

А вот хороший компилятор это сложная штука. Ради него языки и используют.

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Если бы синтаксис небыл важен, нам хватило бы одного C и всё. Далее всё равно всё приводится к одному и тому же набору инструкций. Но у нас всё таки есть большое количество языков, потому что различаются образы мысли, задачи, инструменты (управления памятью / потоками, как самое насущное).

показать ответы
1
Автор поста оценил этот комментарий

Не плохо


Но может посмотришь на mojo, который по синтаксису как питон, но компилируется. Да, там нет поддержки микроконтроллеров, но думаю через ту же нейронку нагенерить такую поддержку сильно проще, чем делать свой язык

Вопрос с указателями в примере не раскрыт

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Ну, это был мысленный эксперимент. Скорее всего вы правы насчёт mojo. Насчёт указателей - это в целом вопрос управления памятью. Я думаю над аренами + явной передачей владения через соответствующий оператор вроде take. Но это пока так, несущественно.

0
Автор поста оценил этот комментарий

А для чего? Можно же продемонстрировать идею на любом языке из десятков существующих. Тем более если у вас там С, можно заодно и разобраться как любая структура данных устроена.

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Эм. Каким образом вы продемонстрируете идею синтаксиса несуществующего языка на другом языке? (На си?)

показать ответы
3
Автор поста оценил этот комментарий

краткое содержание

Иллюстрация к комментарию
раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Я хотел это вставить)

0
Автор поста оценил этот комментарий

Есть возможность что в тексте я это пропустил. Но, позвольте, какая была цель вайбкодинга языка?

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Демонстрация идеи синтаксиса.

показать ответы
4
Автор поста оценил этот комментарий

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

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

Ну вот вы широким жестом завели List i32 без всякой разметки. Тем самым вы молча создали шаблоны/дженерики. Вот только не продумали как они будут выглядеть в более сложном случае. Ведь если есть List, то есть наверное и Dict / Map, да? И наверное у него 2 параметра-типа, а не один? Так, fn printMap(Map String i32), ну вроде парсится однозначно. А если у нас вместо String в качестве типа ключа другой тип-шаблон, например List? printMap(Map List i32 i32), ой уже не так читаемо хотя пока тоже парсится однозначно, но если ввести вариадик шаблоны, это всё разваливается: printSomething(Tuple Tuple i32 i32 i32)


txt = read_file(file) on error {output("не смогли прочитать файл :( ")}

Ненене, давайте-ка по-честному, решайте такую же задачу какую вы дали Расту - возвращаемая структура есть какая-то пара (ok, значение1) или (error, значение2) и тело функции должно обработать это значение. Потому что аналог на Rust этого вашего read_file это достаточно короткое и понятное

let txt = read_file(file).expect("не смогли прочитать файл :(");

раскрыть ветку (1)
Автор поста оценил этот комментарий

Ну тут вы сами додумали. Нет, дженериков нет. Есть лишь тип динамического массива, которому нужно знать размер ячейки. Map / Dict - нет. Хотите функцию дженерик - пишите явно.


Насчёт обработки ошибок: это не я так придумал, это раст базово возвращает такую структуру как интерфейс обработки ошибок.

1
Автор поста оценил этот комментарий

О каких глубинных вещах идет речь? Вам просто не зашел синтаксис Rust при том, что на нем вы не программировали. Это называется порогом вхождения, он у rust достаточно высокий. Мне, кстати, он тоже не нравится. Если бы поработали на нем год или два, то поняли бы суть всего и уже руки менять не тянулись.

Хоть rust я учил, но программирую на go много, на php и js. Их синтаксис мне вполне нравится (конечно не во всем), а с rust больше никогда связываться не хочу.

раскрыть ветку (1)
Автор поста оценил этот комментарий

Вы говорите ровно то, что я написал в посте. Но есть ощущение что вы прочитали только заголовок.

показать ответы
6
Автор поста оценил этот комментарий

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

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

раскрыть ветку (1)
Автор поста оценил этот комментарий

В вашем комментарии и есть ответ: "... Имеющие огромный опыт разработки..."


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

показать ответы

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества