Первая статья про dap, очевидно, не стала моим писательским успехом: подавляющее большинство коментов к ней свелись к «ниасилил» и «ниасилил, но осуждаю». А приз за самый единственный конструктивный комментарий верхнего уровня достается OldVitus, за совет продемонстрировать dap на примере TodoMVC, чтобы было с чем сравнить. Чем я в этой статье и займусь.

TodoMVC, если кто не знает, это такой стандартный UI-хелловорлд, позволяющий сравнить решения одной и той же задачи — условного «Списка дел» — средствами разных фреймворков. Задачка, при всей своей простоте (ее решение на dap влезает «в один экран»), весьма иллюстративна. Поэтому на ее примере я попробую показать, как типичные для веб-фронтенда задачи реализуются с помощью dap.

Искать и изучать формальное описание задачи я не стал, а решил просто среверсить один из примеров. Бэкенд в рамках этой статьи нам не интересен, поэтому сами мы его писать не будем, а воспользуемся одним из готовых с сайта www.todobackend.com, оттуда же возьмем и пример клиента и стандартный CSS-файл.

Для использования dap вам не нужно ничего скачивать и устанавливать. Никаких npm install и вот этого всего. Не требуется создавать никаких проектов с определенной структурой каталогов, манифестами и прочей атрибутикой IT-успеха. Достаточно текcтового редактора и браузера. Для отладки XHR-запросов может еще потребоваться веб-сервер — достаточно простейшего, типа вот этого расширения для Chrome. Весь наш фронтенд будет состоять из одного-единственного .html-файла (разумеется, ссылающегося на скрипт dap-движка и на стандартный CSS-файл TodoMVC)

Итак, с чистого листа.

1. Создаем .html файл


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Todo -- dap sample</title>
  <link rel="stylesheet" href="https://www.todobackend.com/client/css/vendor/todomvc-common.css"/>
  <script src="https://dap.js.org/0.4.js"></script>
</head>

<body>
<script>
// здесь будет dap
</script>
</body>

</html>

Обычная html-заготовка, в которой подключаем CSS-файл, любезно предоставляемый сайтом www.todobackend.com и dap-движок, не менее любезно предоставляемый сайтом dap.js.org

2. Копируем DOM-структуру оригинального примера


Чтобы пользоваться стандартным CSS-файлом без переделок, будем придерживаться той же DOM-структуры, что и оригинальный пример. Открываем его в браузере Chrome, жмем Ctr+Shift+I, выбираем вкладку Elements и видим, что собственно приложение находится в элементе section id="todo-app">



Последовательно раскрывая это поддерево, переписываем его структуру в наш .html файл. Сейчас мы просто срисовываем по-быстренькому, а не пишем код, поэтому просто пишем сигнатуры элементов в 'одинарных кавычках', а в скобках их детей. Если детей нет — рисуем пустые скобочки. Следим за индентами и балансом скобок.

// здесь будет dap

'#todoapp'(
  '#header'(
    'H1'()
    'INPUT#new-todo placeholder="What needs to be done?" autofocus'()
  )
  '#main'(
    '#toggle-all type=checkbox'()
    'UL#todo-list'(
      'LI'(
        'INPUT.toggle type=checkbox'()
        'LABEL'()
        'BUTTON.destroy'()
      )
    )
  )
  '#footer'(
    '#todo-count'()
    'UL#filters'(
      'LI'()
    )
    '#clear-completed'()
  )
)

Oбратите внимание: повторяющиеся элементы (например, здесь это элементы LI) мы пишем в структуру по одному разу, даже если в оригинале их несколько; очевидно, что это массивы из одного и того же шаблона.

Формат сигнатур, думаю, понятен любому, кто писал руками HTML и CSS, поэтому останавливаться на нем подробно пока не буду. Скажу лишь, что теги пишутся ЗАГЛАВНЫМИ буквами, а отсутствие тега равносильно наличию тега DIV. Обилие здесь #-элементов (имеющих id) обусловлено спецификой подключаемого CSS-файла, в котором используются в основном как раз id-селекторы.

