LINUX.ORG.RU

beef - новый системный ЯП

 , ,


2

8

https://www.beeflang.org/

Особенности:

  • По заявлению автора, представляет собой смесь C++ и C#, с небольшими вкраплениями Rust.
  • Без GC, JIT и тому подобного.
  • Развивается параллельно с IDE (написана на самом beef и собственном тулките). Дизайн языка развивается с учётом удобства разработки IDE.
  • Автор делает упор на удобную отладку с помощью дебаггера, а не print.
  • Умеет все модные фичи: ADT, pattern matching, лямбды, дженерики, миксины, кортежи, опциональные типы и тд. Но не гарантирует null-safety.
  • Поддерживает рантайм рефлексию.
  • Не использует исключения. Используется тот же подход что и в Rust: Result + panic.
  • Проверяет проблемы с памятью в рантайме в отладочной сборке. В релизной сборке всё как в C/C++.
  • Предоставляет лёгкое взаимодействие с C/C++ кодом (не уверен в каком виде).
  • Основан на ворованном LLVM. Как будто кто-то сомневался.
  • Автор пилит язык последние 5 лет full-time.

Простой пример:

static Result<uint> GetMinusOne(uint i)
{
    if (i == 0)
        return .Err;
    return .Ok(i - 1);  
}

void Use()
{
    /* Handle result via a switch */
    switch (GetMinusOne(i))
    {
        case .Ok(let newVal): Console.WriteLine("Val: {}", newVal);
        case .Err: Console.WriteLine("Failed");
    }

    /* This invokes an implicit conversion operator, which will be fatal at runtime if an error is returned */
    int newVal = GetMinusOne(i);

    /* Result<T> contains a special "ReturnValueDiscarded" method which is invoked to facilitate failing fatally on ignored returned errors here */
    GetMinusOne(i);
}

В целом ближе к D, чем к Rust, так как содержит намного меньше гарантий.

Ответ на: комментарий от anonymous

Приведи пример на языках с _мономорфизируемыми_ шаблонами/генериками/как их ещё называют. Java, если что, не подойдёт

В жаве используется вполне себе мономорфный object. Ты намерено ограничиваешь условия таким образом, чтобы под них попадал C++ и ближайшие производные (D, Rust). Есть очень много языков программирования в IT: Python, Lisp, ML, Scheme - даже Go, как ни странно, имеет хорошую поддержку полиморфизма во время компиляции при помощи объектов и интерфейсов.

byko3y ()
Ответ на: комментарий от byko3y

Ты намерено ограничиваешь условия

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

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

Он, вроде, компилируемый. Или там генерики сильно урезанные?

Lisp

Да ёпт, кто бы сомневался

anonymous ()
Ответ на: комментарий от anonymous

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

Казалось бы, при чем тут производительность, если есть Си, который дает ее такую же. Кресты никому бы не были нужны, если бы разрабы беспокоились одной лишь производительностью. Есть как минимум Pascal и Eiffel, которые статически компилируются, базируются на ручной работе с памятью, и при этом намного приятнее C/C++ в работе.

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

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

Он, вроде, компилируемый. Или там генерики сильно урезанные?

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

Да ёпт, кто бы сомневался

Не, большинство подумает на CL и подобны тормознутых мамонтов, а, тем не менее, есть статически компилируемые диалекты с минимальным использованием макросов во время компиляции - вместо них для полиморфизма используют функции, протоколы, интерфейсы.

byko3y ()
Ответ на: комментарий от anonymous

А, так там самому методы в интерфейсах прописывать надо. Это и на крестах так можно(виртуальные функции в помощь)

Так в крестах делать нельзя: в Go реализация класса ничего не знает про интерфейс.

byko3y ()
Ответ на: комментарий от byko3y

одной лишь производительностью

Это в контексте java/lisp/ect было. А на C обобщёнку писать макросами чресчур весело

вообще не понимаю, какое отношение это имеет к разговору

Такое, что либо производительность, либо удобство. Нельзя в компилируемых языках иначе - либо «обобщёнка на макросах», либо «хидер-онли»

anonymous ()
Ответ на: комментарий от anonymous

