Лениво оценивать монадические функции в Хаскеле - PullRequest
8 голосов
/ 09 мая 2011

Кажется, я не могу обойти эту проблему.

У меня есть что-то вроде этого:

  getFilePathForDay :: Day -> IO (Maybe FilePath)

  getFilePathForDays date days = do
      files <- mapM getFilePathForDay $ iterate (addDays 1) date
      return . take days . catMaybes $ files

Я пытаюсь получить х количестводопустимые пути к файлам, где x - это количество дней, которое я хочу, но приведенный выше код работает всегда.Раньше я сталкивался с этой проблемой с монадами, и мне было интересно, есть ли обходной путь для этой проблемы, которую я имею.

Спасибо!

Ответы [ 2 ]

8 голосов
/ 09 мая 2011

Код выполняется вечно, потому что вы пытаетесь упорядочить бесконечное количество побочных вычислений при вызове mapM.

Поскольку вы не знаете заранее, сколько из этих вычислений вам нужно будет выполнить (как указано в вашем использовании catMaybes), вам придется чередовать вызовы на getFilePathForDay с остальными вашими вычисление. Это можно сделать с помощью unsafeInterleaveIO, но, как следует из названия, это не рекомендуемый подход.

Вы можете реализовать это вручную как:

getFilePathForDays _ 0 = return []
getFilePathForDays date days = do
    mpath <- getFilePathForDay date
    case mpath of
        Just path -> (path :) <$> remaining (days-1)
        Nothing   -> remaining days
  where
    remaining days = getFilePathForDays (addDays 1 date) days

Возможно, есть более элегантный способ, но он сейчас не подходит мне:)

4 голосов
/ 09 мая 2011

Иногда ленивый ввод-вывод - правильный подход. Поскольку у вас бесконечная ленивая последовательность дней, и вы лениво выбираете некоторый (динамический) диапазон из этой последовательности, мы также должны обращаться к файловой системе только по требованию.

Это можно сделать с помощью unsafeInterleaveIO:

import System.IO.Unsafe         ( unsafeInterleaveIO )

-- return a lazy list of filepaths, pull as many as you need
getFilePathForDays :: Day -> Int -> IO [FilePath]
getFilePathForDays date days = do

    let go (d:ds) = unsafeInterleaveIO $ do
                        f  <- getFilePathForDay d
                        fs <- go ds
                        return (f:fs)

    -- an infinite, lazy stream of filepaths
    fs <- go (iterate (addDays 1) date)

    return . take days . catMaybes $ fs

Где go возвращает ленивый поток результатов, и мы берем столько, сколько нам нужно. Легко.

...