|
Представлен релиз языка системного программирования Nim 2.4.0. Nim – статически типизированный компилируемый язык программирования с синтаксисом, вдохновлённым Python, и возможностями метапрограммирования на уровне Lisp. Язык компилируется в C, C++ и JavaScript, обеспечивая производительность на уровне C при выразительности высокоуровневых языков. Код проекта поставляется под лицензией MIT.
Возможности Nim включают систему макросов, работающих на AST во время компиляции, поддержку обобщённого программирования с концептами, множественную диспетчеризацию (multiple dispatch), детерминированное управление памятью с поддержкой нескольких стратегий (ARC/ORC, refc, маркировка-и-подметание), встроенную поддержку async/await для асинхронного программирования и FFI для простой интеграции с C/C++/JavaScript. Nim позиционируется как системный язык, подходящий для разработки от встраиваемых систем до веб-серверов, с акцентом на эффективность, безопасность памяти и удобство разработки.
Основные изменения:
- Реализован режим оптимизации коротких строк (SSO, Small-String Optimization), включаемый через опцию "--strings:sso" и приводящий к хранению небольших строк непосредственно в самом объекте, без отдельного выделения памяти в куче. Для эффективного использования этого режима предложен новый низкоуровневый API, в котором вместо синтаксиса "addr myString[offset]" предложены раздельные операции для чтения и записи (readRawData, readRawDataStable, beginStore и endStore). Например, при использовании нового API операции "addr s[i]" и "unsafeAddr s[i]" заменяются на вызов "readRawData(s, i)", а связка "setLen" и "addr s[oldLen]" заменяется на "beginStore(s, newLen, start)" с последующим "endStore(s)".
# До: установка длины и копирование по адресу элемента строки
result.setLen newLen
copyMem(addr result[oldLen], buf, buflen)
# После: beginStore устанавливает длину, обеспечивает уникальное
# владение и возвращает указатель для записи; endStore завершает запись
copyMem(beginStore(result, newLen, oldLen), buf, buflen)
endStore(result)
- Реализована поддержка параллельной и инкрементальной сборки, активируемой через опцию "--ic:on". Распараллеливание реализовано на основе многопроцессности, а не многопоточности, поэтому макросы, использующие глобальные переменные ".compileTime", могут потребовать доработки для ускорения сборки. Ускорение наблюдается не только при повторных пересборках, но и при первой сборке за счёт распараллеливания. Например, сборка компилятора Nim на машине с 16 физическими ядрами при использовании опции "--ic:on" сократилась с 31 до 25 секунд.
- Добавлен новый алгоритм управления памятью YRC, активируемый опцией "--mm:yrc" и обеспечивающий корректную обработку циклических ссылок, пересекающих границы потоков. YRC предоставляет тот же API, что и ORC, но помимо возможностей последнего обеспечивает потокобезопасность.
Для ациклических данных применяется атомарный подсчёт ссылок, а для циклических - барьер записи (write barrier), который активируется только при присваивании указателей. При этом в тесте orcbench YRC примерно в два раза медленнее ORC и потребляет вдвое больше памяти.
- Поддержка типов-сумм (sum types), при объявлении которых применяется сопоставление с образцом.
type
Node = ref object
case
of IntLit: intVal: int
of FloatLit: floatVal: float
of StringLit: strVal: string
of AddOp, SubOp:
leftOp, rightOp: Node
of IfStmt:
condition, thenPart, elsePart: Node
Имя ветви служит конструктором с именованными аргументами:
let n = AddOp(leftOp: IntLit(intVal: 40), rightOp: IntLit(intVal: 2))
Поддерживаются обобщённые типы-сумм (generic), в которых конкретный экземпляр выводится из значений полей или из ожидаемого типа:
type
Opt[T] = object
case
of None: discard
of Some: val: T
let a = Some(val: "abc") # Opt[string]
let b: Opt[int] = None() # ожидаемый тип выбирает экземпляр
Доступ к полям осуществляется исключительно через сопоставление с образцом, поэтому обращение к полю чужой ветви (FieldDefect) невозможно. Поля привязываются к именам, следующим за именем ветви:
proc eval(n: Node): int =
case n
of IntLit(v): v # v привязано к intVal
of AddOp(a, b): eval(a) + eval(b) # a, b — leftOp, rightOp
of SubOp(a, b): eval(a) - eval(b)
else: raiseAssert "not an arithmetic expression"
- Добавлен экспериментальный модификатор "owned" для управления памятью на основе явного владения без использования сборщика мусора, активируемый при указании опции "--experimental:ownedRefs".
Типы "owned ref T" и замыкания "owned" гарантируют наличие
единственного владельца у объекта. Подобные объекты нельзя копировать и можно только перемещать, а при выходе из области видимости они автоматически уничтожаются.
- Добавлена прагма "{.cyclic.}" для обработки циклического импорта модулей, ссылающихся друг на друга, которая позволяет связывать модули напрямую без выноса типов в отдельный файл и избавляет от необходимости подбирать строгий порядок объявлений в коде.
|