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

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

Upload: alexander-granin

Post on 27-Jul-2015

71 views

Category:

Education


3 download

TRANSCRIPT

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

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

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

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

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

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

Циклы

Базовые типы

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

Декларация

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

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

Рекурсия

Базовые типы

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

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

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

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

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

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

Интерфейсы

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

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

Значение АТД

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

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

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

???

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

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

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

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

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

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

Интерфейсы

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

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

Значение АТД

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

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

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

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

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

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

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

● 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 Идиома

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

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

Zippers

http

://de

sign

.vid

anto

.com

/?p=

225

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

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

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

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

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 =

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

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

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

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/

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