Comments 23
Какой язык протестировать следующим? GoLang? Python?
brainfuck
Требуется высокая когнитивная нагрузка и внимательное управление borrow/clone/Weak
Мне интересно а что мешало скрыть все эти свистоплясания за методами? Rc::downgrade превратился бы в вызов .into(), а то и вовсе можно было бы обойтись сделав constraint а ля Rc<impl CardItem>. Да и builder pattern неплохо бы сократил необходимость в ассертах.
Какой язык протестировать следующим? GoLang? Python? Или отказаться от своего принципа не рекламировать Аргентум, и показать этот пример на нем?
Предлагаю попробовать Odin и его модель композиции. Предложил бы попробовать Jai, но его компилятор сложно добыть. А вообще было бы неплохо иметь рефенсную имплементацию на Аргентуме, чтобы понимать к чему надо стремиться.
Отдельно хотелось бы попросить сделать репозиторий с вашими имплементациями на разных языках.
Добавил табличку для сравнения Zig, Odin, Jai, GoLang, Python, The V.
Все перечисленные языки попали в две категории - с ручным управлением и с GC.
Все языки построенные на сборщике мусора будут иметь реализацию DOMа аналогичную моему предыдущему примеру на JS. А все языки с ручными управлением памятью зарание и гарантированно уступают всему ранее рассмотренному.
При этом я не говорю, что эти языки плохие. Просто сфера их приложения находится не в области DOM-задач.
По факту остались неохваченными только Nim и Dlang. С шарпом, го и явой все очевидно.
Свифт, котлин - неинтересно.
Аргентум тоже можно посмотреть и этот еще - Шрек-язык "зю", или как его там, что @panzershrek изобрёл=)
Есть ещё такая экзотика Cone
А чем D так принципиально отличается от тех же плюсов? Каких-то новшеств кастельно наследования и полиморфизма там нет. Nim кажется от JS тоже не сильно ушёл и не предлагает в этом плане тоже ничего нового. Pony тоже выглядит похожим на C#/F#. Акторная модель там по заветам Кэя с сигналами, но для DOM-like это вроде не сильно помогает.
D принципиально отличается от плюсов более человеческими шаблонами, основной технологией аллокации GC и акцентом на безопасность. Хотя и Arc/RAII и стековые аллокации и ручное управление памятью, а также работа с указателями остаются доступными по выбору разработчика, в отличие от большинства ЯП с GC.
Nim же имеет очень интересный выбор автоматических аллокаторов. Возможно, для DOM это будет удобно. Мне он кажется ближе к Питону, чем к JS.
Напишите CardDOM на любом из этих языков и покажите их сильные стороны.
Плюсы - код 1:1 с идеологией С++, но чище без шаред пойнтеров и связанного с этим кода. Чуть по другому сделал копирование, но непринципиально, только что больше бойлерплейта.
Минусы как и в JS - cсылки просто так не оборвешь. Нужно или городить двусвязную ссылку с подписчиками, либо рекурсивно зачищать, либо при обращении по коннектору проверять, что владелец еще указан.
Проблемы - надо по иерархии объектов приводить типы аккуратно или проверять, что же за объект мы получили. Это касательно вариантов doc.items[0].items[1].to. Иначе получаем обращение по нулевому указателю.
Собственно, то же самое будет в C# итп. Чуть может красивее можно отслеживать проблемы приведения через ?=
Собственно, подумалось, что такая реализация DOM слишком прямолинейная. На практике при существенном кол-ве дочерних объектов вместе паутины ссылок проще было хранить их в каком-нибудь дженерик массиве владельца.
Огромное спасибо за код и анализ! Результат похож на JS-код, что естественно, т.к. те же плюсы и минусы GC. Непосредственно реализация 150 строк, примерно как в C++, но если добавить ручной трекинг преекрестных ссылок, то кода станет примерно как в JS.
Есть ряд вопросов: auto hello_text = cast(TextItem)(doc.items[0].items[0]);
это cast без рантайм-проверки?
alias CardItem = DomElement; // <-это все-таки новый тип или прозрачный алиас?Если последнее, у нас дыра в типах - можно хранить документ в карточке.
При существенном кол-ве дочерних объектов вместе паутины ссылок проще было хранить их в каком-нибудь дженерик массиве владельца
Дочерние объекты не кмегда хранятся в одной коллекции, они играют разные роли и хранятся в разных полях - скалярных и коллекциях, они часто разнотипные. Например, в ворд-документе есть коллекция страниц, шаблонов, стилей, дочерний объект с метаданными. Не хранить же все это в одной коллекции.
Аналогично, слабые ссылки из этого объекта наружу тоже различаются - по ролям, типам и арности. Их сложно хранить в одном массиве.
для подобной задачи Lisp / Scheme реализация хорошо подойдет вероятно.
Racket / Typed Racket или для перфоменса что то компилируемое;
Bigloo, Chez Scheme, CHICKEN, Gambit, Guile
А все языки с ручными управлением памятью зарание и гарантированно уступают всему ранее рассмотренному.
Слишком смелое утверждение, так как цели в задаче могут быть разными - к примеру, с упором на перформанс по скорости или эффективность по памяти, а не только на простоту реализации или безопасность. Да и в последнем случае есть различия в способах избегания того же разыменовывания нулевых указателей - где-то надо руками следить за этим, как в плюсах, а где-то позаботились на уровне языка, как в том же Rust.
А вообще было бы неплохо иметь рефенсную имплементацию на Аргентуме, чтобы понимать к чему надо стремиться. Отдельно хотелось бы попросить сделать репозиторий с вашими имплементациями на разных языках.
сделаю
ручное жонглирование бесконечными borrow/drop/upgrade/clone тратит больше времени и сил, чем собственно решение задачи.
Вызовы borrow стоит спрятать в методы реализации, погрузив Rc<RefCell> в обёртку, чтобы потроха не торчали на клиенте. - Тогда и drop не понадобится. А upgrade это аналог lock в C++, так что здесь странная претензия именно к Rust, а clone - явное дешёвое копирование shared pointer-а - это как раз базовая вещь в Rust - явное клонирование в противовес неявному в плюсах.
Если, как полагается, прятать детали реализации, то можно не трогая клиент заменять реализацию на другие варианты, например, на арену с хендлами вместо указателей.
А выставить из обёртки удобно методы, либо возвращающие обёрнутые объекты с shared pointer внутри, либо принимающие коллбэк в виде замыкания для чтения либо модификации сложных объектов - например, для чтения или модификации стиля, что у вас делалось через style.clone_resized(style.size + 1.0).
Это показывает противоречие в философии языка:
безопасность без падений остается недостижимым идеалом,
В чём же противоречие? Rust не декларирует безопасность без паник, хотя и без них тоже можно. Паники обычно применяются как раз для недопущения UB либо при нарушении инвариантов.
Спасбо за подробные советы как нужно делать. Пожалуйста покажите код переделанный в сответствии с этими советами. Это позволит сравнить плюсы и минусы разных подходов.
Паники обычно применяются как раз для недопущения UB либо при нарушении инвариантов.
Когда приложение падает, и моя машина становится неуправляемой на хайвее, или когда я теряю с трудом написанный документ или не могу провести презентацию или заказать билеты на самолет, мне все равно, происходит это из-за segfault или из-за паники в RefCell::drop. Этого и есть отсутствие безопасности, этого просто не должно происходить. И Раст с этим обеспечением безопасности не справляется.
CardDOM на Rust: через лайфтаймы и боль