FP_HSE2020Fall_10
module Fp10 where | |
import Data.Function ((&)) | |
import Control.Monad.Identity (Identity(Identity)) | |
import Data.Monoid (First(..)) | |
-- Из произвольной функции с помощью return | |
-- можно сделать стрелку Клейсли | |
toKleisli :: Monad m => (a -> b) -> (a -> m b) | |
toKleisli f = \x -> return (f x) | |
---------------------------------------- | |
-- монада Identity | |
wrap'n'succ :: Integer -> Identity Integer | |
wrap'n'succ x = Identity (succ x) | |
{- | |
GHCi> runIdentity $ wrap'n'succ 3 >>= wrap'n'succ >>= wrap'n'succ | |
6 | |
GHCi> runIdentity $ wrap'n'succ 3 >>= (\x -> wrap'n'succ x >>= wrap'n'succ) | |
6 | |
-} | |
goWrap0 :: Identity Integer | |
goWrap0 = wrap'n'succ 3 >>= | |
wrap'n'succ >>= | |
wrap'n'succ >>= | |
return | |
-- применим третий закон монад несколько раз | |
goWrap1 :: Identity Integer | |
goWrap1 = wrap'n'succ 3 >>= (\x -> | |
wrap'n'succ x >>= (\y -> | |
wrap'n'succ y >>= \z -> | |
return z)) | |
-- можем обращаться к распакованным результатам промежуточных вычислений | |
goWrap2 :: Identity (Integer,Integer,Integer) | |
goWrap2 = wrap'n'succ 3 >>= (\x -> | |
wrap'n'succ x >>= (\y -> | |
wrap'n'succ y >>= \z -> | |
return (x,y,z))) | |
-- let-связывание позволяет использовать обычные переменные | |
goWrap3 :: Identity (Integer,Integer,Integer,Integer) | |
goWrap3 = let i = 3 in | |
wrap'n'succ i >>= (\x -> | |
wrap'n'succ x >>= (\y -> | |
wrap'n'succ y >>= \z -> | |
return (i,x,y,z))) | |
-- если результат какого-то вычисления не интересен, переменную можно не вводить | |
goWrap4 :: Identity (Integer,Integer,Integer) | |
goWrap4 = let i = 3 in | |
wrap'n'succ i >>= (\x -> | |
wrap'n'succ x >>= (\y -> | |
wrap'n'succ y >> | |
return (i,x,y))) | |
-- то же самое удобно записывать в do-нотации | |
goWrap5 :: Identity (Integer,Integer,Integer) | |
goWrap5 = do | |
let i = 3 | |
x <- wrap'n'succ i | |
y <- wrap'n'succ x | |
wrap'n'succ y | |
return (i,x,y) | |
goWrap5' :: Identity (Integer,Integer) | |
goWrap5' = do | |
let i = 3 | |
x <- wrap'n'succ i | |
x <- wrap'n'succ x | |
x <- wrap'n'succ x | |
return (i,x) | |
---------------------------------------- | |
-- монада Maybe | |
type Name = String | |
type ParentsTable = [(Name,Name)] | |
fathers, mothers :: ParentsTable | |
fathers = [("Bill","John"),("Ann","John"), ("John","Piter")] | |
mothers = [("Bill","Jane"),("Ann","Jane"), ("John","Alice"),("Jane","Dorothy"), ("Alice","Mary")] | |
-- стрелки Клейсли для Maybe | |
getM, getF :: Name -> Maybe Name | |
getM person = lookup person mothers | |
getF person = lookup person fathers | |
{- | |
ищем прабабушку по материнской линии отца | |
GHCi> getF "Bill" >>= getM >>= getM | |
Just "Mary" | |
GHCi> do {f <- getF "Bill"; m <- getM f; getM m} | |
Just "Mary" | |
-} | |
-- Ищем бабушек | |
granmas :: Name -> Maybe (Name, Name) | |
granmas person = do | |
m <- getM person | |
gmm <- getM m | |
f <- getF person | |
gmf <- getM f | |
return (gmm, gmf) | |
{- | |
GHCi> granmas "Ann" | |
Just ("Dorothy","Alice") | |
GHCi> granmas "John" | |
Nothing | |
-} | |
---------------------------------------- | |
-- монада списка | |
-- Следующие три списка --- это одно и то же | |
list1 = [(x,y) | x <- [1,2,3] , y <- [1,2,3], x /= y] | |
list2 = do | |
x <- [1,2,3] | |
y <- [1,2,3] | |
True <- return (x /= y) | |
return (x,y) | |
list3 = | |
[1,2,3] >>= (\x -> | |
[1,2,3] >>= (\y -> | |
return (x/=y) >>= (\r -> | |
case r of True -> return (x,y) | |
_ -> fail "Will be ignored :)"))) | |
----------------------------------------- | |
-- Класс типов MonadFail | |
{- | |
Вызов fail в современном GHC (>=8.6) ссылается на | |
функцию из класса типов Control.Monad.Fail.MonadFail: | |
class Monad m => MonadFail m where | |
fail :: String -> m a | |
При реализации представителей Monad функцию fail пока еще | |
допустимо реализовывать, но уже бессмысленно. | |
instance MonadFail Maybe where | |
fail _ = Nothing | |
-} | |
{- | |
GHCi> do {3 <- Just 5; return 'Z'} | |
Nothing | |
GHCi> do {3 <- Identity 5; return 'Z'} | |
<interactive>:3:5: error: | |
* No instance for (Control.Monad.Fail.MonadFail Identity) | |
arising from a do statement | |
with the failable pattern `3' | |
* In a stmt of a 'do' block: 3 <- Identity 5 | |
In the expression: | |
do 3 <- Identity 5 | |
return 'Z' | |
In an equation for `it': | |
it | |
= do 3 <- Identity 5 | |
return 'Z' | |
Сообщение об ошибке выдает тайпчекер, код не пройдет компиляцию. | |
do-нотация транслируется в Haskell Kernel по-разному, в | |
зависимости от того является ли образец "failable" или нет: | |
GHCi> :t do {x <- return 5; return 'Z'} | |
do {x <- return 5; return 'Z'} :: Monad m => m Char | |
GHCi> :t do {3 <- return 5; return 'Z'} | |
do {3 <- return 5; return 'Z'} | |
:: Control.Monad.Fail.MonadFail m => m Char | |
GHCi> :t do {~3 <- return 5; return 'Z'} | |
do {~3 <- return 5; return 'Z'} :: Monad m => m Char | |
Неопровержимые образцы не являются "failable". | |
data с одним конструктором и newtype не "failable" сами по | |
себе, но могут оказаться "failable" при вложении образцов. | |
GHCi> :t do {(s,x) <- return ("Answer",42); return 'Z'} | |
do {(s,x) <- return ("Answer",42); return 'Z'} :: Monad m => m Char | |
GHCi> :t do {First x <- return (First (Just 42)); return 'Z'} | |
do {First x <- return (First (Just 42)); return 'Z'} | |
:: Monad m => m Char | |
GHCi> :t do {(s,42) <- return ("Answer",42); return 'Z'} | |
do {(s,42) <- return ("Answer",42); return 'Z'} | |
:: Control.Monad.Fail.MonadFail m => m Char | |
GHCi> :t do {Left x <- return (Left 42); return 'Z'} | |
do {Left x <- return (Left 42); return 'Z'} | |
:: Control.Monad.Fail.MonadFail m => m Char | |
-} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment