Не могу вернуть возможно результат в IO Monad

Я не понимаю, почему этот пример кода не работает и в книге RWH это работает:

module Monads where 
    import Data.Maybe
    import Control.Monad
    amap=[("a",1),("bb",2)]
    bmap=[(1,100),(2,200)]
    final=[(100,1000),(200,2000)]

    f::String->IO (Maybe Int)
    f par=do
          a<-lookup par amap
          b<-lookup a bmap
          lookup b final

Это не работает, и я не понимаю, как это может работать, так как последняя строка возвращает Maybe something и не IO something,

Я также попытался изменить последнюю строку на:
return (lookup b final)
который, по моему мнению, должен быть идеальным (поиск возвращает Maybe Int а затем оберните его return)

Я получаю следующую ошибку при использовании return

* Couldn't match type `Maybe' with `IO'
      Expected type: IO Integer
        Actual type: Maybe Integer
    * In a stmt of a 'do' block: b <- lookup a bmap
      In the expression:
        do a <- lookup par amap
           b <- lookup a bmap
           return (lookup b final)
      In an equation for `f':
          f par
            = do a <- lookup par amap
                 b <- lookup a bmap
                 return (lookup b final)
   |
11 |           b<-lookup a bmap
   |              ^^^^^^^^^^^^^

1 ответ

Решение

Для данной сигнатуры типа вам нужно поднять значение, возвращаемое последним вызовом lookup в IO значение.

f::String->IO (Maybe Int)
f par = return $ do
      a <- lookup par amap
      b <- lookup a bmap
      lookup b final

do выражение является синтаксическим сахаром для использования >>= которые работают в Maybe монада, а не IOв этом случае, но return принимает в результате Maybe Int стоимость и производит необходимые IO (Maybe Int) значение. Это может быть

f par = return (lookup par amap >>= \a -> lookup a bmap >>= \b -> lookup b final)

Однако, кроме подписи типа, заставляющей вас использовать returnнет ничего другого f для этого требуется вернуть IO значение; ни его аргумент, ни какой-либо из трех списков ассоциаций не включают IO в любом случае, так что вы можете просто изменить сигнатуру типа, чтобы исключить любую ссылку на IO и работать исключительно в Maybe монада.

f :: String -> Maybe Int
f par = do
    a <- lookup par amap
    b <- lookup a bmap
    lookup b final

В стороне: избавиться от do

Так как вы объединяете различные призывы к lookupбыло бы неплохо, если бы вы могли передать каждый результат следующему вызову в стиле без точек. Для этого вам нужно изменить порядок, в котором lookup принимает свои аргументы. Это тривиально сделать с помощью flip: flip lookup :: [(a,b)] -> a -> Maybe b,

f :: String -> Maybe Int
f par = let lookup' = flip lookup
        in lookup' amap par >>= lookup' bmap >>= lookup' final

Сделав этот шаг дальше, вы можете удалить ссылку на par в целом, используя >=> оператор импортирован из Control.Monad, Сравните его тип с типом >>=:

:t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
:t (>=>)
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c

Вместо того чтобы начинать с Maybe значение, вы составляете вызовы lookup'и передайте начальную строку функции.

f :: String -> Maybe Int
f = let lookup' = flip lookup
     in lookup' amap >=> lookup' bmap >=> lookup' final
Другие вопросы по тегам