Skip to content

Instantly share code, notes, and snippets.

@gavr123456789
Last active August 13, 2022 21:17
Show Gist options
  • Save gavr123456789/d646b55e3947b9ebb94b4527b78af977 to your computer and use it in GitHub Desktop.
Save gavr123456789/d646b55e3947b9ebb94b4527b78af977 to your computer and use it in GitHub Desktop.
Nim Podvodniy

Общее

Девиз: Efficient, Expressive, Elegant где под эффективностью понимается скорость С, под выразительностью синтаксис Python, а под элегантностью метапрограммирование Lisp https://nim-lang.org/

Структурная парадигма

Под структурной парадигмой автор понимает что у вас есть один вход в блок кода и один выход. То есть использовать return, break, continue не идиоматично  

Так например внутри цикла вместо continue по условию можно инвертировать условие избавившись от continue(но увеличив вложенность)

https://youtu.be/D_G9h7DcIqM?t=1095

неявный result

Во всех функциях неявно объявлена переменная result которая является возвращаемым значением, это также способствует соблюдению принципа один вход один выход control flow функций.

разделение на value и referecne типы

Отдается предпочтение использованию value типов, с ними control flow выглядит предсказуемее

person = newPerson(name: "Alice")
otherPerson = person
person.renameTo "Bob"
assert otherPerson.name == "alice"

Ну и выделение памяти на стеке куда быстрее чем на куче.

Нно у value типов есть один минус, обычно они постоянно копируются, при передаче в функцию, при присваивании, при возврате

Функциональные

разделение на func и proc

TODO и разумеется функции являются объектами первого класса.

Система эффектов

TODO

отлов мест выбрасывающих ошибки

TODO

иммутабельность

Иммутабельность не shallow как в большенстве языков, а более настоящая.
Например если в шарпах создать массив через const, в него все равно можно будет записывать данные, тоже самое с объектами и их полями, константная только ссылка.

В ниме такое не пройдет, var является модификатором типа, функции добавления в массив объявлены для var версии, поэтому для коснтантной просто не найдется append функции. Но ничто не мешает создать мутабельную ссылку на иммутабедбную переменную, и это даже является неким паттерном.

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

ifы и switchи экспрешоны

Что куда более свойственно функциональным языкам, но в последнее время набирает популярность и в остальных. И да в Nim как и в Kotlin нет тернарного оператора, вместо него предлагается использовать if

UFCS

universal function call syntax
Позволяет вызывать функцию так как будто это метод первого аргумента То есть функцию foo с параметра a,b,c можно вызвать как a.foo(b, c)

temp Я совсем недавно пробовал объяснить UFCS моему другу плюсовику хаскелисту, и ему этот концепт был прям очень непривычек, он сказал проще выразить это через композицию функций f1 f2 f3 . x Поэтому я придумал 2 способа быстрее свыкнуться с такой парадигмой:

для функциональщиков: точка является ферст пайп оператором, для оопшников: Каждая функция является функцией расширения для типа первого аргумента


Заметка про пайп операторы Для тех кто не слышал что пайп оператор может быть first и last, это довольно просто first pipe отправляет аргумент на первое место, last на последнее last: array |> filter(func) = filter(array, func) fiest: array -> filter(func) = filter(func, array)

То есть функции удобно пайпить только если они дружелюбны к этому, и принимают скажем так главный аргумент последним

Забавный факт, rescript сделал по 2 версии пакетов при бинде std js, один обычный а второй с инвентированным порядком аргументов, чтобы их можно было красиво пайпить


Итераторы

Это как генераторы в питоне, тут они зеро кост.

Конверторы

Чем то напоминает страшные имплиситы из Scala. Рекомендуется не использовать. Можно объявть функцию конвертер из одного типа в другой, тогда эти типы будут неявна кастится друг в друга при случаи. К счастью транзитивность не поддерживается и если объявлен конвертер из A в B и из B в C, A не будет неявно кастится к C, в отличии от скалы)

СTFE

when

использование when вместе с isMainModule TODO

GC

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

RefC

ARC

Move семанитка

Циклические ссылки

acyclic-pragma https://nim-lang.org/docs/manual.html#pragmas-acyclic-pragma TODO

ORC

ООП

