Top Banner
Идиоматичный функциональный код Александр Гранин [email protected]
37

Идиоматичный функциональный код

Jul 27, 2015

Download

Education

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Идиоматичный функциональный код

Идиоматичный функциональный код

Александр Гранин[email protected]

Page 2: Идиоматичный функциональный код

Изменяемое состояние

Присваивание

Сторонние эффекты

Методы, функции

Циклы

Базовые типы

Иммутабельность

Декларация

Чистые функции

Функции, лямбды

Рекурсия

Базовые типы

Императивное программирование

Функциональное программирование

Page 3: Идиоматичный функциональный код

Класс = поля + методы

Объект класса

Наследование, ассоциация

Функторы, делегаты

Интерфейсы

ООП-паттерны

Модуль = типы + функции

Значение АТД

Комбинаторы, композиция

Лямбды, ФВП, продолжения

Обобщение типов

???

Объектно - ориентированное программирование

Функциональное программирование

Page 4: Идиоматичный функциональный код

Класс = поля + методы

Объект класса

Наследование, ассоциация

Функторы, делегаты

Интерфейсы

ООП-паттерны

Модуль = типы + функции

Значение АТД

Комбинаторы, композиция

Лямбды, ФВП, продолжения

Обобщение типов

Функциональные идиомы

Объектно - ориентированное программирование

Функциональное программирование

Page 5: Идиоматичный функциональный код

Функциональные идиомы

● Foldable, Traversable● Functors● Applicative Functors● Monoids● Monads● ...

● Existential types● Lenses● Zippers● Comonads● GATDs● ...

Page 6: Идиоматичный функциональный код

Функциональные идиомы

● Foldable, Traversable● Functors● Applicative Functors● Monoids● Monads

● Existential types● Lenses● Zippers● Comonads● GATDs

В чем разница между понятиями“ООП-паттерн” и “ФП-идиома”?

Page 7: Идиоматичный функциональный код

ООП-паттерн vs Идиома

Паттерн: подход “снаружи”.Несколько классов связываются в единую систему с обобщающими интерфейсами.

Идиома: подход “изнутри”.Идиома структурирует данные, обобщает и пополняет их свойства.

Page 8: Идиоматичный функциональный код

Zippers

http

://de

sign

.vid

anto

.com

/?p=

225

Page 9: Идиоматичный функциональный код

Список-- Список на АТД: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

Page 10: Идиоматичный функциональный код

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

Page 11: Идиоматичный функциональный код

Zipper для спискаzipper = Zip [] 0 [1..10] > toLeft zipperZip [0] 1 [2, 3, 4, 5, 6, 7, 8, 9, 10]

> extract (toLeft (toLeft zipper))2

Page 12: Идиоматичный функциональный код

Zip [2, 1, 0] 3 [4..10]

Текущий элемент

Сохраненный Контекст

Page 13: Идиоматичный функциональный код

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

Page 14: Идиоматичный функциональный код

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

Page 15: Идиоматичный функциональный код

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 для дерева

Page 16: Идиоматичный функциональный код

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 для дерева

Page 17: Идиоматичный функциональный код

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 []

Page 18: Идиоматичный функциональный код

fromZipper :: TreeZipper a -> Tree afromZipper (TZ cur []) = curfromZipper z = fromZipper (goUp z)

Сборка дерева

RCtx 1

LCtx 2 3

5

1

2

35

Page 19: Идиоматичный функциональный код

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

Изменение дерева

Page 20: Идиоматичный функциональный код

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

Page 21: Идиоматичный функциональный код

Комонады

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

Page 22: Идиоматичный функциональный код

“Жизнь” без идиом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)

Page 23: Идиоматичный функциональный код

“Жизнь” без идиом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)

这是什么?

Page 24: Идиоматичный функциональный код

“Жизнь” на монадах

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

Page 25: Идиоматичный функциональный код

“Жизнь” на комонадах и зипперах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/

Page 26: Идиоматичный функциональный код

Множество Кантора на комонадах

Page 27: Идиоматичный функциональный код

Правило вывода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)]

Page 28: Идиоматичный функциональный код

Фрактал - список списков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)] ]

Page 29: Идиоматичный функциональный код

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)] ]

Фрактал - список слоев

Page 30: Идиоматичный функциональный код

Определение комонады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

Page 31: Идиоматичный функциональный код

Простейшая комонада 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

Page 32: Идиоматичный функциональный код

Laye

r

Segments

Laye

r

Segments

Laye

r

duplicate =

Laye

r

Segments Segmentsextract =

duplicate :: w a -> w (w a)

extract :: w a -> a

Page 33: Идиоматичный функциональный код

=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 =

Page 34: Идиоматичный функциональный код

Спасибо за внимание!

Александр Гранин[email protected]

Page 35: Идиоматичный функциональный код

Зипперы - это комонады

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)

Page 36: Идиоматичный функциональный код

Зиппер зипперов чиселuniverse = Universe [-1, -2..] 0 [1, 2..]universeOfUniverses = duplicate universe

http

://ha

brah

abr.r

u/po

st/2

2547

3/

Page 37: Идиоматичный функциональный код

1

2

35

> goLeft (goRight zipper)

TZ (Node 5 Empty Empty) [ LCtx 2 (Node 3 Empty Empty) , RCtx 1 Empty]

RCtx 1

LCtx 2 3

5