Для данной сигнатуры типа вам нужно поднять значение, возвращаемое при последнем вызове, до 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