Хоть у нима это и не основная парадигма, ним полностью ее поддерживает.

Автодополнение

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

Хоть в ниме и нельзя добавить метод непосредственно в тип(что есть хорошо), но эта фича дается при помощи UFCS

Наследование

Обычное

TODO

от root object

TODO

Ключевое слово method

TODO

Мультиметоды

https://nim-lang.org/docs/tut2.html#object-oriented-programming-dynamic-dispatch

Примечание по производительности: Nim не создает таблицу виртуальных методов, но генерирует деревья диспетчеризации. Это позволяет реализовать оптимизацию инлайнинга и избежать дорогостоящей операции поиска для вызовов методов. Однако CTFE и DCE работать с методами не будут по очевидным причинам.

Система типов

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

дефолтные значения

Все инициализируется дефолтными значениями, даже объекты, и у этого есть как плюсы так и минусы.

Плюсы в том что не нужно волноваться проинициализирован ли объект new, а в купе с result получается совсем красота, в функциях создающих или преобразующих данные вы просто сразу заполняете поля объекта result, не нужно вызывать конструктор, не нужно писать return

Минусы компилятор не выдаст ошибку если вы заполнили только name. но забыли про age, age просто будет равно 0.

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

distict types

TODO Вот тут классные примеры https://nim-lang.org/docs/manual.html#types-distinct-type

Структурная и номинальная типизация

TODO

ADT (ну типа)

TODO Минусы: можно попробовать получить доступ к любому полю варианта в любой момент, если этот вариант при этом является другой веткой, это приведет к вылету в рантайме

exhaustive switch s

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

Минусы: нет тайп гвардов или они в недоделанном состоянии, например есть прагма которая фиксит проблему доступа к полям вариантов, но она работает только для switch экспрешона, то есть внутри switch кейса доступ к неправильному полю будет ошибкой компиляции, а внутри ифа нет. Это конечно просто недопиленность, прагма находится в разделе экспериментальных фичь и скорее всего будет исправлена.

Concepts

TODO

Метапрограммирование

TODO

Дженерики

Темплейты

Макросы

Многопоточность

TODO https://nim-lang.org/docs/manual.html#threads

Hot code reload

TODO https://youtu.be/7WgCt0Wooeo

Модули

TODO https://nim-lang.org/docs/tut1.html#modules

Взаимодействие с другими ЯП

Python

nimpy 1.1K - Импорт питона в nim и наоборот. Сделано с расчетом на ABI совместимость, скомпилированные модули не зависят от конкретной версии питона. С символы загружаются в рантайме

Nimporter 640 - Использует nimpy, позволяет импортировать Nim в Python напрямую, как если бы это были нативные Python модули. Кеширует результаты в __ pycache __ что позволяет не пересобирать ним каждый раз. Никаких дополнительных конфигураций, просто помечаете ним функцию атрибутом кода exportpy, и импортируете nim модуль из python. Поддерживаются не только одиночные файлы но и целые проекты. Подходит для ускорения узких мест, для чего обычно в питоне используют C.

nimpy можно использовать вместе с nimporter чтобы бесшовно импортировать модули сгенерированные nimpy.

nimpylib синтаксический сахар, функции которые выглядят как фукнции из стандартной библиотеки Python для упрощения портирования кодовой базы с Python на Nim.

C

c2nim 387 - Пилится автором языка, транслирует ANSI C в читаемый Nim код. Предназначен для трансляции фрагментов C/C++ кода, а не проектов или библиотек, поэтому не фоловит include директивы, парсит не весь возможный C/C++, некоторые конструкции не могут быть представлены в nim. Подходит для облегчения ручного портирования кодовой базы на ним

futhark 172 - использует Clang, позволяя импортировать C файлы напрямую без оберток как в Zig.
Результаты кешируются в виде пре генерированных ним файлов. В C нет типов, и часто используются всякие void*, в ним типизация более строгая, поэтому есть директива retype object.field type Также в прегенерированных файлах с биндингами все находится под when тип defined, то есть можно легко переопределить типизацию целого типа если создать тип перед importc(тогда он будет)

Nimterop 305 - в отличии от c2nim сосредотачивается на преобразовании хедеров, а не самого кода. Использует treesitter для парсинга C