По диагонали читал. Но смысл не сильно меняется: к пользовательским типам добавляются требования

Я не уверен, что к ним предъявляются какие-то серьезные требования. Вектор заказывал?

https://github.com/golang/go/wiki/SliceTricks

byko3y ()
Ответ на: комментарий от anonymous

Это в контексте java/lisp/ect было. А на C обобщёнку писать макросами чресчур весело

А типа на крестах зело ясные ошибки в кодах на сложных шаблонах? По крайней мере в Си шаблоны макросы побыстрее будут, чем та порнуха, в которую их превратили в крестах.

вообще не понимаю, какое отношение это имеет к разговору

Такое, что либо производительность, либо удобство. Нельзя в компилируемых языках иначе - либо «обобщёнка на макросах», либо «хидер-онли»

А тебе не приходило в голову, что шаблонный код можно эффективно компилировать? Не так, как компилируют в крестах, а эффективно. Так, как делают в хаскеле, например. ML/haskell ведь долго компилируются не потому, что они обобщения парсят, а потому что при условии отсутствия явных объявлений типов они выводят типы свозь всю иерархию вызовов, что есть трудоемко.

byko3y ()
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от byko3y

на крестах зело ясные ошибки в кодах на сложных шаблонах?

Возможностей больше. Но ты можешь показать, как сделать специализацию шаблона на макросах(SFINAE туда же)

шаблонный код можно эффективно компилировать?

Мой вопрос, собственно, об этом и был: как это сделать?

anonymous ()
Ответ на: комментарий от byko3y

Я не уверен, что к ним предъявляются какие-то серьезные требования

Да что тут гадать?

In other words, interface types in Go are a form of generic programming. They let us capture the common aspects of different types and express them as methods. We can then write functions that use those interface types, and those functions will work for any type that implements those methods

anonymous ()
Ответ на: комментарий от anonymous

Я не уверен, что к ним предъявляются какие-то серьезные требования

Да что тут гадать?
They let us capture the common aspects of different types and express them as methods. We can then write functions that use those interface types, and those functions will work for any type that implements those methods

И? Я бы это написал так: в Go полиморфными аргументами функций могут быть только интерфейсы.

byko3y ()
Ответ на: комментарий от byko3y

По go у меня только один вопрос остался:

в Go полиморфными аргументами функций могут быть только интерфейсы

Это не требование, или требование, но не

серьёзное

?

И я всё ещё жду разъяснений, каким образом обобщённый код, параметризуемый произвольными типами, можно компилировать один раз, а не для каждого инстанса отдельно

anonymous ()
Ответ на: комментарий от anonymous

Но ты можешь показать, как сделать специализацию шаблона на макросах(SFINAE туда же)

Перегрузки функций в Си нет, но есть возможность просто добавить имя типа или имя переменной к имени функции в макросе. Таким макаром можно создать соответствие имени переменной и функций для работы с конкретным типом, что позволит делать что-то вроде:

vector(v, int);
vector_append(v, 1);
vector_append(v, 2);
vector_for(v, i, {
    printf("%d\n", i);
});
А с поддержкой RAII в GCC/Clang можно даже не освобождать выделенные локальные ресурсы.

шаблонный код можно эффективно компилировать?

Мой вопрос, собственно, об этом и был: как это сделать?

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

В крестах из-за неоднозначности синтаксиса компилятор не может решить, что значат те или иные строки, и ему нужно каждый раз для каждого файла делать два прохода, чтобы конкретизировать значение строк, и только тогда он сможет создавать экземпляры шаблонов и строить синтаксическое дерево. Это и убивает на корню любые надежды на быструю компиляцию. Это когда берешь кусок дерьма, умножаешь его на два. добавляешь сверху еще дерьма, а потом ставишь сверху на это вишенку - получается эволюция C в C++. Какой смысл теперь сидеть и думать о том, как съесть вишенку, не измаравшись в дерьме?

byko3y ()
Ответ на: комментарий от anonymous

По go у меня только один вопрос остался:

в Go полиморфными аргументами функций могут быть только интерфейсы

Это не требование, или требование, но не

серьёзное

?

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

byko3y ()
Ответ на: комментарий от byko3y

