2013-11-01 2 views
6

Это вопрос о нобе.Haskell: Скрытие сбоев в ленивом IO

Я хотел бы написать функцию, которая обеспечивает ленивый поток изображений, по-видимому что-то вроде:

imageStream :: [IO Image] 

К сожалению, функция, которая считывает изображения может не сработать, так как это выглядит:

readImage :: IO (Maybe Image) 

Таким образом, функция I может запись выглядит следующим образом:

maybeImageStream :: [IO (Maybe Image)] 

Как реализовать функцию, такую ​​как следующее, сохраняя при этом ленивый ввод-вывод?

flattenImageStream :: [IO (Maybe Image)] -> [IO Image] 

семантический, когда вы спросите flattenImageStream для следующего изображения, он должен перебирать список и пытаться читать каждое изображение. Он делает это, пока не найдет изображение, которое загружает и возвращает его.

EDIT: В ответах есть некоторые разногласия. Некоторые предложили решения, которые используют sequence, но я уверен, что проверял это и обнаружил, что он уничтожает лень. (я проверю его снова, чтобы убедиться, когда вернусь к компьютеру.) Кто-то также предложил использовать unsafeInterleaveIO. Из документации для этой функции, похоже, это сработает, но, очевидно, я хочу как можно больше уважать систему типов.

+0

Вы действительно хотите '[IO Image]'? С 'IO [Image]' было бы легче работать? – jwodder

+0

@jwodder, я могу ошибаться, но я думаю, что 'IO [Image]' подразумевает, что изображения будут загружаться строго, в то время как я хочу загрузить их лениво из-за памяти и других вещей. – emchristiansen

+0

Нет, они все равно будут лениться. если вы вернете [IO Image], вы ничего не загрузили. У вас есть только список функций io для загрузки изображений. – DiegoNolan

ответ

9

Вы можете использовать ListT от pipes, что обеспечивает более безопасную альтернативу ленивому IO, который делает правильные вещи в этом случае.

Как вы моделировать ленивый поток потенциально неудовлетворительных изображений:

imageStream :: ListT IO (Maybe Image) 

Предполагая, что вы имели некоторую функцию загрузки изображений типа:

loadImage :: FileName -> IO (Maybe Image) 

.. то, как вы построить такими поток будет что-то вроде:

imageStream = do 
    fileName <- Select $ each ["file1.jpg", "file2.jpg", "file3.jpg"] 
    lift $ loadImage fileName 

Если вы используете dirstream library, то вы можете даже лениво перетекать через содержимое каталога.

Функция, которая отфильтровывает только успешные результаты будут иметь этот тип:

flattenImageStream :: (Monad m) => ListT m (Maybe a) -> ListT m a 
flattenImageStream stream = do 
    ma <- stream 
    case ma of 
     Just a -> return a 
     Nothing -> mzero 

Обратите внимание, что эта функция работает для любой базы монады, m. Ничего нет IO-специфический об этом. Он также сохраняет лень!

Применение flattenImage к imageStream, дает нам что-то типа:

finalStream :: List IO Image 
finalStream = flattenImage imageStream 

Теперь давайте предположим, что у вас есть некоторые функции, потребляющих эти образы, типа:

useImage :: Image -> IO() 

Если вы хотите обработать в окончательном ListT с использованием функции useImage вы просто пишете:

main = runEffect $ 
    for (every finalStream) $ \image -> do 
     lift $ useImage image 

Это будет лениво потреблять поток изображений.

Конечно, вы также можете играть в гольф-кода и объединить все, что в следующем более короткий вариант:

main = runEffect $ for (every image) (lift . useImage) 
    where 
    image = do 
     fileName <- Select $ each ["file1.jpg", "file2.jpg", "file3.jpg"] 
     maybeImage <- lift $ loadImage fileName   
     case maybeImage of 
      Just img -> return img 
      Nothing -> mzero 

Я также думал о добавлении fail определения для ListT так, что вы могли бы просто написать :

main = runEffect $ for (every image) (lift . useImage) 
    where 
    image = do 
     fileName <- Select $ each ["file1.jpg", "file2.jpg", "file3.jpg"] 
     Just img <- lift $ loadImage fileName   
     return img 
+0

Это отлично работает, но, прочитав больше о 'pipe', я решил пойти с' Producer'/'Consumer' /' spawn' для моего полного решения, которое требует 'pipe-concurrency'. Это впечатляющая библиотека; спасибо :) – emchristiansen

+0

Добро пожаловать! –

0

Реализация этого в соответствии с просьбой, кажется, что это потребовало бы зная вне монады IO было ли значение внутри IO Nothing, а также IO предназначен для предотвращения его значения от «утечки» в снаружи чисто функциональный мир (unsafePerformIO несмотря), это было бы невозможно. Вместо этого я рекомендую получения IO [Image]: использовать sequence для преобразования [IO (Maybe Image)] в IO [Maybe Image], а затем использовать Data.Maybe.catMaybes в монаде IO (например, с fmap или liftM) для преобразования в IO [Image], например:

flattenImageStream = fmap catMaybes $ sequence maybeImageStream 
+0

Я почти уверен, что я попробовал это, и у меня есть ленивый список 'Image'. Я мог сказать, что он не ленив, потому что я регистрирую всякий раз, когда изображение загружается. – emchristiansen

1

как предложено U может превратить [мо] в м [а] с использованием последовательностью

так что вы получите:

imageStream :: IO [Image] 

, то вы можете использовать ки yMaybes от Data.Maybe сохранить только значения Just:

catMaybes `liftM` imageStream 
0

Я не думаю, что любой из этих ответов делают именно то, что вы хотите. Потому что я уверен, что catMaybes просто пропустит изображение и не попытается перезагрузить его. Если вы хотите просто попытаться перезагрузить изображение, попробуйте это.

flattenImageStream :: [IO (Maybe Image)] -> IO [Image] 
flattenImageStream xs = mapM untilSuc xs 

untilSuc :: IO (Maybe a) -> IO a 
untilSuc f = do 
    res <- f 
    case res of 
     Nothing -> untilSuc f 
     Just i -> return i 

Но то, что вы делаете, странно. Что делать, если у вас неправильный путь к файлу? Что делать, если изображение просто невозможно загрузить? Вы просто попытаетесь загрузить изображение навсегда. Вероятно, у вас должно быть несколько раз, чтобы попытаться загрузить изображение до его сдачи.

+0

Это не то, что я пытаюсь сделать. Я хочу прозрачно пропускать изображения, которые не загружаются, а не пытаться навсегда загрузить их. – emchristiansen

+0

«он должен продолжать читать изображения до тех пор, пока это не удастся, и не вернет это изображение». Хорошо, но в вашем вопросе это было не очень ясно. Тогда используйте их ответы. – DiegoNolan

+0

Мой плохой; Я отредактирую вопрос. Спасибо за указание на это. – emchristiansen