Дай дураку F# стеклянный..
Домудрился с вложенными ленивыми секвенсами вперемежку с обращениями к БД до такой степени, что сломал компилятор F#, похоже. Во всяком случае, он у меня теперь умудряется сделать Dispose команде доступа к БД до того как начнет генерить последовательность из нее. Причем, что удивительно - не всегда, а только при определенном паттерне обращения к другим командам.
Однозначно, что ленивость+внешние ресурсы это не совсем то, что следует делать, да и отладке это не подлежит в принципе, ибо монады. Но как превратить это дело в fold-like алгоритм, хрен его знает, да еще с учетом того, что там внутри не просто итератор по плоским записям, а внутри этих записей еще вложенные итераторы, а из них ссылки на элементы из других итераторов, в общем, ад тот еще.
Вот еще смех будет, если это не в F# проблема, а в ADO.NET провайдере Firebird.
PS: Судя по всему, все хорошо и пень все таки я - таки ленивые вложенные итераторы можно использовать только вложенно, а не так как я - сначала пройтись по внешнему, найти нужную запись, а потом из нее читать вложенные. Адекватный вариант - сделать их неленивыми. Обычно в этом помогает Seq.cache, но тут он, похоже, тоже вызывается в неподходящий момент.
PPS: Ухуху, и решение в этом случае действительно, как и советуют гуру ленивости - перевернуть все в fold-like алгоритм, передав две функции - одну для fold внешнего итератора и одну для fold внутреннего (и вроде в общем случае, еще две - одну которая будет из внешнего итератора и состояния делать начальное состояние для fold внутреннего, и вторая будет комбинировать результат от fold внутреннего итератора и состояние внешнего).
Это получается, что для fold в случае вложенных итераторов количество передаваемых параметров-функций прилично возрастает: по одной функции на каждый итератор в иерархии и +2 на каждый случай вложенного итератора, причем у самих этих функций количество передаваемых в них параметров тоже особой радости не доставляет.
Зато соответствующим набором таких функций, судя по всему, можно вычислить произвольную функцию от базы данных, представленной в виде набора разнообразно вложенных итераторов :)
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 на каждый случай вложенного итератора, причем у самих этих функций количество передаваемых в них параметров тоже особой радости не доставляет.
Зато соответствующим набором таких функций, судя по всему, можно вычислить произвольную функцию от базы данных, представленной в виде набора разнообразно вложенных итераторов :)
no subject
Они -- почти тот же самый fold (в том числе вложенный), только с кучей гарантий (по хендлам, по памяти, по ограничению внутренних итераторов(?) внешними). И есть преимущества над fold -- не надо показывать наружу начальное состояние, не надо выдумывать ничего, чтобы прервать выполнение, плюс относительно-прозрачная обработка ошибок.
(во многое пока не вникал, но это точно лучше, чем прямой ввод-вывод (как в императивщине), лучше, чем ленивый ввод-вывод (как IO в х-е), и лучше, чем fold поверх потоков с данными.)
no subject
no subject
А заодно я при портировании понимаю, что и как Олег сделал. И я ещё комментатор. Дополняю документацию для тех, кто функциональщину знает, но не всегда понимает интуитивно монады, хитровыебанные инфиксные вызовы по шесть функций за раз, при их неочевидности и ёбаной перегрузке операторов из ёбаных тайпклассов, и навожу там порядок. Чтобы простой быдло-ML кодер смог сделать свой вполне защищённый веб-сервер за 21 день (ну или что-то типа того; 20 дней на iteratees).
no subject
no subject