Идиоматичный функциональный код
-
Upload
alexander-granin -
Category
Education
-
view
79 -
download
3
Transcript of Идиоматичный функциональный код
Изменяемое состояние
Присваивание
Сторонние эффекты
Методы, функции
Циклы
Базовые типы
Иммутабельность
Декларация
Чистые функции
Функции, лямбды
Рекурсия
Базовые типы
Императивное программирование
Функциональное программирование
Класс = поля + методы
Объект класса
Наследование, ассоциация
Функторы, делегаты
Интерфейсы
ООП-паттерны
Модуль = типы + функции
Значение АТД
Комбинаторы, композиция
Лямбды, ФВП, продолжения
Обобщение типов
???
Объектно - ориентированное программирование
Функциональное программирование
Класс = поля + методы
Объект класса
Наследование, ассоциация
Функторы, делегаты
Интерфейсы
ООП-паттерны
Модуль = типы + функции
Значение АТД
Комбинаторы, композиция
Лямбды, ФВП, продолжения
Обобщение типов
Функциональные идиомы
Объектно - ориентированное программирование
Функциональное программирование
Функциональные идиомы
● Foldable, Traversable● Functors● Applicative Functors● Monoids● Monads● ...
● Existential types● Lenses● Zippers● Comonads● GATDs● ...
Функциональные идиомы
● Foldable, Traversable● Functors● Applicative Functors● Monoids● Monads
● Existential types● Lenses● Zippers● Comonads● GATDs
В чем разница между понятиями“ООП-паттерн” и “ФП-идиома”?
ООП-паттерн vs Идиома
Паттерн: подход “снаружи”.Несколько классов связываются в единую систему с обобщающими интерфейсами.
Идиома: подход “изнутри”.Идиома структурирует данные, обобщает и пополняет их свойства.
Список-- Список на АТД:data List a = Empty | Cons a (List a)myList1 = Cons 1 (Cons 2 (Cons 3 Empty))
-- Списки в Haskell:myList1 = [1, 2, 3]myList2 = 1 : 2 : 3 : []myList3 = 1 : [2, 3]
http
://le
arny
ouah
aske
ll.co
m/z
ippe
rs
Zipper для спискаdata Zipper a = Zip [a] a [a]
toLeft (Zip xs a (y:ys)) = Zip (a:xs) y ys
toRight (Zip (x:xs) a ys) = Zip xs x (a:ys)
extract (Zip _ a _) = a
http
://le
arny
ouah
aske
ll.co
m/z
ippe
rs
Zipper для спискаzipper = Zip [] 0 [1..10] > toLeft zipperZip [0] 1 [2, 3, 4, 5, 6, 7, 8, 9, 10]
> extract (toLeft (toLeft zipper))2
data Tree a = Empty | Node a (Tree a) (Tree a)
data Direction = L | Rmodify :: (a -> a) -> Tree a -> [Direction] -> Tree a
Дерево
12
5 3
12
50 30
data Tree a = Empty | Node a (Tree a) (Tree a)
data Direction = L | Rmodify :: (a -> a) -> Tree a -> [Direction] -> Tree a
newTree1 = modify (*10) myTree [R, L]newTree2 = modify (*10) newTree1 [R, R]
Изменение дерева
12
50 30
data Tree a = Empty | Node a (Tree a) (Tree a)
data NodeCtx a = LCtx a (Tree a) | RCtx a (Tree a)
data TreeZipper a = TZ (Tree a) [NodeCtx a]
extract (TZ (Node a _ _) _) = a
Zipper для дерева
goLeft :: TreeZipper a -> TreeZipper agoLeft (TZ (Node a l r) ctxs) = (TZ l (LCtx a r : ctxs))
goRight :: TreeZipper a -> TreeZipper agoRight (TZ (Node a l r) ctxs) = (TZ r (RCtx a l : ctxs))
goUp :: TreeZipper a -> TreeZipper agoUp = ...
Zipper для дерева
2
35 RCtx 1
> goRight zipper
TZ (Node 2 (Node 5 Empty Empty) (Node 3 Empty Empty)) [RCtx 1 Empty]
1
2
35
tree = Node 1 Empty (Node 2 (Node 5 Empty Empty) (Node 3 Empty Empty))
zipper = TZ tree []
fromZipper :: TreeZipper a -> Tree afromZipper (TZ cur []) = curfromZipper z = fromZipper (goUp z)
Сборка дерева
RCtx 1
LCtx 2 3
5
1
2
35
data TreeDir = U | L | R modify :: (a -> a) -> TreeZipper a -> [TreeDir] -> TreeZipper a
modify f (TZ (Node a l r) ctxs) [] = TZ (Node (f a) l r) ctxsmodify f z (L:dirs) = modify f (goLeft z) dirsmodify f z (R:dirs) = modify f (goRight z) dirsmodify f z (U:dirs) = modify f (goUp z) dirs
Изменение дерева
tree = Node 1 Empty (Node 2 (Node 5 Empty Empty) (Node 3 Empty Empty))
zipper = TZ tree []
newZipper1 = modify (*10) zipper [R, L]newZipper2 = modify (*10) newZipper1 [U, R]newTree = fromZipper newZipper
Изменение дерева 12
5 3
12
50 30
Комонады
http
s://g
reyf
og.fi
les.
wor
dpre
ss.c
om/2
010/
02/e
splo
so-c
ellu
lar-
auto
mat
a.jp
g
“Жизнь” без идиомtype Cell = (Int, Int)type Grid = [Cell]
step :: Grid -> Gridstep p = let next all [] = [] next all cur@((aX, aY) : alives) = [(x, y) | x <- lim aX, y <- lim aY, length (neighbours8 (x, y) all) == 3] ++ (next all alives) alive all cell = length (neighbours8 cell all) `elem` [2,3] in L.nub $ filter (alive p) p ++ (next p p)
“Жизнь” без идиомtype Cell = (Int, Int)type Grid = [Cell]
step :: Grid -> Gridstep p = let next all [] = [] next all cur@((aX, aY) : alives) = [(x, y) | x <- lim aX, y <- lim aY, length (neighbours8 (x, y) all) == 3] ++ (next all alives) alive all cell = length (neighbours8 cell all) `elem` [2,3] in L.nub $ filter (alive p) p ++ (next p p)
这是什么?
“Жизнь” на монадах
type Cell = (Int, Int)type Grid = [Cell]
step :: Grid -> Grid
step cells = do
(newCell, n) <- frequencies $ concatMap neighbours cells
guard $ (n == 3) || (n == 2 && newCell `elem` cells)
return newCell
http
://rh
nh.n
et/2
012/
01/0
2/co
nway
's-g
ame-
of-li
fe-in
-has
kell
“Жизнь” на комонадах и зипперахdata Universe a = Universe [a] a [a]data Cell = Dead | Alivenewtype Grid = Grid (Universe (Universe Cell))
rule :: Grid Cell -> Cellrule grid | nc == 2 = extract grid | nc == 3 = Alive | otherwise = Dead where nc = length $ filter (==Alive) (neighbours grid)
next grid = grid =>> rule
http
://ha
brah
abr.r
u/po
st/2
2547
3/
Правило выводаtype Segment = (Float, Float)type Segments = [(Float, Float)]
cantorRule :: Segment -> SegmentscantorRule (x1, x2) = let len = x2 - x1 oneThird = len / 3.0 in [(x1, x1 + oneThird), (x2 - oneThird, x2)]
Фрактал - список списковcantorGen :: Segments -> SegmentscantorGen segs = concatMap cantorRule segs
fractal :: [Segments]fractal = iterate cantorGen [(0.0, 0.9)]
> take 2 fractal[ [(0.0,0.9)], [(0.0,0.3),(0.6,0.9)] ]
data Layer a = Layer a
comonadCantorRule :: Layer Segments -> SegmentscomonadCantorRule layer = cantorGen (extract layer)
comonadCantorGen :: Layer Segments -> Layer SegmentscomonadCantorGen layer = layer =>> comonadCantorRule
> take 2 $ iterate comonadCantorGen cantorLayer[ Layer [(0.0,9.0)], Layer [(0.0,3.0),(6.0,9.0)] ]
Фрактал - список слоев
Определение комонадыclass Functor w => Comonad w where extract :: w a -> a duplicate :: w a -> w (w a) extend :: (w a -> b) -> w a -> w b extend f = fmap f . duplicate duplicate = extend id
(=>>) :: Comonad w => w a -> (w a -> b) -> w bcx =>> rule = extend rule cx
Простейшая комонада Layerdata Layer a = Layer a
instance Functor Layer where fmap f (Layer a) = Layer (f a)
instance Comonad Layer where duplicate (Layer a) = Layer (Layer a) -- w a -> w (w a) extract (Layer a) = a -- w a -> a
Laye
r
Segments
Laye
r
Segments
Laye
r
duplicate =
Laye
r
Segments Segmentsextract =
duplicate :: w a -> w (w a)
extract :: w a -> a
=La
yer
Segments
Laye
r
comonadRule =
Laye
r
Segments
Laye
r
Segments =>> comonadRule =
=>> :: w a -> (w a -> b) -> w bcomonadRule :: (w a -> b)
Laye
r
Segments
Laye
r
= fmap comonadRule =
Зипперы - это комонады
http
://ha
brah
abr.r
u/po
st/2
2547
3/data Universe a = Universe [a] a [a]
left, right :: Universe a -> Universe aleft (Universe (a:as) x bs) = Universe as a (x:bs)right (Universe as x (b:bs)) = Universe (x:as) b bs
extract :: Universe a -> aextract (Universe _ x _) = xduplicate :: Universe a -> Universe (Universe a)duplicate u = Universe (tail $ iterate left u) u (tail $ iterate right u)
Зиппер зипперов чиселuniverse = Universe [-1, -2..] 0 [1, 2..]universeOfUniverses = duplicate universe
http
://ha
brah
abr.r
u/po
st/2
2547
3/