Не могу вернуть возможно результат в IO Monad - PullRequest
0 голосов
/ 01 марта 2019

Я не понимаю, почему этот пример кода не работает, и в 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 Ответ

0 голосов
/ 01 марта 2019

Для данной сигнатуры типа вам нужно поднять значение, возвращаемое при последнем вызове, до 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 нет ничего, что требует , чтобы он возвратил *Значение 1020 *;ни его аргумент, ни какой-либо из трех списков ассоциаций никоим образом не включают 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
...