Предлагает удобные способы упаковки, статическое или динамическое связывание с установленными в системе библиотеками или их загрузка и сборка с помощью autoconf или cmake из Git репы или архива с исходниками

Из всех трех я считаю лучшим futhark из-за использования CLang, аналогичный подход используют в DLang, но там нет прямых C импортов Минус: в отличии от биндингов созданных вручную использование сгенерированных будет очень сишным и не идеоматичным по ниму.

C++

c2nim cinterop - не заменяет c2nim и остальные врапер генераторы, нужна для быстрого прототипирования взаимодействия с большими кодовыми C/C++ базами, спроектирована так чтобы было легко смигрировать на importC/Cpp прагмы.

importcpp прагма

Один из трех языков который может полностью интеропится с C++ кодом(D(юзает clang), Carbon(юзает тоже ABI), nim(эммитит C++)).

Шаблоны C++ могут быть преобразованы в дженерики или темплейты нима. Как это работает?

На декларацию ним функции можно поставить importcpp прагму указав хедер, если функция простая то этого достаточно, тут хватит c2nim.

Для более сложных случаев, используется мини язык подстановок аля С макросы. К импортируемой из C++ функции цепляете прагму importCPP со строкой содержащей то что должно быть сгенерированно на стороне CPP. Например можно использовать для простого переименования PascalCase в camelCase. proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "CppMethod".} Внутри этой строки могут содержаться спец символы для замены, тут начинает работать аналогия с си макросами апостроф звездочка цифра '*i заменится на итый дженерик параметр функции

Nim
proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.}
let x: ptr Input = getSubsystem[Input]()
C++
x = SystemManager::getSubsystem<System::Input>()

означает нный аргумент функции,

простой пример: add(a, b) '# + #' @ означает все оставшиеся аргументы функции разделенные запятой #. означает аргумент который должен быть использован на первом месте до точки, это связано с UFCS foo(a, b, c) {.importcpp: "#.foo(@)".} obj.(2, 3) запродьюсит a->foo(b, c)

compile прагма

скомпилирует и слинкует указанный c/cpp файл, ним вычисляет sha1 чтобы перекомпилировать только при изменениях.

emit прагма

Использование крайне не рекомендуется, но может быть незаменимо для взаимодействия с кодом на C++/ObjC. Разумеется код с использованием emit становится непереносим на другие бекенды(разве что эмитить внутри when??)

{.emit: """
static int cvariable = 420;
""".}

{.push stackTrace:off.}
proc embedsC() =
  var nimVar = 89
  # access Nim symbols within an emit section outside of string literals:
  {.emit: ["""fprintf(stdout, "%d\n", cvariable + (int)""", nimVar, ");"].}
{.pop.}

embedsC()

Задекларированное в емит прагме можно тут же использовать в ниме через importc/cpp с прагмой nodecl вместо хедера.

Пример импорта хешмапы из движка Urho3D используя c2nim https://github.com/3dicc/Urhonimo/blob/master/modules/container/hashmap.nim

Работа с памятью

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

compatible string

Для строк можно использовать тип cstring который является нативным представлением строк таргет языка, то бишь у js будут js строки, у C будет const char* c нуль терминатором на конце

ним строки неявно преобразуются в cstring для удобства, то есть в функцию принимающую си строку можно кидать ним строку, си строка будет указывать на буфер ним строки, если посланная в си код строка лежала в ним объекте, то нужно не забывать про GCref/unre тк кк этот объект может быть освобожден GC и си код засегфолтится.

владеет С сторона

Если нечто освободиться на стороне С то нужно вызвать над ним фукцнию GC_ref чтобы пометить ссылку как используемую, тогда GC не станет ее освобождать, и вы не получите внезапный сегфолт со стороны С.

Если на стороне си написаны свои malloc/free функции для конкретной структуры, чтобы GC использовал их достаточно забиндить эти функции как =destroy на стороне nim

владеет Nim сторона

Если ссылкой владеет ним сторона то нужно вызвать над ней GC_unref, тогда объект освободиться текущим GC, но разумеется не в текущий момент, а когда станет мертвой/недостижимой, это не аналог free.

ObjC

Есть importObjC прагма которая работает по тем же принципам что и C++

NodeJS

