Девиз: Efficient, Expressive, Elegant где под эффективностью понимается скорость С, под выразительностью синтаксис Python, а под элегантностью метапрограммирование Lisp https://nim-lang.org/
Под структурной парадигмой автор понимает что у вас есть один вход в блок кода и один выход. То есть использовать return
, break
, continue
не идиоматично
Так например внутри цикла вместо continue по условию можно инвертировать условие избавившись от continue(но увеличив вложенность)
https://youtu.be/D_G9h7DcIqM?t=1095
Во всех функциях неявно объявлена переменная result которая является возвращаемым значением, это также способствует соблюдению принципа один вход один выход control flow функций.
Отдается предпочтение использованию value типов, с ними control flow выглядит предсказуемее
person = newPerson(name: "Alice")
otherPerson = person
person.renameTo "Bob"
assert otherPerson.name == "alice"
Ну и выделение памяти на стеке куда быстрее чем на куче.
Нно у value типов есть один минус, обычно они постоянно копируются, при передаче в функцию, при присваивании, при возврате
TODO и разумеется функции являются объектами первого класса.
TODO
TODO
Иммутабельность не shallow как в большенстве языков, а более настоящая.
Например если в шарпах создать массив через const, в него все равно можно будет записывать данные, тоже самое с объектами и их полями, константная только ссылка.
В ниме такое не пройдет, var является модификатором типа, функции добавления в массив объявлены для var версии, поэтому для коснтантной просто не найдется append функции. Но ничто не мешает создать мутабельную ссылку на иммутабедбную переменную, и это даже является неким паттерном.
Araq говорит что мутабельность это конечно ужасно, но только в тех случаях когда мьютабл стейт расшарен, если же он является локальным то в этом нет ничего плохого.
Что куда более свойственно функциональным языкам, но в последнее время набирает популярность и в остальных. И да в Nim как и в Kotlin нет тернарного оператора, вместо него предлагается использовать if
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, в отличии от скалы)
использование when вместе с isMainModule TODO
TODO Разделение ссылко на управляемые и неуправляемые с помощью ref, ptr что сильно облегчает работу с низкоуровневым кодом.
acyclic-pragma https://nim-lang.org/docs/manual.html#pragmas-acyclic-pragma TODO
Хоть у нима это и не основная парадигма, ним полностью ее поддерживает.
С ООП у нас появилось няшное автодополнение глаголов за существительными, когда поставив точку мы видим список всех доступных методов которые можно сделать с этим объектом, даже не нужно лезть в доку, сейчас это воспринимается как данность, но раньше было одним из продающих джаву пунктов.
Хоть в ниме и нельзя добавить метод непосредственно в тип(что есть хорошо), но эта фича дается при помощи UFCS
TODO
TODO
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.
Или например функция должна возвращать строку, но внутри вы делали какие то вычисления и случайно забыли вернуть результат. Никакой валидации этого не будет, функция будет возвращать пустую строку.
TODO Вот тут классные примеры https://nim-lang.org/docs/manual.html#types-distinct-type
TODO
TODO Минусы: можно попробовать получить доступ к любому полю варианта в любой момент, если этот вариант при этом является другой веткой, это приведет к вылету в рантайме
switch должен обработать все варианты, иначе это ошибка компиляции.
Это очень приятно при работе с ADT, например при добавлении нового варианта сразу загорятся красным все места где он не обработан.
Минусы: нет тайп гвардов или они в недоделанном состоянии, например есть прагма которая фиксит проблему доступа к полям вариантов, но она работает только для switch экспрешона, то есть внутри switch кейса доступ к неправильному полю будет ошибкой компиляции, а внутри ифа нет. Это конечно просто недопиленность, прагма находится в разделе экспериментальных фичь и скорее всего будет исправлена.
TODO
TODO
TODO https://nim-lang.org/docs/manual.html#threads