3. Вспоминаем, что dap-программа — это Javascript


Чтобы избавить нас от лишних скобочек в коде, dap-движок внедряет прямо в String.prototype несколько методов (я в курсе, что внедрять свои методы в стандартные объекты — это айяйяй, но… короче, проехали), которые преобразует строку-сигнатуру в dap-шаблон. Один из таких методов — .d(rule, ...children). Первым аргументом он принимает правило генерации (d-правило), и остальными аргументами — произвольное число чайлдов.

Исходя из этого нового знания, дописываем наш код так, чтобы вместо каждой открывающей скобки у нас была последовательность .d("", а перед каждой открывающей одинарной кавычкой, кроме самой первой, была запятая. Лайфхак: можно воспользоваться автозаменой.

'#todoapp'.d(""
  ,'#header'.d(""
    ,'H1'.d("")
    ,'INPUT#new-todo placeholder="What needs to be done?" autofocus'.d("")
  )
  ,'#main'.d(""
    ,'#toggle-all type=checkbox'.d("")
    ,'UL#todo-list'.d(""
      ,'LI'.d(""
        ,'INPUT.toggle type=checkbox'.d("")
        ,'LABEL'.d("")
        ,'BUTTON.destroy'.d("")
      )
    )
  )
  ,'#footer'.d(""
    ,'#todo-count'.d("")
    ,'UL#filters'.d(""
      ,'LI'.d("")
    )
    ,'#clear-completed'.d("")
  )
)

Вуаля! Мы получили дерево вызовов метода .d, которое уже готово трансформироваться в dap-шаблон. Пустые строки "" — это зародыши будущих d-правил, а чайлды стали перечисленными через запятую аргументами. Формально, это уже валидная dap-программа, хоть пока и не совсем с тем выхлопом, который нам нужен. Но ее уже можно запустить! Для этого после закрывающей корневой скобки дописываем метод .RENDER(). Этот метод, как понятно из его названия, рендерит полученный шаблон.

Итак, на данном этапе имеем .html-файл вот с таким содержанием:

<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
  <title>Todo -- dap sample</title>
  <link rel="stylesheet" href="https://www.todobackend.com/client/css/vendor/todomvc-common.css"/>
  <script src="https://dap.js.org/0.4.js"></script>
  </head>

  <body>
    <script>

'#todoapp'.d(""
  ,'#header'.d(""
    ,'H1'.d("")
    ,'INPUT#new-todo placeholder="What needs to be done?" autofocus'.d("")
  )
  ,'#main'.d(""
    ,'#toggle-all type=checkbox'.d("")
    ,'UL#todo-list'.d(""
      ,'LI'.d(""
        ,'INPUT.toggle type=checkbox'.d("")
        ,'LABEL'.d("")
        ,'BUTTON.destroy'.d("")
      )
    )
  )
  ,'#footer'.d(""
    ,'#todo-count'.d("")
    ,'UL#filters'.d(""
      ,'LI'.d("")
    )
    ,'#clear-completed'.d("")
  )
)

.RENDER() // рендерим полученный dap в документ

    </script>
  </body>
</html>

Можно открыть его в браузере, чтобы убедиться, что DOM-элементы генерятся, CSS-стили применяются, осталось только наполнить этот шаблон данными.

4. Получаем данные


Идем на страничку-оригинал, открываем в инструментах вкладку Network, включаем фильтр XHR, и смотрим, откуда берутся данные, и в каком виде.





Окей, понятненько. Список дел берется прямо из todo-backend-express.herokuapp.com в виде json-массива объектов. Замечательно.

Для получения данных в dap имеется встроенный конвертор :query который асинхронно «конвертирует» URL в данные, с него полученные. Сам URL мы не будем писать прямо в правиле, а обозначим его константой todos; тогда вся конструкция по добыче данных будет выглядеть так:

todos:query

а саму константу todos пропишем словаре — в секции .DICT, прямо перед .RENDER():

'#todoapp'.d(""
  ...
)
.DICT({
  todos  : "https://todo-backend-express.herokuapp.com/"
})
.RENDER()

Получив массив todos, строим из него список дел: для каждого дела берем название из поля .title и пишем его в элемент LABEL, а из поля .completed берем признак «завершенности» — и пишем в свойство checked элемента-чекбокса INPUT.toggle. Делается это так:

    ,'UL#todo-list'.d("*@ todos:query" // Оператор * выполняет повтор для всех элементов массива
      ,'LI'.d(""
        ,'INPUT.toggle type=checkbox'.d("#.checked=.completed") // # обозначает "этот элемент"
        ,'LABEL'.d("! .title") // Оператор ! просто добавляет текст в элемент
        ,'BUTTON.destroy'.d("")
      )
    )

Обновляем эту нашу страничку в браузере и… если вы запускаете ее из файловой системы, то ничего не происходит. Проблема в том, что современные браузеры не разрешают кросс-доменные XHR-запросы из локальных документов.



Пришло время смотреть нашу страничку через http — с помощью любого локального вебсервера. Ну, или если вы пока не готовы писать dap своими руками, смотрите последовательные версии странички по моим ссылкам (не забывайте смотреть исходники — в Хроме это делается с помощью Ctrl+U)

Итак, заходим на нашу страничку по http:// и видим, что данные приходят, список строится. Отлично! Вы уже освоили операторы * и !, конвертор :query, константы и доступ к полям текущего элемента массива. Посмотрите еще раз на получающийся код. Он вам все еще кажется нечитаемым?

5. Добавляем состояние


Возможно, вы уже попробовали понажимать на галочки в списке дел. Сами галочки меняют цвет, но, в отличие от оригинала, родительский элемент LI не меняет свой стиль («завершенное дело» должно становиться серым и зачеркнутым, но этого не происходит) — дела не меняют свое состояние. А никакого состояния эти элементы пока и не имеют и, соответственно, не могут его менять. Сейчас мы это поправим.

Добавим элементу LI состояние «завершенности». Для этого определим в его d-правиле переменную состояния $completed. Элементу INPUT.toggle, который может это состояние менять, назначим соответствующее правило реакции (ui-правило), которое будет устанавливать переменную $completed в соответствии с собственным признаком checked («галка включена»). В зависимости от состояния $completed элементу LI будем либо включать, либо выключать CSS-класс «completed».

    ,'UL#todo-list'.d("*@ todos:query"
      ,'LI'.d("$completed=.completed"// Переменная состояния, инициализируем из поля .completed
        ,'INPUT.toggle type=checkbox'
          .d("#.checked=.completed") // Начальное состояние галочки берем из данных
          .ui("$completed=#.checked") // при нажатии обновляем $completed
        ,'LABEL'.d("! .title")
        ,'BUTTON.destroy'.d("")
      )
      .a("!? $completed") // в зависимости от значения $completed, включаем или выключаем css-класс completed
    )

Подобные манипуляции с CSS-классами — вещь довольно частая, поэтому для них в dap имеется специальный оператор !?
Обратите внимание, делаем мы это в а-правиле (от слова accumulate). Почему не в d-правиле? Отличие между этими двумя типами правил в том, что d-правило при обновлении полностью перестраивает содержимое элемента, удаляя старое и генеря все заново, тогда как a-правило не трогает имеющееся содержимое элемента, а «дописывает» результат к тому, что уже есть. Смена отдельного атрибута элемента LI не требует перестройки остального его содержимого, поэтому рациональней это делать именно в a-правиле.

Смотрим на результат. Уже лучше: нажатия на галочки меняют состояние соответствующего элемента списка дел, и в соответствии с этим состоянием меняется и визуальный стиль элемента. Но все еще есть проблема: если в списке изначально присутствовали завершенные дела — они не будут серенькими, т. к. по умолчанию a-правило не исполняется при генерации элемента. Чтобы исполнить его и при генерации, допишем в d-правило элемента LI оператор a!

      ,'LI'.d("$completed=.completed; a!" // Сразу же после инициализации переменной $completed используем ее в a-правиле

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

6. Редактирование названий дел


Вернемся к оригиналу. При двойном клике по названию дела включается режим редактирования, в котором это название можно поменять. Там это реализовано так, что шаблон режима просмотра «view» (с галкой, названием и кнопкой удаления) целиком прячется, а показывается элемент INPUT class="edit". Мы сделаем чуть иначе — прятать будем только элемент LABEL, т. к. остальные два элемента нам при редактировании не мешают. Просто допишем класс view элементу LABEL

Для состояния «редактирование» определим в элементе LI переменную $editing. Изначально оно (состояние) сброшено, включается по dblclick на элементе LABEL, а выключается при расфокусе элемента INPUT.edit. Так и запишем:

      ,'LI'.d("$completed=.completed $editing=; a!" // Теперь у нас две переменные состояния
      
        ,'INPUT.toggle type=checkbox'
          .d("#.checked=.completed")
          .ui("$completed=#.checked")
	  
        ,'LABEL.view'
          .d("? $editing:!; ! .title") // Если $editing сброшена, то показываем этот элемент
          .e("dblclick","$editing=`yes") // По dblclick включаем $editing
	  
        ,'INPUT.edit'
          .d("? $editing; !! .title@value") // Если $editing непустой
          .ui(".title=#.value") // обновляем .title по событию change (ui событие по умолчанию для INPUT)
          .e("blur","$editing=") // сбрасываем $editing по событию blur
	  
        ,'BUTTON.destroy'.d("")
      
      ).a("!? $completed $editing") // отображаем состояния $completed и $editing в css-классе элемента 'LI'

Теперь мы можем редактировать названия дел.

7. Отправка данных на сервер


Ок, в браузере мы дела редактировать уже можем, но эти изменения нужно еще и передавать на сервер. Смотрим, как это делает оригинал:



Внесенные изменения отправляются на сервер методом PATCH с неким URL вида http://todo-backend-express.herokuapp.com/28185, который, очевидно, является уникальным для каждого дела. Этот URL указывается сервером в поле .url для каждого дела, присутствующего в списке. То есть все, что от нас требуется для обновления дела на сервере — это отправить PATCH-запрос по адресу, указанному в поле .url, с измененными данными в формате JSON:

        ,'INPUT.edit'
          .d("? $editing; !! .title@value")
          .ui(".title=#.value; (@method`PATCH .url (@Content-type`application/json)@headers (.title):json.encode@body):query")
          .e("blur","$editing=") 

Здесь мы используем все тот же конвертор :query, но в более развернутом варианте. Когда :query применяется к простой строке, эта строка трактуется как URL и выполняется GET-запрос. Если же :query получает сложный объект, как в данном случае, он трактует его как детальное описание запроса, содержащее поля .method, .url, .headers и .body, и выполняет запрос в соответствии с ними. Здесь мы сразу после обновления .title отправляем серверу PATCH-запрос c этим обновленным .title

Но есть нюанс. Поле .url мы получаем от сервера, оно выглядит примерно так: http://todo-backend-express.herokuapp.com/28185, то есть в нем жестко прописан протокол http:// Если наш клиент тоже открыт по http://, то все нормально. Но если клиент открыт по https:// — то возникает проблема: по соображениям безопасности браузер блокирует http-трафик от https-источника.

Решается это просто: если убрать из .url протокол, то запрос будет проходить по протоколу страницы. Так и сделаем: напишем соответствующий конвертер — dehttp, и будем пропускать .url через него. Собственные конверторы (и прочий функционал) прописывается в секции .FUNC:

          .ui(".title=#.value; (@method`PATCH .url:dehttp (@Content-type`application/json)@headers (.title):json.encode@body):query")
...
.FUNC({
  convert:{ // конверторы - это функции с одним входом и одним выходом
    dehhtp: url=>url.replace(/^https?\:/,'')// удаляем протокол из URL
  }
})

Еще имеет смысл вынести объект headers в словарь, чтобы использовать его и в других запросах:

          .ui(".title=#.value; (@method`PATCH .url:dehttp headers (.title):json.encode@body):query")
...
.DICT({
  todos  : "//todo-backend-express.herokuapp.com/",
  headers: {"Content-type":"application/json"}
})

Ну и для полного фэншуя воспользуемся еще одним полезным свойством конвертора :query — автоматическим кодированием тела запроса в json в соответствии с заголовком Content-type:application/json. В итоге правило будет выглядеть так:

          .ui(".title=#.value; (@method`PATCH .url:dehttp headers (.title)):query")

Итак, смотрим. Окей, названия дел теперь меняются не только в браузере, но и на сервере. Но! Меняться-то может не только название дела, но и его состояние завершенности — completed. Значит, его тоже нужно отправлять серверу.
Можно элементу INPUT.toggle дописать аналогичный PATCH-запрос, просто вместо (.title) отправлять (.completed):

        ,'INPUT.toggle type=checkbox'
          .d("#.checked=.completed")
          .ui("$completed=#.checked; (@method`PATCH .url:dehttp headers (.completed:?)):query")

А можно вынести этот PATCH-запрос «за скобки»:

      ,'LI'.d("$completed=.completed $editing= $patch=; a!" // $patch - "посылка" для сервера
      
        ,'INPUT.toggle type=checkbox'
          .d("#.checked=.completed")
          .ui("$patch=($completed=#.checked)") // кладем в $patch измененный completed
	  
        ,'LABEL.view'
          .d("? $editing:!; ! .title")
          .e("dblclick","$editing=`yes")
	  
        ,'INPUT.edit'
          .d("? $editing; !! .title@value")
          .ui("$patch=(.title=#.value)") // кладем в $patch измененный title
          .e("blur","$editing=")
	  
        ,'BUTTON.destroy'.d("")
	
      )
      .a("!? $completed $editing")
      
      // если $patch не пустой, отправляем его серверу, потом сбрасываем
      .u("? $patch; (@method`PATCH .url:dehttp headers $patch@):query $patch=")

Тут дело вот в чем. Правила реакции относятся к группе «up-правил», которые исполняются «снизу вверх» — от потомка к родителю, до самого корня (эта последовательность может быть прервана при необходимости). Это чем-то похоже на «всплывающие» события в DOM. Поэтому какие-то фрагменты реакции, общие для нескольких потомков, можно поручить их общему предку.

Конкретно в нашем случае выигрыш от такого делегирования не особо заметный, но если бы редактируемых полей было больше, то вынос этого громоздкого (по меркам dap, конечно) запроса в одно общее правило сильно помог бы сохранять код простым и читабельным. Так что рекомендую.

Смотрим: Теперь на сервер отправляются и изменения названия, и изменения статуса.

В следующей статье, если будет интерес, рассмотрим добавление, удаление и фильтрацию дел. А пока можно посмотреть финальный результат и другие примеры dap-кода на dap.js.org/docs

Комментарии (26)


  1. token
    18.12.2019 11:46
    +2

    image
    Что я только что прочитал?


    1. jooher Автор
      18.12.2019 11:49

      Забей. Это не твое


  1. jooher Автор
    18.12.2019 11:59
    -2

    Дисклеймер: если вы зашли в комментарии для того, чтобы сообщить о том, что dap вам не нравится и вы никогда не будете им пользоваться — сначала загляните, пожалуйста сюда. Спасибо за понимание.
    Если у вас вопрос «а как на dap сделать X, да так чтобы Y» или аналогичный — буду рад ответить во всех подробностях.


    1. KaneUA
      18.12.2019 12:20
      +1

      А можно прежде чем посылать на страницу «Всем похуй» на Lurkmore перечитать свой пост? А то выглядит как задел под монетизацию. Или безразличие крайней степени — ваш девиз?
      image


      1. jooher Автор
        18.12.2019 12:42

        Про рекламу и монетизацию — идея хорошая, спасибо :)


      1. jooher Автор
        18.12.2019 14:53

        Ок, заменил табы на пробелы. Лучше, да.


        1. KaneUA
          18.12.2019 15:37

          Кусок кода на сайте вообще не должен начинаться с какого-либо отступа, ведь это самостоятельный кусок кода.


          1. jooher Автор
            18.12.2019 15:40

            Это как раз не самостоятельные куски кода, а фрагменты, в которые внесены изменения. Если сравнивать эти фрагменты с предыдущими — отступы помогают сориентироваться, какая именно строка изменена. Ну так, по крайней мере, задумывалось. Такое «скользящее окно».


  1. RuGrof
    18.12.2019 12:13
    +1

    Возможно теги Angular, ReactJS, всё таки лишние для этого.
    Получение данных в UI не лучший подход.
    Почему у этого лицензия не MIT, не понятно.


    1. jooher Автор
      18.12.2019 12:46

      Получение данных в UI не лучший подход. — это было бы справедливо, если вместо "UI" написать "view".
      Dap — программа это не view.


  1. TargetSan
    18.12.2019 12:26
    +1

    Заголовок кликбейтный. Думал — какой-то маленький движок как альтернатива V8. Оказалось — очередной "гениальный фреймворк".


    1. jooher Автор
      18.12.2019 14:28

      Пожалуй, да. Убрал про «js-движок». Теперь просто Dap в действии. Про «гениальный фреймворк» — подумаю… может так и напишу.


  1. hrie
    18.12.2019 13:48

    Я поймал себя вот на какой мысли: явно JS это не то, что вам интересно.

    Пойдите другим путём — сделайте отдельный язык, компилируемый в JS. Тогда всё встанет на свои места. Всё это безумие решёток, вопросительных знаков, кавычек и скобочек обретёт смысл, плюс вам не придётся выслушавать от JS-еров вопли «мои глаза». Потому что это будет уже не JS.

    Ну и фраза «Никаких npm install и вот этого всего» это минус, а не плюс. В современной веб-разработке под браузер всё идёт через npm и если вашу либу нельзя проинсталить и скормить в babel/webpack, то никто ею и пользоваться не будет.


    1. token
      18.12.2019 13:59

      Так он же уже есть язык этот. Брэйнфак называется.


    1. jooher Автор
      18.12.2019 16:15

      На какой-то странной мысли вы себя поймали. Те, кому не интересен JS, пишут на TS, на JSX и прочих «более интересных» языках, как раз таки компилируемых в JS. Все как вы хотите.
      А вот dap как раз обходится средствами голого JS. И почему вы думаете, что выслушивать вопли «JS-еров»-неофитов — это что-то плохое? :)
      Непонятно, что вы называете безумием решеток и кавычек. Обилие решеток здесь — это «безумие» CSS-файла, который писал не я, и который к dap отношения не имеет. Кавычки и скобочки вас пугают? А вы когда на JS/HTML последний раз писали? Сравните, пожалуйста, на количество кавычек и скобочек (и их видов) вот эти два выражения:

      '#toggle-all type=checkbox'
      <div id="toggle-all" type=checkbox>
      

      Мою либу вы можете проинсталить, просто взяв файл dap.js.org/0.4.js если хотите.
      Другое дело, что это именно «не нужно», в смысле «не требуется».


      1. hrie
        18.12.2019 16:24

        "$completed=#.checked; (@method`PATCH .url:dehttp headers (.completed:?)):query"

        А, так это CSS. Похоже я сильно отстал от него )))


        1. jooher Автор
          18.12.2019 16:48

          А, так вы безумием называете вот эту решетку, наличествующую аж в 2-х экземплярах?

          d("#.checked=.completed") // # обозначает "этот элемент"
          

          Похоже, вы не от CSS сильно отстали :)


          1. hrie
            18.12.2019 17:03

            Вооот, а теперь смотрите:

            // Оператор * выполняет повтор для всех элементов массива
            // # обозначает "этот элемент"
            // Оператор ! просто добавляет текст в элемент
            "? $editing:!; ! .title"
            ...
            "? $editing; !! .title@value"
            ...
            ".title=#.value; (@method`PATCH .url:dehttp (@Content-type`application/json)@headers (.title):json.encode@body):query"
            


            Очевидно же, что если я хочу писать на dap, JS мне не помощник. Нужно учить особый синтаксис. Так зачем тогда вообще нужен JS, если вся мякотка dap'а — в этих?!!! ` @

            Избавьтесь от балласта, запилите свой компилятор. Elm, PureScript, ClojureScript, CoffeeScript все пошли этим путём и выйграли.


            1. jooher Автор
              18.12.2019 17:41

              Если вы хотите писать на dap, то JS — ваш главный помощник, на самом деле. Из второй части этого туториала это будет более понятно.

              Мякотка dap вовсе не в этих *?!
              я мог бы их назвать for-each, if, print. Просто зачем? Я вот сейчас реально перечислил все три(!) основных оператора, которые вам нужно «выучить», чтобы начать писать на dap. Выучить три оператора — это сложно, я понимаю, но мы не на ЕГЭ, тут дело добровольное. А, собственно, мякотка dap в том, что он позволяет просто и коротко описывать то, что средствами JS пишется громоздко, муторно и просто некрасиво. При этом dap не пытается лезть туда, где все можно просто и красиво сделать на JS. Дождитесь второй части туториала.

              Зачем нужен JS — кстати это хороший вопрос, я вам с удовольствием отвечу. Изначально (2008-м году, bankreports.dapmx.org) дап строился на HTML/XML. Правила писались в атрибутах d, u и т.п. В принципе это было вполне удобно (в отличие от js-строк, SGML-атрибуты могут быть многострочными, этого сейчас иногда не хватает) и выглядело в те времена доминирования XML вполне логично (хотя синтаксис самих dap-правил был действительно адский). Но в какой-то момент я подумал: а нахрена? Зачем писать это тегами, потом парсить это все, собирать в объекты, иметь вот этот весь головняк, если все это можно прекрасно сделать средствами самого JS? Так же как сейчас я не понимаю этого прикола JSX писать в JS-коде HTML-теги.

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

              В отличие от помянутых PureScript, ClojureScript, CoffeeScript, которые «вместо» JS, dap не вместо.


  1. Yeah
    19.12.2019 01:34

    Слоган/девиз фреймворка: HAPPY DEBUGGING MOTHERFUCKER!!!!!


  1. XAHTEP26
    19.12.2019 09:16

    Это очень сильно похоже на то, во что компилируется JSX или шаблоны Vue, только хуже.
    ru.vuejs.org/v2/guide/render-function.html


    1. jooher Автор
      19.12.2019 12:49

      Похоже оно только тем, что и там и там JS. Для некоторых граждан любой JS-код на одно лицо, только хуже.


      1. XAHTEP26
        19.12.2019 12:58

        Уверенности в себе вам не занимать. )


        1. jooher Автор
          19.12.2019 13:11

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


          1. XAHTEP26
            19.12.2019 13:33

            Насколько я вижу js у вас только под капотом. А используя Dap надо писать строки текста на каком-то другом языке и передавать их в функции.


            1. jooher Автор
              19.12.2019 13:55

              Да, строки текста на другом языке это и есть дап. С помощью js под капотом эти строки компилируются в шаблоны и реактивные связи между ними. Но js тут не только под капотом. Эти dap правила сами оперируют js-данными и js-функциями. Во второй части туториала это будет подробно. Пока можно посмотреть примеры на dap.js.org если интересно