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] qehgt.livejournal.com 2013-02-20 02:40 pm (UTC)(link)
>В F# и прочих типизированных языках же мне придется сначала ползти в описание записи
Ну, не всё так плохо. В последнем F# есть Type Providers - они решают именно эту задачу: например, из SQL базы построить строго типизированные F#-классы.

[identity profile] theiced.livejournal.com 2013-02-20 02:57 pm (UTC)(link)
я вижу только одну проблему: "то менее опытным коллегам это вырывает мозг" - заменить этих коллег на нормальных.

[identity profile] tzirechnoy.livejournal.com 2013-02-20 03:04 pm (UTC)(link)
Написать на clojure статический анализатор кода. Вроде lintа. Но с хинтами по поводу возвращаемых запросами типов.

Или сделать полноразмерные unit- и functionality- тэсты и прочее CI.

Можно и то и другое.

[identity profile] gds.livejournal.com 2013-02-20 03:10 pm (UTC)(link)
> Языки же кроме F#, Scala и Clojure можно не рассматривать вообще

можно рассматривать окамл, где данная проблема решается "расширяемыми записями", всё типизировано.

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

в окамле это -- синтаксическое расширение, генерирующее тип по схеме и запросу. Для некоторых вещей есть http://pgocaml.forge.ocamlcore.org/ и http://www.strauss-acoustics.ch/macaque-pgocaml.html .

[identity profile] metaclass.livejournal.com 2013-02-20 03:21 pm (UTC)(link)
Второе есть. Склоняюсь прикрутить первое, для ускорения поиска ошибок заранее.

[identity profile] theiced.livejournal.com 2013-02-20 03:23 pm (UTC)(link)
окамл негуманен

[identity profile] gds.livejournal.com 2013-02-20 03:29 pm (UTC)(link)
почему Вы так считаешь?

[identity profile] sum-erman.livejournal.com 2013-02-20 03:53 pm (UTC)(link)
В erlang например есть dialyzer который неплохо спасает от описанной проблемы с лишними скобками.

[identity profile] theiced.livejournal.com 2013-02-20 03:56 pm (UTC)(link)
потому что он негуманен и ужасен :)

[identity profile] gds.livejournal.com 2013-02-20 03:57 pm (UTC)(link)
ответ уровня жж.
ок.

[identity profile] veremeenko-alex.livejournal.com 2013-02-20 04:04 pm (UTC)(link)
и рыбку съесть и ...

[identity profile] vshabanov.livejournal.com 2013-02-20 04:06 pm (UTC)(link)
Лучше всего это сделано в Ur/Web. Там пишешь SQL запрос, или рисуешь какую-нибудь HTML формочку и он сам определяет, какие будут поля в результате. Там правда есть проблема с километровыми сообщениями об ошибках, когда он выводит всю запись (но это по идее решаемо, если выводить только разницу).

Еще есть ocaml. Там можно создавать объекты по месту (без описания класса) и он сам определит, какой у них тип.

В том же Хаскеле можно работать с записями не так:
r = MyRecord { mrField1 = 1, mrField2 = "asdf" }
case r of
    MyRecord { mrField1 = f1, mrField2 = f2 } ->
    -- или MyRecord f1 f2

а так
instance Default MyRecord where 
    def = MyRecord { mrField1 = 0, mrField2 = "" }
r = def { mrField1 = 1, mrField2 = "asdf" }
case r of
    MyRecord {..} -> mrField1 + 2
-- или просто mrField1 r + 2

Тогда не надо будет править конструкторы.

Ну и в принципе, можно создавать такие же словари в типизированных языках.

[identity profile] jakobz.livejournal.com 2013-02-20 04:09 pm (UTC)(link)
Блин, хотел камент отредактировать, а стер его вместо этого. Короче мне вот хочется чего-то типа TypeScript, но чтобы работало в гораздо большем количестве случаев.

function makeObject() {
	return {
		a : 1,
		b : 2
	}
}

function alterObject(obj) {
	obj.c = 3
	return obj
}

var o = makeObject()
console.log(o.c) //The property 'c' does not exist on value of type '{ a: number; b: number; }'
var o2 = alterObject(o)
console.log(o2.c) // Тут всё ок
console.log(o.c)  // The property 'c' does not exist on value of type '{ a: number; b: number; }' -- а такое не осиливает


http://www.typescriptlang.org/Playground/

[identity profile] theiced.livejournal.com 2013-02-20 04:16 pm (UTC)(link)
ололо

[identity profile] metaclass.livejournal.com 2013-02-20 04:17 pm (UTC)(link)
Оно самое.

[identity profile] jdevelop.livejournal.com 2013-02-20 04:52 pm (UTC)(link)
киэха/моэха?

[identity profile] jdevelop.livejournal.com 2013-02-20 04:53 pm (UTC)(link)
беда записей в цацкеле - это неудобство их использования

[identity profile] gds.livejournal.com 2013-02-20 04:56 pm (UTC)(link)
моэха нет, киэху асилил потом, но изначально кащенка и эхехехи, заражённые набегами.

[identity profile] jdevelop.livejournal.com 2013-02-20 05:02 pm (UTC)(link)
ок :)

[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] avnik.livejournal.com 2013-02-20 05:33 pm (UTC)(link)
Только питон, только хардкор

[identity profile] avnik.livejournal.com 2013-02-20 05:37 pm (UTC)(link)
В питоне (точнее Zope) можно приводить один тип к другому (точнее к интерфейсу в рамках zope.interface),или кинь исключение/верни дефолт если не получилось (+можно приводить много значений к одному) -- но это не свойство языка, это библиотечный костыль

[identity profile] metaclass.livejournal.com 2013-02-20 05:39 pm (UTC)(link)
Линзы?

[identity profile] v-l-a-d.livejournal.com 2013-02-20 05:45 pm (UTC)(link)
ребе, вы ж сами в подзамке у ждевелопа писали, что нормальных коллег хрен найдешь

Page 1 of 3