nodejs обновлена 2 дня назад Альтернативная STD для nim использующая функции из node. Дело в том что во время генерации JS кода ним также генерирует свою STD, но на у ноды уже есть своя STD.

Существует либа AndroidJS позволяющая билдить приложения для андройда на html, js, она предоставляет окружение ноды и все нативные апи вроде звонков, нотификаций, камеры.

Python(еще раз)

cpython автор std nodejs std сделал тоже самое с Python

Видео пример использования либы turtle из Nim(черепашка для детей учащих программирования) https://juancarlospaco.github.io/cpython/turtle

Java

jnim Автор nimpy сделал тоже самое для Java

import jnim

# Import a couple of classes
jclass java.io.PrintStream of JVMObject:
  proc println(s: string)
jclass java.lang.System of JVMObject:
  proc `out`: PrintStream {.prop, final, `static`.}

# Call!
System.`out`.println("This string is printed with System.out.println!")

JS

В std имеются модули для std, макросы для async await

  • asyncjs Types and macros for writing asynchronous procedures in JavaScript.
  • dom Declaration of the Document Object Model for the JS backend.
  • jsbigints Arbitrary precision integers.
  • jsconsole Wrapper for the console object.
  • jscore The wrapper of core JavaScript functions. For most purposes, you should be using the math, json, and times stdlib modules instead of this module.
  • jsffi Types and macros для простого использования JavaScript библиотек из ним.

importjs прагма которая работает также как остальные import прагмы proc jq(selector: JsObject): JsObject {.importjs: "$$(#)".}

Экосистема

библиотеки

STD

STD нима вдохновлена Python и включает батарейки. Я не знаю Python но мне кажется что она слишком избыточна. Там есть драйвера к sqlite, PostgreSQL, mySql, нет монги, зачем то есть SMTP, и недавно я нашел там парсер языка sql. В версии 2.0 планируется привести это все в порядок, разбить на более мелкие модули, например сейчас модуль os включает в себя

кейсы продакшн использования

Organizations-using-Nim

Ссылки про вирусы

https://github.com/nim-lang/Nim/wiki/Unofficial-FAQ#why-is-it-caseunderscore-insensitive

Status

Хайрят C, C++, Go, Rust девелоперов, 1 месяц до продуктивности нужен минимум 1 ментор, много знаний скрыто в баг репортах и RFC Основными преемуществами считают:

  • очень простое использование С/С++ библиотек с автогенерацией биндов через c2nim или nimterop, требующий минимума ручных изменений.
  • Скорость и использование памяти, нет ничего лучше чем отзывы клиентов, один из них рассказал что использует разбери пай и после сравнения существующих решений Nimbus(Ethereum 2.0 blockchain) дает лучшие результаты, всего 700 мегабайт рам, это может показаться большим числом для обычных приложений, но для блокчейна это очень мало, он включает в себя кеши, историю месяцов транзакций и сложную логику для работы с сетью и блокчейн консенсуса
  • Возможность портировать Python код практически 1 к 1 Имплементация на ниме читаема для код аудиторов(код Status'а проходит аудит в трех независимых компаниях)

Что именно в компании написано на Nim

  • Nimbus - Реализация клиента Etherium 1 и 2, https://youtu.be/5wljNaPkU7M
  • LibP2P - P2P network stack используемый в блокчейнах, ним является одиной из официальных реализаций, Статус спонсируется за это
  • Waku - P2P messaging protocol - форк чего то что было начато на го переписанное на ним
  • Status Desktop client - раньше был написан на реакт нейтиве qt и go, теперь это nim + qt + qml
  • некоторые части стд, где нужна повышенная безопасность например модуль для работы с битами, это типичная практика, гугл имеет свою стд для плюсов abseil, facebook - folly

В статусе работает около 30ти человек на ниме

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

На что похожа криптовалютная индустрия

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

Тк кк код статуса проходит аудит у трех компаний Статус написали свой гайд для аудиторов, из него можно узнать много интересных вещей о внутреннем устройстве нима https://nimbus.guide/auditors-book/01_introduction.html

стартап геймбой игры

работает на геймбое, свитче и пк https://www.kickstarter.com/projects/penguinrik/goodboy-galaxy-exploration-platform-game-gba-pc-and-switch

тулинг

Коммьюнити

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment