metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2010-10-13 06:39 pm
Entry tags:

Дай дураку F# стеклянный..

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


2010-10-13 21:43:20,944 [1] DEBUG StartupCode$ModelGen.$FirebirdReader newCmd   cmdForeignKeys 61724767 Open
2010-10-13 21:43:20,944 [1] DEBUG StartupCode$ModelGen.$FirebirdReader newCmd   cmdForeignKeyFields 18651996 Open
2010-10-13 21:43:20,944 [1] DEBUG StartupCode$ModelGen.$FirebirdReader Disposed cmdForeignKeyFields cmdForeignKeyFields 18651996
2010-10-13 21:43:20,944 [1] DEBUG StartupCode$ModelGen.$FirebirdReader Disposed cmdForeignKeys cmdForeignKeys 61724767
2010-10-13 21:43:20,944 [1] DEBUG StartupCode$ModelGen.$FirebirdReader cmdForeignKeyFields.ExecuteReader 18651996 ForeignKeyFields FK_ForeignKeys
2010-10-13 21:43:20,944 [1] FATAL CurrentDomain_UnhandledException
System.InvalidOperationException: Connection must be valid and open



Однозначно, что ленивость+внешние ресурсы это не совсем то, что следует делать, да и отладке это не подлежит в принципе, ибо монады. Но как превратить это дело в fold-like алгоритм, хрен его знает, да еще с учетом того, что там внутри не просто итератор по плоским записям, а внутри этих записей еще вложенные итераторы, а из них ссылки на элементы из других итераторов, в общем, ад тот еще.
Вот еще смех будет, если это не в F# проблема, а в ADO.NET провайдере Firebird.

PS: Судя по всему, все хорошо и пень все таки я - таки ленивые вложенные итераторы можно использовать только вложенно, а не так как я - сначала пройтись по внешнему, найти нужную запись, а потом из нее читать вложенные. Адекватный вариант - сделать их неленивыми. Обычно в этом помогает Seq.cache, но тут он, похоже, тоже вызывается в неподходящий момент.

PPS: Ухуху, и решение в этом случае действительно, как и советуют гуру ленивости - перевернуть все в fold-like алгоритм, передав две функции - одну для fold внешнего итератора и одну для fold внутреннего (и вроде в общем случае, еще две - одну которая будет из внешнего итератора и состояния делать начальное состояние для fold внутреннего, и вторая будет комбинировать результат от fold внутреннего итератора и состояние внешнего).
Это получается, что для fold в случае вложенных итераторов количество передаваемых параметров-функций прилично возрастает: по одной функции на каждый итератор в иерархии и +2 на каждый случай вложенного итератора, причем у самих этих функций количество передаваемых в них параметров тоже особой радости не доставляет.
Зато соответствующим набором таких функций, судя по всему, можно вычислить произвольную функцию от базы данных, представленной в виде набора разнообразно вложенных итераторов :)

[identity profile] gds.livejournal.com 2010-10-13 08:11 pm (UTC)(link)
кстати, я сейчас в процессе портирования олеговских iteratees (например, talk notes) на окамл. Если чо, как проверю это в деле, смогу перевести в ocaml original syntax, а далее, если F# умеет custom infix operators, будет почти халява перевести его на F#. Поэтому стоит подумать, помогут ли тут iteratees.
Они -- почти тот же самый fold (в том числе вложенный), только с кучей гарантий (по хендлам, по памяти, по ограничению внутренних итераторов(?) внешними). И есть преимущества над fold -- не надо показывать наружу начальное состояние, не надо выдумывать ничего, чтобы прервать выполнение, плюс относительно-прозрачная обработка ошибок.
(во многое пока не вникал, но это точно лучше, чем прямой ввод-вывод (как в императивщине), лучше, чем ленивый ввод-вывод (как IO в х-е), и лучше, чем fold поверх потоков с данными.)