Перегрузки функций в Си нет

Я знаю. А также много чего ещё, о чём и была речь.

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

И перекомпилируется по требованию. Найс

В крестах из-за неоднозначности синтаксиса компилятор не может решить

Старая песня

anonymous ()
Ответ на: комментарий от anonymous

Я знаю. А также много чего ещё, о чём и была речь

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

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

И перекомпилируется по требованию. Найс

По какому требованию? Если изменен один исходник, то меняется только один предкомпилированный модуль. Бинарный файл - да, нужно каждый раз заново пересобирать. А что, какой-то язык позволяет делать частичную сборку, быстро заменяя в бинарнике только один модуль и не читая другие?

byko3y ()
Ответ на: комментарий от byko3y

Чего не хватает?

Инструментов для написания обобщённого кода

По какому требованию?

По требованию пользователя инстанцировать шаблон для аргументов, которые пользователь предоставил. Не тупи, а?

Если изменен один исходник, то меняется только один предкомпилированный модуль

В крестах точно также

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

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

? В библиотеке(модуле) или в клиентском бинарнике(использующем эту библиотеку)?

anonymous ()
Ответ на: комментарий от anonymous

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

По какому требованию?

По требованию пользователя инстанцировать шаблон для аргументов, которые пользователь предоставил. Не тупи, а?

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

Если изменен один исходник, то меняется только один предкомпилированный модуль

В крестах точно также

Как насчет изменения константы в заголовочках? Для меня загадка, как люди на этом фоне разрабатывают хром, ведь там изменение интерфейсов приводит к необходимости полной или почти полной пересборки всего проекта.

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

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

? В библиотеке(модуле) или в клиентском бинарнике(использующем эту библиотеку)?

В бинарник ничего не попадает, весь полиморфизм заканчивается на уровне компилированных модулей. У полимфорных статичных функций нет аналогов на машинных кодах, потому в стиле Си слинковать два бинарника просто так не получится - нужны конкретные бинарные интерфейсы.

byko3y ()
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от byko3y

В бинарник ничего не попадает

А, кажется, я понял, что ты имеешь ввиду. Ну, от нынешней ситуации отличие всего одно, на самом деле - не нужно руками инстанцировать. А так, вышеупомянутые extern templates тот же самый эффект дадут.

P. S. Такое даже делал какой-то компилятор крестовый, ЕМНИП. export templates называлось. Но больше никто не реализовал, и в итоге выкинули даже из стандарта. Так-то идея неплохая, но видимо никому особо не нужно

anonymous ()
Ответ на: комментарий от Legioner

мне не нравится

const сумма пенсионных накоплений({
    пенсионные накопления,
    родственники
}: физическое лицо) => родственники.reduce(
    (промежуточная сумма пенсионных накоплений, родственник) =>
	промежуточная сумма пенсионных накоплений +
	сумма пенсионных накоплений(родственник),
    пенсионные накопления
);
Princesska ()
Ответ на: комментарий от anonymous

Ну, от нынешней ситуации отличие всего одно, на самом деле - не нужно руками инстанцировать. А так, вышеупомянутые extern templates тот же самый эффект дадут

Для кого дадут? Для компилятора? Для человека они превратят написание кода в кошмар, полностью нивелируя все преимущества обобщений.

export templates называлось. Но больше никто не реализовал, и в итоге выкинули даже из стандарта. Так-то идея неплохая, но видимо никому особо не нужно

У меня давно сложилось впечатление, что главный критерий разработки языка крестов - это как максимизировать страдание кодера на этом языке. Потому, следуя этой логике, можно сказать, что export templates слишком упрощал работу программиста.

byko3y ()
Ответ на: комментарий от Legioner

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

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

DarkEld3r ★★★★★ ()
Ответ на: комментарий от vertexua

В Rust надо было не делать тут затычку в стиле трейта Error, а сразу идти дальше и стандартизировать более мощные средства обработки ошибок, прямо культивировать общую культуру их обработки.

Проблема в том, что всё сразу вряд ли удалось бы учесть. В итоге в стд бы торчала хрень, которую нельзя было бы выкинуть без слома обратной совместимости.

DarkEld3r ★★★★★ ()