Skip to content

Instantly share code, notes, and snippets.

@deniok

deniok/Fp10.hs Secret

Last active November 18, 2020 13:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deniok/59704716199fefaa7e0068723f75525f to your computer and use it in GitHub Desktop.
Save deniok/59704716199fefaa7e0068723f75525f to your computer and use it in GitHub Desktop.
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