-
-
Save deniok/59704716199fefaa7e0068723f75525f to your computer and use it in GitHub Desktop.
FP_HSE2020Fall_10
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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