종이접기 프로그래밍
하스켈 학교 2016년 6월 7일
서광열
출처: http://homeworks.tistory.com/533
리스트의 정의
data List a = Nil | Cons a (List a)
wrap :: a -> List a
wrap x = Cons x Nil
nil :: List a -> Bool
nil Nil = True
nil (Cons x xs) = False
Tree의 정의
data T a = L a | B (T a) (T a)
deriving (Show)
List fold
foldL :: (a -> b -> b) -> b -> List a -> b
foldL f e Nil = e
foldL f e (Cons x xs) = f x (foldL f e xs)
sumL = foldL (+) 0
productL = foldL (*) 1
reverseL = foldL (a r -> r ++ [a]) []
Tree fold
foldT :: (b -> b -> b) -> (a -> b) -> (T a -> b)
foldT _ l (L a) = l a
foldT b l (B s t) = b (foldT b l s)
(foldT b l t)
productT = foldT (*) id
fold의 예: fact
• 어셈블리
fact0 0 = 1
fact0 n = n * fact (n - 1)
• Structured programming: control structure를 data
structure와 combining form으로 교체
fact1 n = product [1..n]
fold의 예: 피보나치
• 어셈블리
fib0 0 = 0
fib0 1 = 1
fib0 n = fib0 (n - 1) * fib0 (n - 2)
fold의 예: 피보나치
fibT :: Integer
-> T Integer
fibT 0 = L 0
fibT 1 = L 1
fibT n = B (fibT (n - 1))
(fibT (n - 2))
sumT :: T Integer
-> Integer
sumT = foldT (+) id
fib1 = Integer
-> Integer
fib1 = sumT . fibT
foldL과 foldT의
공통점은?
Expr
data Expr = Const Int
| Add Expr Expr
| Mul Expr Expr
• Expr은 재귀 타입(recursive type)
ExprF
• Expr에서 재귀를 제거하면
data ExprF a = Const Int
| Add a a
| Mul a a
e.g., ExprF (ExprF (ExprF a)))
Fix
newtype Fix f = In (f (Fix f))
In :: f (Fix f) -> Fix f
Fix로 정의한 Expr
data Expr = Fix ExprF
val :: Fix ExprF
val = In (Const 12)
testExpr = In $ (In $ (In $ Const 2) `Add`
(In $ Const 3)) `Mul` (In $ Const 4)
ExprF Functor
instance Functor ExprF where
fmap eval (Const i) = Const i
fmap eval (left `Add` right) =
(eval left) `Add` (eval right)
fmap eval (left `Mul` right) =
(eval left) `Mul` (eval right)
alg
alg :: ExprF Int -> Int
alg (Const i) = i
alg (x `Add` y) = x + y
alg (x `Mul` y) = x * y
• Int는 캐리어 타입(carrier type)
alg’
alg' :: ExprF String -> String
alg' (Const i) = [chr (ord 'a' + i)]
alg' (x `Add` y) = x ++ y
alg' (x `Mul` y) =
concat [[a,b] | a <- x, b <- y]
• String은 캐리어 타입(carrier type)
F-Algebra
type Algebra f a = f a -> a
펑터 f와 캐리어 타입 a에 대해서 alg 함수 정의
• Algebra의 구성 요소
1. Functor
2. Carrier type
3. Evaluator
Algebra
type SimpleA = Algebra ExprF Int
-- alg :: SimpleA
alg :: ExprF Int -> Int
alg (Const i) = i
alg (x `Add` y) = x + y
alg (x `Mul` y) = x * y
Initial Algebra
• Int나 String 대신에 (Fix ExprF)를 캐리어 타입으로 사용
type ExprInitAlg = Algebra ExprF (Fix ExprF)
-- ex_init_alg :: ExprInitAlg
ex_init_alg :: ExprF (Fix ExprF) -> Fix ExprF
ex_init_alg = In
• 이 함수는 (ExprF Expr)을 주면 Expr을 리턴함
> ex_init_alg $ Add (In $ Const 2) (In $ Const 3)
In $ Add (In $ Const 2) (In $ Const 3)
Initial Algebra
• 계산 과정에서 정보의 손실 없음
• initial algebra is an isomorphism
• homomoprhism
• initial algebra -> 다른 algebra
Homomorphism
• homomorphism은 하나의 carrier type을 다른
carrier type으로 보내는 함수 g 하나로 결정
g :: Fix f -> a
Catamorphism
unFix :: Fix f -> f (Fix f)
unFix (In x) = x
g = alg . (fmap g) . unFix
Catamorphism
g :: Fix f -> a
g = alg . (fmap g) . unFix
• g = cata alg로 정의하면
cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg = alg . fmap (cata alg) . unFix
cata를 이용한 eval의 정
의
type SimpleA = Algebra
ExprF Int
alg :: SimpleA
alg (Const i) = i
alg (x `Add` y) = x + y
alg (x `Mul` y) = x * y
cata :: Functor f => (f a -> a) ->
Fix f -> a
cata alg = alg . fmap (cata alg) .
unFix
eval :: Fix ExprF -> Int
eval = alg . fmap eval . unFix
List 다시 보기
data ListF a b = Nil | Cons a b
instance Functor (ListF a) where
fmap f Nil = Nil
fmap f (Cons e x) = Cons e (f x)
algSum
algSum :: ListF Int Int -> Int
algSum Nil = 0
algSum (Cons e acc) = e + acc
lst :: Fix (ListF Int)
lst = In $ Cons 2 (In $ Cons 3 (In $ Cons 4 (In Nil)))
cata algSum lst
== foldr (e acc -> e + acc) 0 [2..4]
Tree 다시 보기
data TreeF a b = L a | B b b
deriving (Show)
instance Functor (TreeF a) where
fmap f (L a) = L a
fmap f (B x y) = B (f x) (f y)
algSum
algSum :: TreeF Int Int -
> Int
algSum (L a) = a
algSum (B x y) = x + y
> cata algSum tree
6
type IntTree = Fix (TreeF Int)
l :: Int -> IntTree
l x = In (L x)
b :: IntTree -> IntTree -> IntTree
b x y = In (x `B` y)
tree :: IntTree
tree = ((l 1) `b` (l 2)) `b` (l 3)
정리
1. 재귀 함수가 일반 함수의 fixed point로 정의되는 것처럼 재귀 타입은 일반
타입의 fixed point로 정의
2. functor는 nested data structure에 대한 recursive evaluation을 지원
3. F-algebra는 1) functor f, 2) carrier type a, 3) f a -> a로 가는 함수로 정의
4. initial algebra는 해당 functor로 정의되는 모든 algebra로 매핑 가능. 이
algebra의 carrier type은 functor의 fixed point
5. initial algebra와 다른 algebra 사이의 유일한 mapping은 catamorphism으
로 생성
6. catamorphism은 simple algebra를 받아서 nested data structure에 대한
recursive evaluator를 제공. 이는 list folding을 다른 데이터 타입으로 일반
화한 것.
참고 자료
1. Origami programming, Jeremy Gibbons
• https://www.cs.ox.ac.uk/jeremy.gibbons/publicatio
ns/origami.pdf
2. Understanding F-Algebras
• https://bartoszmilewski.com/2013/06/10/understa
nding-f-algebras/

종이접기(fold) 프로그래밍

  • 1.
  • 3.
  • 4.
    리스트의 정의 data Lista = Nil | Cons a (List a) wrap :: a -> List a wrap x = Cons x Nil nil :: List a -> Bool nil Nil = True nil (Cons x xs) = False
  • 5.
    Tree의 정의 data Ta = L a | B (T a) (T a) deriving (Show)
  • 6.
    List fold foldL ::(a -> b -> b) -> b -> List a -> b foldL f e Nil = e foldL f e (Cons x xs) = f x (foldL f e xs) sumL = foldL (+) 0 productL = foldL (*) 1 reverseL = foldL (a r -> r ++ [a]) []
  • 7.
    Tree fold foldT ::(b -> b -> b) -> (a -> b) -> (T a -> b) foldT _ l (L a) = l a foldT b l (B s t) = b (foldT b l s) (foldT b l t) productT = foldT (*) id
  • 8.
    fold의 예: fact •어셈블리 fact0 0 = 1 fact0 n = n * fact (n - 1) • Structured programming: control structure를 data structure와 combining form으로 교체 fact1 n = product [1..n]
  • 9.
    fold의 예: 피보나치 •어셈블리 fib0 0 = 0 fib0 1 = 1 fib0 n = fib0 (n - 1) * fib0 (n - 2)
  • 10.
    fold의 예: 피보나치 fibT:: Integer -> T Integer fibT 0 = L 0 fibT 1 = L 1 fibT n = B (fibT (n - 1)) (fibT (n - 2)) sumT :: T Integer -> Integer sumT = foldT (+) id fib1 = Integer -> Integer fib1 = sumT . fibT
  • 11.
  • 12.
    Expr data Expr =Const Int | Add Expr Expr | Mul Expr Expr • Expr은 재귀 타입(recursive type)
  • 13.
    ExprF • Expr에서 재귀를제거하면 data ExprF a = Const Int | Add a a | Mul a a e.g., ExprF (ExprF (ExprF a)))
  • 14.
    Fix newtype Fix f= In (f (Fix f)) In :: f (Fix f) -> Fix f
  • 15.
    Fix로 정의한 Expr dataExpr = Fix ExprF val :: Fix ExprF val = In (Const 12) testExpr = In $ (In $ (In $ Const 2) `Add` (In $ Const 3)) `Mul` (In $ Const 4)
  • 16.
    ExprF Functor instance FunctorExprF where fmap eval (Const i) = Const i fmap eval (left `Add` right) = (eval left) `Add` (eval right) fmap eval (left `Mul` right) = (eval left) `Mul` (eval right)
  • 17.
    alg alg :: ExprFInt -> Int alg (Const i) = i alg (x `Add` y) = x + y alg (x `Mul` y) = x * y • Int는 캐리어 타입(carrier type)
  • 18.
    alg’ alg' :: ExprFString -> String alg' (Const i) = [chr (ord 'a' + i)] alg' (x `Add` y) = x ++ y alg' (x `Mul` y) = concat [[a,b] | a <- x, b <- y] • String은 캐리어 타입(carrier type)
  • 19.
    F-Algebra type Algebra fa = f a -> a 펑터 f와 캐리어 타입 a에 대해서 alg 함수 정의 • Algebra의 구성 요소 1. Functor 2. Carrier type 3. Evaluator
  • 20.
    Algebra type SimpleA =Algebra ExprF Int -- alg :: SimpleA alg :: ExprF Int -> Int alg (Const i) = i alg (x `Add` y) = x + y alg (x `Mul` y) = x * y
  • 21.
    Initial Algebra • Int나String 대신에 (Fix ExprF)를 캐리어 타입으로 사용 type ExprInitAlg = Algebra ExprF (Fix ExprF) -- ex_init_alg :: ExprInitAlg ex_init_alg :: ExprF (Fix ExprF) -> Fix ExprF ex_init_alg = In • 이 함수는 (ExprF Expr)을 주면 Expr을 리턴함 > ex_init_alg $ Add (In $ Const 2) (In $ Const 3) In $ Add (In $ Const 2) (In $ Const 3)
  • 22.
    Initial Algebra • 계산과정에서 정보의 손실 없음 • initial algebra is an isomorphism • homomoprhism • initial algebra -> 다른 algebra
  • 23.
    Homomorphism • homomorphism은 하나의carrier type을 다른 carrier type으로 보내는 함수 g 하나로 결정 g :: Fix f -> a
  • 24.
    Catamorphism unFix :: Fixf -> f (Fix f) unFix (In x) = x g = alg . (fmap g) . unFix
  • 25.
    Catamorphism g :: Fixf -> a g = alg . (fmap g) . unFix • g = cata alg로 정의하면 cata :: Functor f => (f a -> a) -> Fix f -> a cata alg = alg . fmap (cata alg) . unFix
  • 26.
    cata를 이용한 eval의정 의 type SimpleA = Algebra ExprF Int alg :: SimpleA alg (Const i) = i alg (x `Add` y) = x + y alg (x `Mul` y) = x * y cata :: Functor f => (f a -> a) -> Fix f -> a cata alg = alg . fmap (cata alg) . unFix eval :: Fix ExprF -> Int eval = alg . fmap eval . unFix
  • 27.
    List 다시 보기 dataListF a b = Nil | Cons a b instance Functor (ListF a) where fmap f Nil = Nil fmap f (Cons e x) = Cons e (f x)
  • 28.
    algSum algSum :: ListFInt Int -> Int algSum Nil = 0 algSum (Cons e acc) = e + acc lst :: Fix (ListF Int) lst = In $ Cons 2 (In $ Cons 3 (In $ Cons 4 (In Nil))) cata algSum lst == foldr (e acc -> e + acc) 0 [2..4]
  • 29.
    Tree 다시 보기 dataTreeF a b = L a | B b b deriving (Show) instance Functor (TreeF a) where fmap f (L a) = L a fmap f (B x y) = B (f x) (f y)
  • 30.
    algSum algSum :: TreeFInt Int - > Int algSum (L a) = a algSum (B x y) = x + y > cata algSum tree 6 type IntTree = Fix (TreeF Int) l :: Int -> IntTree l x = In (L x) b :: IntTree -> IntTree -> IntTree b x y = In (x `B` y) tree :: IntTree tree = ((l 1) `b` (l 2)) `b` (l 3)
  • 31.
    정리 1. 재귀 함수가일반 함수의 fixed point로 정의되는 것처럼 재귀 타입은 일반 타입의 fixed point로 정의 2. functor는 nested data structure에 대한 recursive evaluation을 지원 3. F-algebra는 1) functor f, 2) carrier type a, 3) f a -> a로 가는 함수로 정의 4. initial algebra는 해당 functor로 정의되는 모든 algebra로 매핑 가능. 이 algebra의 carrier type은 functor의 fixed point 5. initial algebra와 다른 algebra 사이의 유일한 mapping은 catamorphism으 로 생성 6. catamorphism은 simple algebra를 받아서 nested data structure에 대한 recursive evaluator를 제공. 이는 list folding을 다른 데이터 타입으로 일반 화한 것.
  • 32.
    참고 자료 1. Origamiprogramming, Jeremy Gibbons • https://www.cs.ox.ac.uk/jeremy.gibbons/publicatio ns/origami.pdf 2. Understanding F-Algebras • https://bartoszmilewski.com/2013/06/10/understa nding-f-algebras/