Comments 51
Когда я первый раз увидел bin, у меня первый раз возникло желание написать свой язык. Там были просто гениальные идеи, и были такие как синтаксис вызова методов. Когда метод может быть вызван четырьмя вариантами, пример есть в почте автора, у вас будет четыре не пересекающихся группы программистов на нем. Почему это плохо.
Вспомните почему заказали Perl.
Почему не пересекающихся? :) Я в проекте использую 3 из 4 перечисленных.
Ну например для логирования я не использую скобки:
logging.debug "Oops" # better
logging.debug("Oops") # worse but not restricted
"Oops".debug() # wat
"Oops".debug # insane
Type convertion удобно писать так:
someProc(10.unit8) # better
someProc(unit8(10)) # worse but not restricted
Для методов (т.е. процедур, которые относятся к какому-то объекту) я использую первое:
someObject.someProc(arg1: 5, arg2: 10) # better
someProc(someObject, arg1: 5, arg2: 10) # worse but not restricted
Т.е. правило "пиши как лучше читается". Хорошо это или плохо — пока не знаю, но более склоняюсь к "строгому" языку, чем к таким вольностям.
Т.е. правило «пиши как лучше читается». Хорошо это или плохо — пока не знаю, но более склоняюсь к «строгому» языку, чем к таким вольностям.
А для меня это киллер-фича. :) Самое главное для меня — это как логично(и быстро) читается исходник.
И по поводу отступов — вот тут как раз «строгость» в тему. Это тоже в плюс читаемости.
Это называлось UFCS в ниме раньше. Потом они стали называть это Method Call Syntax. Потому что могут ¯_(ツ)_/¯
Сильвупле (имхо):
- Быстрая компиляция в быстрый нативный код
- Доступна вся экосистема C/C++
- Простой в освоении
- Позволяет писать код любой сложности
Если С++ ругают за медленную компиляцию, то как компиляция Nim (которая ведь транспиляция в С++ сперва) может быть быстрой?
В этом плане, увы, я могу оперировать только собственным опытом — деталей реализации я не знаю. Разумеется, компиляция не может быть быстрее копиляции C++ кода, но, во-первых, есть кэш компиляции, так что вы не будете пересобирать мир при небольших изменениях, а во-вторых, одно дело когда C++ код пишете вы для себя (вам его ещё читать потом), а другое дело когда это делает транспайлер и он может оптимизировать как хочет:
struct tySequence__K284t8D0DApfLbw9c7cpnKw {
NI len; tySequence__K284t8D0DApfLbw9c7cpnKw_Content* p;
};
struct NimStrPayload {NI cap;
AllocatorObj* allocator;
NIM_CHAR data[SEQ_DECL_SIZE];
};
struct NimStringV2 {NI len;
NimStrPayload* p;
};
typedef N_NIMCALL_PTR(void, tyProc__l0xby6CKnyVDN9bJs3WwRgw) (NI16 entity);
typedef NU8 tyEnum_Level__pW4mH4lipH6u2NKDGEWdGg;
typedef NimStringV2 tyArray__nHXaesL0DJZHyVS07ARPRA[1];
struct TNimType {void* destructor;
NI size;
NCSTRING name;
void* traceImpl;
void* disposeImpl;
};
Но это не точно.
Позволяет писать код любой сложности
Любой тьюринг-полный язык позволяет писать код любой сложности
Итак, у нас есть функции, процедуры, дженерики, мультиметоды, шаблоны и макросы. Когда лучше использовать шаблон, а когда процедуру? Шаблон или дженерик? Функция или процедура? Так, а макросы?
Функции, процедуры, дженерики, мультиметоды, шаблоны, макросы.
Именно в таком порядке по приоритету. Если первого пункта недостаточно для реализации необходимого функционала, переходим к следующему и так далее. К сожалению, не могу вспомнить где в документации видел приоритетность, но она была очень похожа на эту. Разве что мультиметодов и функций там не было. Мультиметоды я бы вообще исключил, поскольку они почти всё время были сломаны.
Наверно, всё так, но ещё не забывайте, что шаблоны работают как подстановка, в отличие от процедур, поэтому теоретически они быстрее — нету прыжка в функцию и передачи параметров. Плюс иногда код сильно упрощается.
Например, сначала у меня был бесконечный цикл (gamedev, как-никак), который был функцией, принимающей коллбек:
proc loop(frequency: float, callback: proc(dt)) =
# ...
var dt = ...
callback(dt)
proc doSmth(dt: float) =
echo &"{dt} seconds passed in this loop iteration"
loop(30, doSmth)
А потом я вдруг понял, что с шаблоном будет проще и быстрее:
template loop(frequency: float, code: untyped) =
# ...
var dt {.inject.} = ...
code
loop(30) do:
echo &"{dt} seconds passed in this loop iteration"
К сожалению, поддержка от Status проекта nim не вечна. Работал там, в течение последнего года компания оптимизируется и оптимизируется, судя по финансам, в новом году оптимизируется окончательно.
И это определённая проблема nim. Интересный язык, но нужна поддержка компаний.
В точку. Сравнивая с rust, я прям вижу, как проекту не хватает внимания, как со стороны разработчиков, так и со стороны компаний.
я сколько ни смотрел на ним, я не понял, зачем он. точка.
нужен понятный таргетинг и обоснование
Ну, знаете… Зачем плюсы?
Для каждой области нужен свой тулинг, библиотеки
Скажу так: ним может всё, что могут C и C++. По собственному опыту не могу сказать, что есть прям какая-то ниша — nim это general purpuse.
Desktop — да, есть несколько библиотек для гуя, но можно написать свой враппер для любой либы из c/c++.
Web — есть jester и karax (фреймворки на ниме), есть всякие сокеты и вебсерверы в стандартной библиотеке. Но можно написать свой враппер для любой либы из c/c++.
Эмбеддед — можно, там надо спец флаги указать.
Геймдев — можно, пишу сейчас сам. Ядро на ниме, графика — на ogre3d или sdl, ввод и окна — на sdl, сеть — на enet (но есть и сетевые библиотеки на pure nim).
Расчёты — есть куча всего, типа arraymancer, neo и что-то ещё. Но можно написать свой враппер для… Вы поняли :)
Конечно, хотелось бы видеть всё на чистом ниме, но это утопия, для этого нужно большое комьюнити, которое это всё напишет, а чтобы было большое комьюнити, нужно чтобы много всего было написано.
Вот эта картинка с их сайта на самом деле сделана с отключенным сборщиком циклов, по сути это просто reference counting. Я понимаю что технически это тоже «сборка мусора», но нельзя просто одно заменять на другое, нужно чтобы программы изначально писалась с учетом этого.
Сейчас автор языка решил всё таки сделать reference counting основным режимом, но из-за этого почему-то отвалилось куча всего и вылезло дикое количество багов. Очень странно называть язык в таком состоянии 1.0.
Стоит отметить, что в версии 1.0 по умолчанию старый сборщик мусора, с которым всё норм. Новый сборщик ещё не пришёл на замену, он включается опционально. Проблема именно в количестве сборщиков мусора (при том они разные: shared heap vs per-thread heap), и в том, что сейчас появился ещё один, вместо того чтобы делать реально полезные вещи.
В ARC есть shared-heap, не нужно каких-то setupForeignThreadGc или похожих для работы с C библиотеками (или наоборот, если нужно писать shared библиотеку на Nim), ARC намного лучше для embedded.
Пока что планируется, что в 1.4 (может и позже, не знаю) ORC станет дефолтным GC (но это произойдёт как минимум тогда, когда сам компилятор будет работать с ARC/ORC и все популярные библиотеки).
Ещё с ARC возможно больше потенциальных оптимизаций благодаря анализу использования данных, к примеру вот самый недавний PR насчёт создания оптимизатора для ARC — github.com/nim-lang/Nim/pull/14962
Больше информации — www.youtube.com/watch?v=aUJcYTnPWCg (презентация Andreas'а с NimConf об ARC/ORC), forum.nim-lang.org/t/5734 (там в постах много интересного), nim-lang.org/docs/destructors.html про деструкторы (они являются важной частью ARC)
Самое яркое впечатление от моего опыта с nim: ты пытаешья сделать что-то незадокументированное (или просто совершаешь ошибку) и в ответ получаешь километровый стектрейс… который абсолютно нечитаем и абсолютно не помогает в решении проблемы. И приходится разбираться в этом самом "фрактале сложности", не всегда с успехом.
Следствие от того же самого: если какая-то фича в библиотеке не задокументирована, то понять, как её использовать можно только с очень большим трудом (исходники не особо-то и помогают).
но вообще я просто хочу сесть и ехать (причём быстро) на вечеринку. Машина — не цель, а средство достижения цели.
…
но от этого у меня отваливается багажник. И знаете что? Мне всё равно чертовски нравится эта машина, ведь это лучшая из всех машин, что я видел.
Но приходится бросать ее на обочине и ехать на вечеринку на другой. :)
Ох и намучился я тоже с глюками Nim. Даже при том, что очень хочется этот язык использовать, вышеупомянутые минусы заставили его отложить в сторону.
Есть шаблоны (templates) — механизм замены, но не такой блевотный, как в C++ (там это всё ещё просто текстовая замена, или уже что-то поумнее?).
Так, а чем плохи шаблоны в плюсах? Имхо явно лучше, чем в этом вашем nim, где можно пихать что угодно куда угодно.
Вообще от nim осталось впечатление какого-то франкенштейна, который сам не знает чем является.
1. Временем компиляции
2. Негибкостью (хороши только если одну функцию надо написать для нескольких типов, но как только начинают решаться проблемы из реального мира, приходится изобретать монструозную конструкцию из шаблонов, а то и вообще откатываться к макросам из C)
3. Сообщениями об ошибках (без парсера в них можно утонуть)
В Nim метапрограммирование на порядок лучше плюсового реализовано.
Я извиняюсь, кажется, я налажал в терминологии. Я имел ввиду механизм подстановки, когда пишешь #define one two, и препроцессор не глядя заменяет одно на другое. В Ниме templates — шаблоны — как раз делают постановку кода, вот меня и переклинило.
Если я всё правильно понял, то шаблоны из c++ — это generic в ниме.
В C++ шаблоны позволяют подставлять в код типы или значения времени компиляции (constexpr).
Для подстановки кода в функцию, как в нимовских шаблонах, в плюсах передают аргументом лямбду, компиляторы довольно неплохо умеют такой код оптимизировать.
Макросы в плюсах приходится использовать чаще всего для генерации определений функций или типов. В Nim, если не ошибаюсь, макросы для того же используют.
Ни про какую стандартную либу я не слышал, вроде просто компилируется в c (или cpp) и оно самодостаточно.
По поводу размера — нет предела совершенству: https://hookrace.net/blog/nim-binary-size/
Последний раз, когда я смотрел Nim, там не было ни интерфейсов, ни трейтов. Для их эмуляции предлагалось руками собирать структуру из нужных замыканий (найдено в недрах форума, ссылку сейчас не найду). Также несколько странное решение для discriminated unions в виде явно выделенного поля под дискриминант.
Как с этим сейчас?
Интерфейсов всё так же нет (ну или, как вы и говорили, собирать из замыканий, что я считаю извращением). Зато есть концепты, там много всего: https://nim-lang.org/docs/manual_experimental.html#concepts
Вообще не вижу проблемы с полем под дискриминант, даже не представлял что можно по-другому. А что не так?
Мой опыт разработки на языке Nim