Записи, макросы, скала, вывод типов, SQL
Feb. 20th, 2013 05:04 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
В очередной раз, в связи с макросами подниму свой любимый вопрос:
Можно ли поиметь в языках типа скалы строгую типизацию, не объявляя типы записей заранее?
Идея в следующем: у меня в Clojure большая часть DSL основана на том, что я могу в любое место описания строк-колонок-ячеек отчета (условно говоря, Excel-like таблица фиксированной формы, заполняемая автоматом из проводок) в любой момент добавить атрибут типа :skip-in-vat true и обработать его. При этом мне нужно это изменить ровно в двух местах - описание документа ("данные") и расчетный алгоритм ("код"). Типов тут нет, а вместо них - структуры данных, скомбинированные из словарей, массивов и атомарных данных.
Но с динамически типизированными языками, а особенно с Clojure с ее destructring и прагматичным подходам к совместимости типов имеется постоянно проблема вида "сунул лишние скобки, на место числа попал массив и через 100500 вызовов когда дело дошло до суммирования, оператор + сдох от попытки просуммировать ссылку на массив со стек-трейсом на три страницы". Причем если мне такие ошибки чинить достаточно легко, то менее опытным коллегам это вырывает мозг.
В F# и прочих типизированных языках же мне придется сначала ползти в описание записи (если там записи вообще есть), добавлять поле или хуже того - еще один вариант для алгебраического типа, править вызовы конструкторов (если оверлоадинга нет) и прочая и прочая.
Кроме того, часто хочется такого: "тип записи создается в SQL запросе и далее используется в функции, обрабатывающей результат запроса". В clojure это опять же - словарь, с кейвордами в качестве ключей.
Языки же кроме F#, Scala и Clojure можно не рассматривать вообще - на них аналогичные задачи или сводятся к "наняли 100 ртишников по одному на объект предметной области и написали вручную" или к "написали самодельный лисп с хаскелем на дельфи/C#" или к "используем безумные ORM с еще более дикими, чем SQL языками и внешними декларативными описаниями".
Т.е. я не говорю, что они не пригодны - но затраты ресусов на решение задачи на таких языках заметно больше.
Можно ли поиметь в языках типа скалы строгую типизацию, не объявляя типы записей заранее?
Идея в следующем: у меня в Clojure большая часть DSL основана на том, что я могу в любое место описания строк-колонок-ячеек отчета (условно говоря, Excel-like таблица фиксированной формы, заполняемая автоматом из проводок) в любой момент добавить атрибут типа :skip-in-vat true и обработать его. При этом мне нужно это изменить ровно в двух местах - описание документа ("данные") и расчетный алгоритм ("код"). Типов тут нет, а вместо них - структуры данных, скомбинированные из словарей, массивов и атомарных данных.
Но с динамически типизированными языками, а особенно с Clojure с ее destructring и прагматичным подходам к совместимости типов имеется постоянно проблема вида "сунул лишние скобки, на место числа попал массив и через 100500 вызовов когда дело дошло до суммирования, оператор + сдох от попытки просуммировать ссылку на массив со стек-трейсом на три страницы". Причем если мне такие ошибки чинить достаточно легко, то менее опытным коллегам это вырывает мозг.
В F# и прочих типизированных языках же мне придется сначала ползти в описание записи (если там записи вообще есть), добавлять поле или хуже того - еще один вариант для алгебраического типа, править вызовы конструкторов (если оверлоадинга нет) и прочая и прочая.
Кроме того, часто хочется такого: "тип записи создается в SQL запросе и далее используется в функции, обрабатывающей результат запроса". В clojure это опять же - словарь, с кейвордами в качестве ключей.
Языки же кроме F#, Scala и Clojure можно не рассматривать вообще - на них аналогичные задачи или сводятся к "наняли 100 ртишников по одному на объект предметной области и написали вручную" или к "написали самодельный лисп с хаскелем на дельфи/C#" или к "используем безумные ORM с еще более дикими, чем SQL языками и внешними декларативными описаниями".
Т.е. я не говорю, что они не пригодны - но затраты ресусов на решение задачи на таких языках заметно больше.
no subject
Date: 2013-02-20 02:40 pm (UTC)Ну, не всё так плохо. В последнем F# есть Type Providers - они решают именно эту задачу: например, из SQL базы построить строго типизированные F#-классы.
no subject
Date: 2013-02-20 02:57 pm (UTC)no subject
Date: 2013-02-20 04:16 pm (UTC)no subject
Date: 2013-02-20 05:45 pm (UTC)no subject
Date: 2013-02-21 06:44 am (UTC)no subject
Date: 2013-02-20 03:04 pm (UTC)Или сделать полноразмерные unit- и functionality- тэсты и прочее CI.
Можно и то и другое.
no subject
Date: 2013-02-20 03:21 pm (UTC)(no subject)
From:no subject
Date: 2013-02-20 03:10 pm (UTC)можно рассматривать окамл, где данная проблема решается "расширяемыми записями", всё типизировано.
> Кроме того, часто хочется такого: "тип записи создается в SQL запросе и далее используется в функции, обрабатывающей результат запроса".
в окамле это -- синтаксическое расширение, генерирующее тип по схеме и запросу. Для некоторых вещей есть http://pgocaml.forge.ocamlcore.org/ и http://www.strauss-acoustics.ch/macaque-pgocaml.html .
no subject
Date: 2013-02-20 03:23 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2013-02-20 04:04 pm (UTC)no subject
Date: 2013-02-20 04:17 pm (UTC)no subject
Date: 2013-02-20 04:06 pm (UTC)Еще есть ocaml. Там можно создавать объекты по месту (без описания класса) и он сам определит, какой у них тип.
В том же Хаскеле можно работать с записями не так:
а так
Тогда не надо будет править конструкторы.
Ну и в принципе, можно создавать такие же словари в типизированных языках.
no subject
Date: 2013-02-20 04:53 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2013-02-20 04:09 pm (UTC)http://www.typescriptlang.org/Playground/
no subject
Date: 2013-02-20 05:37 pm (UTC)no subject
Date: 2013-02-20 05:57 pm (UTC)Впрочем, многим это доставляет удовольствие, так что не хочу больше мешать.
no subject
Date: 2013-02-20 06:13 pm (UTC)И чтобы оно все из базы данных приходило без издевательств над здравым смыслом вида "а теперь мы схему базы данных пишем не на SQL а на руби".
Пока единственный вариант - либо генерить код, либо динамическая типизация.
(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2013-02-20 06:38 pm (UTC)no subject
Date: 2013-02-20 09:45 pm (UTC)Но с динамически типизированными языками, а особенно с Clojure с ее destructring и прагматичным подходам к совместимости типов имеется постоянно проблема вида "сунул лишние скобки, на место числа попал массив и через 100500 вызовов когда дело дошло до суммирования, оператор + сдох от попытки просуммировать ссылку на массив со стек-трейсом на три страницы". Причем если мне такие ошибки чинить достаточно легко, то менее опытным коллегам это вырывает мозг.
Это говорит только о том, что не поставленны внутренние проверки, которые должны отловить абнормальную ситуацию сразу. Причём, расставить такие проверки в нужных местах могут даже "менее опытные коллеги"
no subject
Date: 2013-02-20 09:57 pm (UTC)(no subject)
From:(no subject)
From:no subject
Date: 2013-02-21 06:54 am (UTC)no subject
Date: 2013-02-21 07:40 am (UTC)no subject
Date: 2013-02-21 09:20 am (UTC)В местах использования структурно квантифицироваться по нужным полям.
Если речь не о аргументах метода, а о непосредственном использовании, то можно и без квантификации: getOstatki(...).someField
no subject
Date: 2013-02-23 08:43 pm (UTC)Мне кажется, секрет в фиксации инвариантов — если сама функция использует какое-то поле пришедших данных, то она должна контролировать его наличие, но только и исключительно его (а не «типа рекорда», например). Желательно — в том числе статически (но я не знаю примеров этого кроме костыля в typed clojure), желательно в рантайме тоже с минимальным количеством усилий. Насколько я понимаю, ничего готового нет; но можно попробовать, как я уже упомянул выше, пользоваться стилем из Erlang'а: выматчивать в голове функции все куски данных, которые нужны для неё или её потомков.