Haskell и Монады
При воспроизведении данного куска кода:
half x = if even x
then Just (x `div` 2)
else Nothing
printMaybe m = case m of
Nothing -> putStrLn "List was empty!"
Just x -> print x
main = do
let halfN = Just 4 >>= half
halfN >>= printMaybe
Возникает ошибка:
main.hs:9:1: error:
* Couldn't match expected type `IO t0' with actual type `Maybe ()'
* In the expression: main
When checking the type of the IO action `main'
|
9 | main = do
| ^
main.hs:11:13: error:
* Couldn't match type `IO' with `Maybe'
Expected type: Maybe a0 -> Maybe ()
Actual type: Maybe a0 -> IO ()
* In the second argument of `(>>=)', namely `printMaybe'
In a stmt of a 'do' block: halfN >>= printMaybe
In the expression:
do let halfN = Just 4 >>= half
halfN >>= printMaybe
|
11 | halfN >>= printMaybe
Вот способ решения этой проблемы:
half x = if even x
then Just (x `div` 2)
else Nothing
printMaybe m = case m of
Nothing -> putStrLn "List was empty!"
Just x -> print x
main = do
let halfN = Just 4 >>= half
printMaybe $ halfN
Не могу понять, почему изначальная программа не работает. Сначала получаем половину числа в функции half (в виде монады Maybe), после чего "пропихиваем" это значение в функцию вывода - printMaybe.
Почему первоначальная версия кода не работает?
Ответы (1 шт):
>>= это оператор не "пропихивания", а монадического связывания
(>>=) :: Monad m => m a -> (a -> m b) -> m b
И связывает он, как видно из сигнатуры, только однотипные контейнеры Maybe с Maybe, IO с IO и т.д.
Вы же пытаетесь связать
halfN :: Integral a => Maybe a
и
printMaybe :: Show a => Maybe a -> IO ()
Т.е. контейнеры разных типов - Maybe и IO, и с несовместимыми типами содержимого Integral a => a против Show a => Maybe a.
printMaybe $ halfN
Это правильный способ использования этой функции, альтернативой (эквивалентной по первому закону монад, но совершенно неуместной в данном случае) может быть
return halfN >>= printMaybe
То же касается и Just 4 >>= half, лучше заменить это на half 4 потому, что для Monad Maybe
return = Just
Примеры правильного использования
*Main> half 12
Just 6
*Main> half 12 >>= half
Just 3
*Main> [1..3] >>= show
"123"
*Main> [1..3] >>= \x -> replicate x x
[1,2,2,3,3,3]
*Main> getLine >>= print -- далее ввод с клавиатуры
12
"12"
*Main> getLine >>= \x -> print (half (read x)) -- далее ввод с клавиатуры
12
Just 6
И еще один момент: не забывайте указывать сигнатуры функций, это упростит вам понимание того, что у вас получилось (или не получилось)