Компилятор Аргентума портирован на ARM64. И, т.к. для генерации кода используется LLVM, он собирает исполняемые файлы для ARM.
Сайт плейграунда запущен на Raspberry Pi (node.js).
При нажатии Run:
браузер делает POST с вашим текстом на compile-link-execute endpoint
ваш исходник записывается во временный файл /temp/<hash>/xNNNtst.ag
запускается CGI скрипт, который:
компилирует agc -target aarch64-linux-gnu...
линкует получившийся obj с libag_runtime
запускает получившийся исполняемый файл с memory&timeout лимитами (1Mb & 5sec) через systemd-run от имени специального юзера.
артефакты сборки/запуска удаляются на сервере
браузеру возвращается отфильтрованный stderr&stdout компилятора, линковщика и вашего запущенного приложения.
Я вначале добавил кнопку "download executable" (Linux/Windows) (x86/Arm) но на это очень нервно реагировали виндовые антивирусы, и поэтому осталась только кнопка Run.
В Аргентуме операции над int выполняются по модулю 2^64 в 2-complement, это не UB.
В словременных процессорах все скаляры все равно хранятся в 64-битных регистрах, и переход на 128 скорее всего не произойдет в ближайшие сто лет, а для работы с SIMD потребуются совсем другие типы данных. Поэтому int Аргентума - это всегда 64 бита со знаком. И других целочисленных типов нет.
В модуле sys есть класс байтового массива Blob, который позволяет читать читать/писать 8-16-32-64 битовые значения в/из int.
Спасибо, посмотрю. Только не понятно, как UUIDv7 помогут в организации лукапа одного объекта по другому. Кстати, сейчас хешпап дает амортизированную константную сложность. Если есть возможность сделать проще/быстрее, буду рад.
Тоже интерфейсы. Причем в отличие от Go/Kotlin указатель на интерфейс занимает не два указателя, а один. Это здорово уменьшает количество исполняемых инструкций и экономит траффик регистры<->память.
Пример Котлина показывает, что исправление неудачного синтаксиса предшественников стоит свеч.
О портабельности кода: Исходники на Basic нельзя перевести, например на Си - там нет gosub. При таком переносе программа декомпозируется на функции. Аргентум тоже поднимет уровень абстракций - в нем ссылочная модель задается декларативно - тут "А" владеет "Б", тут "А" просто знает о "Б", тут "А" и "Б" совместно владеют неизменяемым образом "В". И больше не надо писать вручную деструкторы и методы копирования. Автоматические операции сделают все как надо. Сдвиг парадигмы серьезнее, чем синтаксис.
Тем более, что синтаксис Аргентума - это примерно C++/Java с некоторыми улучшениями.
можно ли в этой модели сделать такие структуры данных?
есть ли они сейчас в стандартной библиотеке?
Ответ на первый вопрос. Да. Связный список это просто ноды, ссылающиеся веперед композитным указателем, и назад ассоциативным. А красно-черное дерево - это просто дерево. Аргентум позволяет переподчинять владеющие узлы с помощью безопасной сплайс-операции. Так что перевешивать узлы дерева тоже можно. Так что проблем нет. И циклические структуры данных - тоже не является проблемой. Например в плейграунде есть готовый пример построения и обхода графа с поиском циклов.
Ответ на второй вопрос. Сейчас нет. В рантайм-библиотеке реализован индексированный массив переменного размера и хеш-мап. Я планирую добавлять новые контейнеры если они будут нужны, после реализации GUI-библиотеки, встроенной сериализации, интеграции с базами данных, каким-нибудь фреймворком http-сервера, SKIA, и геренации WASM.
Счетчики - только для стековых и агрегатных ссылок + escape analysis который уменьшает потребность в операциях над ссылками в разы + счетчики не атомарны. То есть счетчики есть, но используются не часто и без ARC накладных расходов.
Сильные ссылки в свою очередь делятся на стековые и хиповые. У них разная семантика, котрая гарантирует отсутствие циклов.
Появился он раньше а до рынка дошел на полтора года позже. С вдвое урезанной производительностью и заданной ценой. Джобс несколько раз разгонял команду переименовывал проект, менял цели, приоритеты. Все чтобы дать своему маку возможность победить в этом соревновании.
Например, Apple II не имел графического пользовательского интерфейса и мыши
У Apple II GS, который появился раньше Макинтоша, был и многооконный графический интерфейс и мышь, причеми ADB - более продвинутая, чем у Мака, и открытая расширяемая архитектура, и полная обратная совместимость при существенно меньшей чем у Мака цене. Джобс сознательно убил этот порывной продукт Возняка.
Хотелось бы увидеть какой-нибудь практический пример.
Например, объект ‘магазин', который владеет учётными записями покупателей списками категорий и товаров. Покупатель владеет несколькими корзинами, которые ссылаются на товары. Товары ссылаются на категории, а категории хранят списки ссылок на товары. Есть групповые товары соединяющие несколько товаров в один.
Как эти структуры данных выражаются в виде умных ссылок раста. Как lifetimes и borrow checker предохраняют от случайного шарения корзин между покупателями, включения группового товара в самого себя, случайного включения одной и той же категории два раза в список категорий магазина и других нарушений логической структуры модели.
Как в Расте предлагается обрабатывать эту структуру данных многопоточно?
"Поэтому я и сделал замечание, что данная запись в C++17 зачастую требуется для другого, и привёл соответствующий код." Для какого другого?
Если в этой вашей записи:
if (line := input()) != '':
use(line)
.. пустая строка не означает отсутствие строки, а означает именно пустую введенную человеком строку, то молча игнорировать ее будет как минимум невежливо. Она должна проверяться на соответствие какой-то грамматике с выводом осмысленных сообщений об ошибках.
Если введенная строка обрабатывается большим куском кода, который по каким-то причнам не захотели оформить в виде фукнции, ок:
input() -> {
// большой кусок кода, использующий _
}
// или, если в большом куске кода тоже есть потребность в своих внутренних _-переменных
input() -> `name {
// большой кусок кода, использующий name
}
// или, если нет желания локализовать результат input в этом выражении:
name = input();
use(name)
....
Рантайм-исключения в Аргентуме есть. Они реализованы по-другому - с возможностью перезапуска, с обязательным включением в прототип функций и методов, очень легковесно и используя только уже существующие в языке конструкции.
В двух словах - в метод передается лямбда-параметр который вызывается при определенных проблемах и который может или решить проблему в той точке, в которой она возникла или завершить приложение или вернуть управление из любого блока внешнего лексического скоупа, безопасно размотав стек до любой точки. Это как если бы цикл for был функцией, а тело цикла было ее лямбда-парамером и в нем бы раболал break передающий управление за пределы цикла for. Аргентум именно так работает и с циклами и с исключениями.
Поскольку в статье упомянули Аргентум, а я как раз автора этого языка, позволю себе вкратце пробежаться по тоиу, как там сделана многопоточность и совместный доступ.
Каждый объект в агентуме может быть изменяемым или неизменяемым. Существует операция заморозки, которая превращает объект в неизменяемый и операции снятия изменяемой копии с неизменяемого объекта. Ссылки на объекты на уровне системы типов также различаются на изменяемые и неизменяемые. Существуют слабые ссылки на изменяемые объекты.
Теперь о том, как это всё работает в многопотоке:
Неизменяемые объекты свободно шарятся между всеми потоками. Для доступа к ним мне нужна синхронизация, н нужны mitexes и atomics.
Изменяемые объекты всегда принадлежат одному потоку, и для доступа к ним также не требуется никакой синхронизации.
Один поток может иметь слабые ссылки на объекты другого потока. Но обращаться к ним синхронно он не может. Зато он может посылать этим объектом асинхронные сообщения, которые будут исполнены в контексте потока объекта - получателя. Этот механизм очень похож на рандеву.
Если асинхронное сообщение несёт на себе в качестве параметра какой-то изменяемымй объект, этот объект логически передаётся из потоков поток никакого копирования при этом конечно же не происходит.
Эта схема гарантирует
отсутствие дедлоков, поскольку работающий код вообще никогда не хватает мьютексов,
отсутствие лайвлоков, поскольку атомарные операции тоже не используются,
отсутствие data races, поскольку каждая операция выполняется в своём потоке как изолированная транзакция.
Даже подсчёт ссылок для неизменяемых объектов не требует атомарных операций, поскольку существует гарантия того, что любой поток имеющий ссылку на неизменяемый объект, получил эту ссылку по цепочке других неизменяемых объектов вплоть до корневого и измеряемого объекта который локален в этом потоке, и только эта корневая ссылка на самом деле должна быть защищена, а она локальна для потока, и её операции можно отложить до момента когда какой-то мютекс всё равно нужно будет схватить например чтобы работать с очередями рандеву.
Эта схема проста с точки зрения реализации, эффективна и безопасна.
Все примеры описывают поведение ссылок, когда они локальные переменные. Хотелось бы увидеть как они будут работать в полях объектов. Например, что будет, если у меня есть многопоточная ссылка на объект, поле которого является однопроточной ссылкой, как я могу получить доступ к этому полю, зная, что этот объект на самом деле - объект моего потока.
# Обычная переменная без раздельного доступа # и без возможности создания ссылки на объект let val := 123; let val_err := &val; # Ошибка !!!!
Если на объект нельзя создавать другие ссылки, даже временные ссылки в виде локальных переменных, то такой объект нельзя передать параметром в функцию, например, this-параметром, то есть у такого объекта нельзя вызывать методы, что делает такую конструкцию бесполезной.
В этом примере input возвращает или введенную строку или магическое значение пустая строка "", если ничего не ввели. При этом теряется возможность различить не веденную строку и пустую введенную строку.
В идиоматическом Аргентуме функция input() будет возвращать ?String, чтобы различить существующую (возможно и пустую) строку и несуществующую строку. И обрабатываться она будет так:
input() ? use(_)
Или со специальной обработкой введенных пустых строк:
Кстати, вспомогательная функция-предикат - это не так уж плохо, она поможет сделать код самодокументирующимся даже если input() будет кодировать отсутствие строки пустой строкой:
Компилятор Аргентума портирован на ARM64. И, т.к. для генерации кода используется LLVM, он собирает исполняемые файлы для ARM.
Сайт плейграунда запущен на Raspberry Pi (node.js).
При нажатии Run:
браузер делает
POST
с вашим текстом на compile-link-execute endpointваш исходник записывается во временный файл
/temp/<hash>/xNNNtst.ag
запускается CGI скрипт, который:
компилирует
agc -target aarch64-linux-gnu...
линкует получившийся obj с
libag_runtime
запускает получившийся исполняемый файл с memory&timeout лимитами (1Mb & 5sec) через
systemd-run
от имени специального юзера.артефакты сборки/запуска удаляются на сервере
браузеру возвращается отфильтрованный stderr&stdout компилятора, линковщика и вашего запущенного приложения.
Я вначале добавил кнопку "download executable" (Linux/Windows) (x86/Arm) но на это очень нервно реагировали виндовые антивирусы, и поэтому осталась только кнопка Run.
В Аргентуме операции над
int
выполняются по модулю 2^64 в 2-complement, это не UB.В словременных процессорах все скаляры все равно хранятся в 64-битных регистрах, и переход на 128 скорее всего не произойдет в ближайшие сто лет, а для работы с SIMD потребуются совсем другие типы данных. Поэтому
int
Аргентума - это всегда 64 бита со знаком. И других целочисленных типов нет.В модуле
sys
есть класс байтового массиваBlob
, который позволяет читать читать/писать 8-16-32-64 битовые значения в/изint
.Спасибо, посмотрю. Только не понятно, как UUIDv7 помогут в организации лукапа одного объекта по другому. Кстати, сейчас хешпап дает амортизированную константную сложность. Если есть возможность сделать проще/быстрее, буду рад.
Тоже интерфейсы. Причем в отличие от Go/Kotlin указатель на интерфейс занимает не два указателя, а один. Это здорово уменьшает количество исполняемых инструкций и экономит траффик регистры<->память.
Пример Котлина показывает, что исправление неудачного синтаксиса предшественников стоит свеч.
О портабельности кода: Исходники на Basic нельзя перевести, например на Си - там нет gosub. При таком переносе программа декомпозируется на функции. Аргентум тоже поднимет уровень абстракций - в нем ссылочная модель задается декларативно - тут "А" владеет "Б", тут "А" просто знает о "Б", тут "А" и "Б" совместно владеют неизменяемым образом "В". И больше не надо писать вручную деструкторы и методы копирования. Автоматические операции сделают все как надо. Сдвиг парадигмы серьезнее, чем синтаксис.
Тем более, что синтаксис Аргентума - это примерно C++/Java с некоторыми улучшениями.
Спасибо за тест-кейс. Ошибка в sys_StringBuilder.putInt из модуля strings.ag.
Ваш вопрос можно прочитать двояко:
можно ли в этой модели сделать такие структуры данных?
есть ли они сейчас в стандартной библиотеке?
Ответ на первый вопрос. Да. Связный список это просто ноды, ссылающиеся веперед композитным указателем, и назад ассоциативным. А красно-черное дерево - это просто дерево. Аргентум позволяет переподчинять владеющие узлы с помощью безопасной сплайс-операции. Так что перевешивать узлы дерева тоже можно. Так что проблем нет. И циклические структуры данных - тоже не является проблемой. Например в плейграунде есть готовый пример построения и обхода графа с поиском циклов.
Ответ на второй вопрос. Сейчас нет. В рантайм-библиотеке реализован индексированный массив переменного размера и хеш-мап. Я планирую добавлять новые контейнеры если они будут нужны, после реализации GUI-библиотеки, встроенной сериализации, интеграции с базами данных, каким-нибудь фреймворком http-сервера, SKIA, и геренации WASM.
Счетчики - только для стековых и агрегатных ссылок + escape analysis который уменьшает потребность в операциях над ссылками в разы + счетчики не атомарны. То есть счетчики есть, но используются не часто и без ARC накладных расходов.
Сильные ссылки в свою очередь делятся на стековые и хиповые. У них разная семантика, котрая гарантирует отсутствие циклов.
Запрет проверяется компилятором.
Тут: https://aglang.org/how-argentum-programming-language-handles-object-hierarchies/
Спасибо, исправил.
Появился он раньше а до рынка дошел на полтора года позже. С вдвое урезанной производительностью и заданной ценой. Джобс несколько раз разгонял команду переименовывал проект, менял цели, приоритеты. Все чтобы дать своему маку возможность победить в этом соревновании.
У Apple II GS, который появился раньше Макинтоша, был и многооконный графический интерфейс и мышь, причеми ADB - более продвинутая, чем у Мака, и открытая расширяемая архитектура, и полная обратная совместимость при существенно меньшей чем у Мака цене. Джобс сознательно убил этот порывной продукт Возняка.
Хотелось бы увидеть какой-нибудь практический пример.
Например, объект ‘магазин', который владеет учётными записями покупателей списками категорий и товаров. Покупатель владеет несколькими корзинами, которые ссылаются на товары. Товары ссылаются на категории, а категории хранят списки ссылок на товары. Есть групповые товары соединяющие несколько товаров в один.
Как эти структуры данных выражаются в виде умных ссылок раста. Как lifetimes и borrow checker предохраняют от случайного шарения корзин между покупателями, включения группового товара в самого себя, случайного включения одной и той же категории два раза в список категорий магазина и других нарушений логической структуры модели.
Как в Расте предлагается обрабатывать эту структуру данных многопоточно?
"Поэтому я и сделал замечание, что данная запись в C++17 зачастую требуется для другого, и привёл соответствующий код." Для какого другого?
Если в этой вашей записи:
.. пустая строка не означает отсутствие строки, а означает именно пустую введенную человеком строку, то молча игнорировать ее будет как минимум невежливо. Она должна проверяться на соответствие какой-то грамматике с выводом осмысленных сообщений об ошибках.
Например так:
Если введенная строка обрабатывается большим куском кода, который по каким-то причнам не захотели оформить в виде фукнции, ок:
Рантайм-исключения в Аргентуме есть. Они реализованы по-другому - с возможностью перезапуска, с обязательным включением в прототип функций и методов, очень легковесно и используя только уже существующие в языке конструкции.
В двух словах - в метод передается лямбда-параметр который вызывается при определенных проблемах и который может или решить проблему в той точке, в которой она возникла или завершить приложение или вернуть управление из любого блока внешнего лексического скоупа, безопасно размотав стек до любой точки. Это как если бы цикл
for
был функцией, а тело цикла было ее лямбда-парамером и в нем бы раболалbreak
передающий управление за пределы циклаfor
. Аргентум именно так работает и с циклами и с исключениями.Поскольку в статье упомянули Аргентум, а я как раз автора этого языка, позволю себе вкратце пробежаться по тоиу, как там сделана многопоточность и совместный доступ.
Каждый объект в агентуме может быть изменяемым или неизменяемым. Существует операция заморозки, которая превращает объект в неизменяемый и операции снятия изменяемой копии с неизменяемого объекта. Ссылки на объекты на уровне системы типов также различаются на изменяемые и неизменяемые. Существуют слабые ссылки на изменяемые объекты.
Теперь о том, как это всё работает в многопотоке:
Неизменяемые объекты свободно шарятся между всеми потоками. Для доступа к ним мне нужна синхронизация, н нужны mitexes и atomics.
Изменяемые объекты всегда принадлежат одному потоку, и для доступа к ним также не требуется никакой синхронизации.
Один поток может иметь слабые ссылки на объекты другого потока. Но обращаться к ним синхронно он не может. Зато он может посылать этим объектом асинхронные сообщения, которые будут исполнены в контексте потока объекта - получателя. Этот механизм очень похож на рандеву.
Если асинхронное сообщение несёт на себе в качестве параметра какой-то изменяемымй объект, этот объект логически передаётся из потоков поток никакого копирования при этом конечно же не происходит.
Эта схема гарантирует
отсутствие дедлоков, поскольку работающий код вообще никогда не хватает мьютексов,
отсутствие лайвлоков, поскольку атомарные операции тоже не используются,
отсутствие data races, поскольку каждая операция выполняется в своём потоке как изолированная транзакция.
Даже подсчёт ссылок для неизменяемых объектов не требует атомарных операций, поскольку существует гарантия того, что любой поток имеющий ссылку на неизменяемый объект, получил эту ссылку по цепочке других неизменяемых объектов вплоть до корневого и измеряемого объекта который локален в этом потоке, и только эта корневая ссылка на самом деле должна быть защищена, а она локальна для потока, и её операции можно отложить до момента когда какой-то мютекс всё равно нужно будет схватить например чтобы работать с очередями рандеву.
Эта схема проста с точки зрения реализации, эффективна и безопасна.
Пока это выглядит как "пусть мыши станут ёжиками". Идея нуждается в доработке и не может быть оценена.
Все примеры описывают поведение ссылок, когда они локальные переменные. Хотелось бы увидеть как они будут работать в полях объектов. Например, что будет, если у меня есть многопоточная ссылка на объект, поле которого является однопроточной ссылкой, как я могу получить доступ к этому полю, зная, что этот объект на самом деле - объект моего потока.
Если на объект нельзя создавать другие ссылки, даже временные ссылки в виде локальных переменных, то такой объект нельзя передать параметром в функцию, например, this-параметром, то есть у такого объекта нельзя вызывать методы, что делает такую конструкцию бесполезной.
Извиняюсь за долгую задержку. Увольнялся из Гугла, устраивался на работу в Meta. Сейчас немножко появилось время.
Обсудить можно в https://www.reddit.com/r/ArgentumLanguage/, на сайте https://aglang.org/, теперь еще и в https://github.com/karol11/argentum/discussions
Включил Discussions, спасибо за наводку.
По вашим примерам:
В этом примере input возвращает или введенную строку или магическое значение пустая строка "", если ничего не ввели. При этом теряется возможность различить не веденную строку и пустую введенную строку.
В идиоматическом Аргентуме функция input() будет возвращать ?String, чтобы различить существующую (возможно и пустую) строку и несуществующую строку. И обрабатываться она будет так:
Или со специальной обработкой введенных пустых строк:
Кстати, вспомогательная функция-предикат - это не так уж плохо, она поможет сделать код самодокументирующимся даже если input() будет кодировать отсутствие строки пустой строкой:
Если надо прямо отдельно обработать именно не пустую строку и проигнорировать пустую и без фукнций:
Или с использованием нового пайп-оператора: