Pull to refresh

Comments 23

Какой язык протестировать следующим? GoLang? Python?

brainfuck

Требуется высокая когнитивная нагрузка и внимательное управление borrow/clone/Weak

Мне интересно а что мешало скрыть все эти свистоплясания за методами? Rc::downgrade превратился бы в вызов .into(), а то и вовсе можно было бы обойтись сделав constraint а ля Rc<impl CardItem>. Да и builder pattern неплохо бы сократил необходимость в ассертах.

Вы всегда можете написать и показать свой более хороший вариант. Или еще проще - взять мой 330-строчный код по ссылке и отрефакторить его в соответствии с вашими предложениями. Это позволит одновременно показать более идеоматический подход и сравнить плюсы и минусы разных вариантов.

Какой язык протестировать следующим? 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 на любом из этих языков и покажите их сильные стороны.

Переписал на D.

Плюсы - код 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; // <-это все-таки новый тип или прозрачный алиас?Если последнее, у нас дыра в типах - можно хранить документ в карточке.

Тут cast() это аналог dynamic_cast<>, только будет null а не исключение.

alias CardItem = DomElement; это просто алиас. Хранить - да, выйдет =). Вопрос скорее проектирования иерархии.

При существенном кол-ве дочерних объектов вместе паутины ссылок проще было хранить их в каком-нибудь дженерик массиве владельца

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

Аналогично, слабые ссылки из этого объекта наружу тоже различаются - по ролям, типам и арности. Их сложно хранить в одном массиве.

Добавил вышеперечисленные языки в статью. Кроме Ü т. к. из его документации не ясно, как он работает с иерархиями объектов в хипе. Буду рад если для него Card DOM реализует сам автор языка.

для подобной задачи Lisp / Scheme реализация хорошо подойдет вероятно.

Racket / Typed Racket или для перфоменса что то компилируемое;

Bigloo, Chez Scheme, CHICKEN, Gambit, Guile

А все языки с ручными управлением памятью зарание и гарантированно уступают всему ранее рассмотренному.

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

Я говорю про задачу CardDOM, с жестко прописанными условиями и критериями оценки.

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

сделаю

ручное жонглирование бесконечными 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. Этого и есть отсутствие безопасности, этого просто не должно происходить. И Раст с этим обеспечением безопасности не справляется.

Sign up to leave a comment.

Articles