Наткнулся на проблему с использованием функции using и point-free стиля. Пусть, например, мы читаем какие-то данные из файла:
#light
open System.IO
let readFile (path : string) action = using (new StreamReader(path)) action
let applyAction handler (stream : System.IO.TextReader) = stream.ReadLine () |> handler
let trim (s : string) = s.Trim ()
let extractInfo path extracter = readFile path <| applyAction extracter
let filePath = @"C:\some_file.txt"
let info = extractInfo filePath trim // ok
В таком виде всё работает. Теперь переписываем extractInfo в point-free стиле:
let flip f x y = f y x;;
let extractInfo' = flip readFile <| flip applyAction
let info' = extractInfo' filePath trim // System.ObjectDisposedException: Cannot read from a closed TextReader.
и получаем исключение из-за попытки использования закрытого файла. Причина, в принципе, понятна, тем не менее интересно, можно-ли это исправить сохранив point-free стиль extractInfo?
Здравствуйте, Аноним, Вы писали:
А>В таком виде всё работает. Теперь переписываем extractInfo в point-free стиле:
А>[skipped]
А>и получаем исключение из-за попытки использования закрытого файла. Причина, в принципе, понятна, тем не менее интересно, можно-ли это исправить сохранив point-free стиль extractInfo?
Если чуть чуть преобразовать Ваш код:
let extractInfo' = flip readFile <| flip applyAction
let extractInfo' = (flip readFile) (flip applyAction)
let extractInfo' = (fun act path -> readFile path act) (fun st hd -> applyAction hd st)
let extractInfo' = fun path -> readFile path (fun st hd -> applyAction hd st)
let info' = extractInfo' filePath trim
Я думал, что понимаю, как работает partial application, но эта ситуация вогнала меня в размышления
Решил для себя разобраться как происходит вычисление, всё оказалось очень просто:
1. Вычисляется extractInfo' с аргументом filePath.
2. Вычисляется readFile с аргументом path и (fun st hd -> applyAction hd st) в качестве action.
3. readFile создаёт StreamReader, передаёт его и action в функцию using.
4. using вызывает action, передав 1 аргумент — открытый StreamReader.
5. action запоминает первый аргумент-ридер и возвращает новую функцию, ожидающую ещё один аргумент — handler.
6. using возвращает функцию-результат action'а и закрывает ридер (то есть в функции остаётся ссылка на закрытый StreamReader).
7. Функция возвращается назад и info'передаёт ей handler, но стрим уже закрыт...
Совсем pointfree сделать не вышло, мона вот так:
let extractInfo' path = applyAction >> readFile path
let info' = extractInfo' filePath trim
Здравствуйте, Пельмешко, Вы писали:
П>Совсем pointfree сделать не вышло...
Уууухухухууу, догнал!!
let extractInfo' = flip (applyAction >> (flip readFile))
Здравствуйте, Пельмешко, Вы писали:
П>Здравствуйте, Пельмешко, Вы писали:
П>>Совсем pointfree сделать не вышло...
П>Уууухухухууу, догнал!!
П>П>let extractInfo' = flip (applyAction >> (flip readFile))
П>
П>
Спасибо большое! Можно, даже, чуть меньше скобок сделать
let extractInfo' = flip (flip readFile << applyAction)
Кстати, в Haskell, похоже, такие ошибки сложнее случайно сделать. Попытка вернуть значение не обёрнутое в IO обламывается ещё на этапе компиляции. Можно, конечно это сделать специально, но тогда сразу становится видно в чём проблема.