Не могу вернуть возможно результат в 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