Это в основном тот же ответ, что и у Бартека, но с использованием другого подхода.
Допустим, у вас есть функция foo :: Picture -> Picture
, которая каким-то образом преобразует изображение.Он ожидает Picture
в качестве аргумента, но все, что у вас есть, это block :: IO (Maybe Picture)
;там может быть или не быть картинка, скрытая там, но это все, что у вас есть.
Для начала давайте предположим, что у вас есть какая-то функция foo' :: Maybe Picture -> Maybe Picture
.Его определение простое:
foo' :: Maybe Picture -> Maybe Picture
foo' = fmap foo
На самом деле настолько просто, что вы никогда его не напишите;где бы вы ни использовали foo'
, вы просто используете fmap foo
напрямую.Как вы помните, эта функция возвращает Nothing
, если получает Nothing
, и возвращает Just (foo x)
, если получает некоторое значение Just x
.
Теперь, учитывая, что у вас есть foo'
как применить его к значению, скрытому в типе IO
?Для этого мы будем использовать экземпляр Monad
для IO
, который предоставляет нам две функции (типы здесь специализируются на IO
):
return :: a -> IO a
(>>=) :: IO a -> (a -> IO b) -> IO b
В нашем случае мы признаем, что обаa
и b
равны Maybe Picture
.Если foo' :: Maybe Picture -> Maybe Picture
, то return . foo' :: Maybe Picture -> IO (Maybe Picture)
.Это означает, что мы наконец можем «применить» foo
к нашей картинке:
> :t block >>= return . (fmap foo)
block >>= return . (fmap foo) :: IO (Maybe Picture)
Но мы на самом деле не применяем foo
сами.То, что мы действительно делаем, - это поднятие foo
в контекст, где после выполнения block
можно вызывать foo'
для всего, что block
производит.