metaclass: (дзедline)
metaclass ([personal profile] metaclass) wrote2013-02-20 05:04 pm
Entry tags:

Записи, макросы, скала, вывод типов, SQL

В очередной раз, в связи с макросами подниму свой любимый вопрос:
Можно ли поиметь в языках типа скалы строгую типизацию, не объявляя типы записей заранее?

Идея в следующем: у меня в Clojure большая часть DSL основана на том, что я могу в любое место описания строк-колонок-ячеек отчета (условно говоря, Excel-like таблица фиксированной формы, заполняемая автоматом из проводок) в любой момент добавить атрибут типа :skip-in-vat true и обработать его. При этом мне нужно это изменить ровно в двух местах - описание документа ("данные") и расчетный алгоритм ("код"). Типов тут нет, а вместо них - структуры данных, скомбинированные из словарей, массивов и атомарных данных.

Но с динамически типизированными языками, а особенно с Clojure с ее destructring и прагматичным подходам к совместимости типов имеется постоянно проблема вида "сунул лишние скобки, на место числа попал массив и через 100500 вызовов когда дело дошло до суммирования, оператор + сдох от попытки просуммировать ссылку на массив со стек-трейсом на три страницы". Причем если мне такие ошибки чинить достаточно легко, то менее опытным коллегам это вырывает мозг.

В F# и прочих типизированных языках же мне придется сначала ползти в описание записи (если там записи вообще есть), добавлять поле или хуже того - еще один вариант для алгебраического типа, править вызовы конструкторов (если оверлоадинга нет) и прочая и прочая.

Кроме того, часто хочется такого: "тип записи создается в SQL запросе и далее используется в функции, обрабатывающей результат запроса". В clojure это опять же - словарь, с кейвордами в качестве ключей.

Языки же кроме F#, Scala и Clojure можно не рассматривать вообще - на них аналогичные задачи или сводятся к "наняли 100 ртишников по одному на объект предметной области и написали вручную" или к "написали самодельный лисп с хаскелем на дельфи/C#" или к "используем безумные ORM с еще более дикими, чем SQL языками и внешними декларативными описаниями".
Т.е. я не говорю, что они не пригодны - но затраты ресусов на решение задачи на таких языках заметно больше.

[identity profile] vshabanov.livejournal.com 2013-02-20 05:25 pm (UTC)(link)
Что есть то есть. rec.field1.field2.field3 = 'abc' в хаскелле выглядит совершенно негуманно.

Но если без модификаций сильно вложенных полей, то вполне ничего.

[identity profile] jdevelop.livejournal.com 2013-02-20 05:33 pm (UTC)(link)
есть всякие lenses, но они еще более негуманны

остается Scala ;)

[identity profile] vshabanov.livejournal.com 2013-02-20 05:57 pm (UTC)(link)
Скала -- это еще менее гуманно чем lenses ;)

[identity profile] vshabanov.livejournal.com 2013-02-20 05:56 pm (UTC)(link)
Уж очень они жесткие. Я обычно, если надо часто какое-то вложенное поле менять просто вспомогательную ф-ию завожу, типа modifyField1 и все становится более-менее неплохо (не так часто приходится менять что-то глубоко вложенное).

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

[identity profile] jakobz.livejournal.com 2013-02-21 10:46 am (UTC)(link)
А ты какую либу пробовал?

[identity profile] vshabanov.livejournal.com 2013-02-21 12:29 pm (UTC)(link)
Никакую не пробовал.

Мне они как-то сразу не сильно понравились, когда еще давно читал бумажную статью по ним. А недавно читал про них в http://happstack.com/docs/crashcourse/AcidState.html#ixset_lens
(там data-lens вроде) и ужаснулся.

[identity profile] jakobz.livejournal.com 2013-02-21 01:24 pm (UTC)(link)
Коментарий твой в спам попал из-за ссылки.

Я как-то пробовал как-то поюзать data-lens. Мне жутко не понравилось как оно приделано: необходимость прятать точку из prelude, подчеркивания в именах штатных геттеров, чтобы линзы с ними различать, всякое такое.

Но сама концепция - очень даже, эдакие first-class property, которые кроме собственно get/set можно хранить, передавать и слеплять. Всякое такое получается:
changePhone newPhone = (phone.contactInfo.person) ^= newPhone

К слову, автор на нее забил и сделал модуль lens (Control-Lens). Залечил там проблему с точкой. Там какой-то совершенно адский для меня матан с вовлечением всех этих Foldable, Applicative и т.п. Но через это получаются дичайшие вещи, типа такого:
>over (mapped._2) length [("hello","world"),("leaders","!!!")]
[("hello",5),("leaders",3)]

Буду пробовать, с записями местами жить просто никак нельзя.

[identity profile] vshabanov.livejournal.com 2013-02-21 02:28 pm (UTC)(link)
В целом, линзы ничего, если надо много с записями работать (у меня просто редко встречаются вложенные записи, возможно из-за неудобства работы с ними стараюсь обходиться без них).

Но очень уж у них много вариантов операторов.