하스켈 프로그래밍입문 2하스켈 학교 2016 년 5 월 21 일서광열
함수 합성(.): 두 함수를 인자로 받아 두 함수의 합성 함수를 리턴함(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \x -> f (g x)
odd :: Int -> Bool
odd = not . even
$ 를 이용한 함수 application
($) :: (a -> b) -> a -> b
f $ x = f x
> map ($ 3) [(4+), (10*), (^2), sqrt]
[7.0,30.0,9.0,1.7320508075688772]
($): 함수 f 와 인자 a 를 받아 f a 를 호출함
Type synonym• type String = [Char]
• type Pos = (Int, Int)origin :: Pos
origin = (0, 0)
left :: Pos -> Pos
left (x, y) = (x-1, y)
Type synonym
• type Pair a = (a, a)
mult :: Pair Int -> Int
mult (m, n) = m *n
copy :: a -> Pair a
copy x = (x, x)
Type synonym• Can be nested
• type Trans = Pos -> Pos
• Can't be recursive
• type Tree = (Int, [Tree])
Algebraic Data Type
Algebraic Data Typedata Answer = Yes | No | Unknown
answers :: [Answer]
answers = [Yes, No, Unknown]
flip :: Answer -> Answer
flip Yes = No
flip No = Yes
flip Unknown = Unknown
Algebraic Data Typedata Shape = Circle Float
| Rect Float Float
square :: Float -> Shape
square n = Rect n n
area :: Shape -> Float
area (Circle r) = pi * r^2
area (Rect x y) = x * y
Circle :: Float -> Shape
Rect :: Float -> Float -> Shape
재귀 타입 (Succ)data Nat = Zero | Succ Nat
Zero -- 0
Succ Zero -- 1
Succ (Succ Zero) -- 2
Succ (Succ (Succ Zero)) -- 3
재귀 타입 (Succ)nat2int :: Nat -> Int
nat2int Zero = 0
nat2int (Succ n) = 1 + nat2int n
int2nat :: Int -> Nat
int2nat 0 = Zero
int2nat n = Succ (int2nat (n-1))
재귀 타입 (Succ)-- converting to and from integers
add :: Nat -> Nat -> Nat
add m n = int2nat (nat2int m + nat2int n)
-- using recursion
add Zero n = n
add (Succ m) n = Succ (add m n)
재귀 타입 (Succ)
add (Succ (Succ Zero)) (Succ Zero)
= Succ (add (Succ Zero) (Succ Zero)
= Succ (Succ (add Zero (Succ Zero))
= Succ (Succ (Succ Zero))
재귀 타입 (Expr)data Expr = Val Int
| Add Expr Expr
| Mul Expr Expr
-- 1 + (2 * 3)
Add (Val 1) (Mul (Val 2) (Val 3))
재귀 타입 (Expr)size :: Expr -> Int
size (Val n) = 1
size (Add x y) = size x + size y
size (Mul x y) = size x + size y
eval :: Expr -> Int
eval (Var n) = n
eval (Add x y) = eval x + eval y
eval (Mul x y) = eval x * eval y
재귀 타입 (Binary Tree)data Tree a = Leaf a
| Node (Tree a) a (Tree a)
t: Tree Int
t = Node (Node (Leaf 1) 3 (Leaf 4)) 5
(Node (Leaf 6) 7 (Leaf 9))
재귀 타입 (Binary Tree)occurs :: Eq a => a -> Tree a -> Bool
occurs x (Leaf y) = x == y
occurs x (Node l y r) = x == y
|| occurs x l
|| occurs x r
재귀 타입 (Binary Tree)flatten :: Tree a -> [a]
flatten (Leaf x) = [x]
flatten (Node l x r) = flatten l
++ [x]
++ flatten r
Algebraic Data Type 요약종류 예제
Enumeration data Season = Summer | Winter | Autumn | Spring
Product data Pair = Pair Int Int
Sum data Shape = Circle Float | Rect Float Float
Polymorphic & Recursive
data Tree a = Leaf a | Node (Tree a) a (Tree a)
Record
data Person = Person { firstName :: String
, lastName :: String
, age :: Int
}
• 각 필드에 이름을 부여• 필드 이름은 해당 필드 값을 꺼내는 getter 함수가 됨
Record 생성 및 getter> :t Person
Person :: String -> String -> Int -> Person
> :t firstName
firstName :: Person -> String
> :t lastName
lastName :: Person -> String
> :t age
age :: Person -> Int
Record> let p = Person { firstName = "Kwang", lastName = "Seo", age = 20 }
> firstName p
"Kwang"
> age p
20
Record 패턴 매칭fullName :: Person -> String
fullName (Person {firstName = fn, lastName = ln}) =
fn ++ " " ++ ln
deriving• Show, Eq 등의 타입 클래스 인스턴스를 자동으로 생성
data Expr = Const Int
| Add Expr Expr
deriving (Eq, Show)
타입 클래스class Eq a where
(==) :: a -> a -> Bool
타입 클래스data Expr = Const Int
| Add Expr Expr
instance Eq Expr where
(Const _) == (Add _) = False
(Add _) == (Const _) = False
(Const i) == (Const i') = i == i'
(Add x y) == (Add x' y') == x == x' && y == y'
타입 클래스class Eq a where
(==), (/=) :: a -> a -> Bool
x/=y = not (x==y)
x==y = not (x/=y)
(==) 와 (/=) 중 하나만 구현해도 됨
타입 클래스와 C#/Java 인터페이스 비교C#/Java 하스켈
Class -
Interface Type class
Interface member Type-class member
Interface implementation Type-class instance
타입 클래스의 특징• 구현을 따로 지정• 구현 타입에 대한 명시적 지정
• 여러 개의 레퍼런스 (binary method)
• 결과 값에 대한 레퍼런스 (static method)
• 멤버에 대한 디폴트 구현• 여러 개의 타입 인자
타입 클래스 메소드의 종류
> :t show
(Show a) => a -> String
> :t read
(Read a) => String -> a
> :t (==)
(Eq a) => a -> a -> Bool
• instance: show
• static: read
• binary: (==)
하스켈 타입클래스들
https://www.cse.iitb.ac.in/~as/fpcourse/haskell98_report/basic.html
IO• pure expression 과 impure action 을 구분• IO a
• 액션을 수행하고 a 타입을 리턴• 예 ) IO Char, IO ()
간단한 액션• getChar :: IO Char
• 키보드에서 문자 하나를 읽어 리턴함• putChar :: Char -> IO ()
• putChar c 는 화면에 문자 c 를 출력함• return :: a -> IO a
• return v 는 아무런 액션을 하지 않고 v 를 리턴함
do 표기법do 표기법을 이용해 여러 개의 액션을 하나의 액션으로 만들 수 있음act :: IO (Char, Char)
act = do x <- getChar
getChar
y <- getChar
return (x, y)
getLinegetLine :: IO String
getLine = do x <- getChar
if x == '\n' then
return []
else
do xs <- getLine
return (x:xs)
putStr/putStrLnputStr :: String -> IO ()
putStr [] = return ()
putStr (x:xs) = do putChar x
putStr xs
putStrLn :: String -> IO ()
putStrLn = do putStr xs
putChar '\n'
Maybe• 함수의 리턴값이 보장되어 있지 않을 때 사용> import Data.List
> :t elemIndex
elemIndex :: Eq a => a -> [a] -> Maybe Int
> elemIndex ‘b’ "abc"
Just 1
> elemIndex ‘z’ "abc"
Nothing
Maybe 의 콘텍스트• Just 1 과 1
• Just "Hello" 와 "Hello"
• Just ‘a’ 와 ‘ a’
• Nothing 과 에러
Maybe
safediv :: Int -> Int -> Maybe Int
safediv _ 0 = Nothing
safediv m n = Just (m `div` n)
safehead :: [a] -> Maybe a
safehead [] = Nothing
safehead xs = Just (head xs)
data Maybe a = Nothing | Just a
Higher Orderdata RoseTree a = RLeaf a
| RNode [RoseTree a]
data BinTree = BLeaf a
| BNode (Pair (BinTree a))
data Pair a = MkPair a a
Higher Order
data RoseTree a = RLeaf a
| RNode ([] (RoseTree a))
data BinTree = BLeaf a
| BNode (Pair (BinTree a))
— [] :: * -> * The list constructor
Syntactic Sugar 를 제거하면 ?
Higher Order
data Tree k a = Leaf a
| Node (k (Tree k a))
data RoseTree a = Tree [] a
data BinTree = Tree Pair a
data AnnTree = Tree AnnPair a
data Pair a = P a a
data AnnPair a = AP String a a
변화하는 부분을 인자로 추출하면 ?• ‘a’ 는 type (a :: *)
• ‘k’ 는 type constructor (k :: * -> *)
Higher Order • Maybe a
• Type
• Kind : *
• Maybe
• Type Constructor
• Kind : * -> *
Functor
class Functor f where
fmap :: (a -> b) -> f a -> f b
리스트에 대한 Functor> import Data.Char
> fmap (+1) [1,2,3]
[2,3,4]
> fmap toUpper "qwertyuiop"
"QWERTYUIOP"
Maybe 에 대한 Functor
> fmap (+1) (Just 1)
Just 2
Maybe 에 대한 Functor
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
Identity 에 대한 Functor
newtype Identity a = Identity a
튜플에 대한 Functor
instance MyFunctor ((,) a) where
{- ... -}
( 리뷰 ) 튜플의 생성• 두 개 인자를 받아 두 인자로 구성된 튜플을 리턴
• (,) :: a -> b -> (a, b)
튜플의 partial application
> :type (,) ‘X’ True
(,) ‘X’ True :: (Char, Bool)
> :type (,) ‘X’
(,) ‘X" :: (Char, b)
튜플과 타입 시그너처• (,) 를 prefix 로 함수로 사용할 수 있음• (,) 를 type constructor 로 사용할 수 있음foo :: b -> (,) Char b
foo b = (,) ‘X’ b
foo :: b -> (Char, b)
튜플에 대한 Functor
instance Functor ((,) a) where
fmap f (a, b) = (a, f b)
함수에 대한 Functor
instance Functor ((->) a) where
fmap f g = \x -> f (g x)
IO 에 대한 Functor
> fmap (++"!") getLine
hi
"hi!"
IO 에 대한 Functor
readFile "/etc/passwd"
(length . lines) `fmap` readFile "/etc/passwd"
Functor 법칙• Identity 함수는 결과에 영향이 없어야 함
• fmap id === id
• 두 함수의 합성을 매핑하는 것과 각 함수의 매핑을 합성하는 것이 같아야 함• fmap (g . h) = (fmap g) . (fmap h)
리프팅 (lifting) 관점에서 본 Functor
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f where
fmap :: (a -> b) -> (f a -> f b)
콘텍스트로 이해하는 Functor
• [] functor
• 콘텍스트는 리스트• (->) functor
• 첫 번째 인자가 a 인 함수가 콘텍스트 (read-only 환경 )
• IO functor
• 사이드 이펙트가 가능한 computation 이 콘텍스트
Applicative Functor
> (*) <$> Just 2 <*> Just 8
Just 16
MonoidMonoid 는 다음 성질을 만족시키는 함수 •이다
1. Set(S) 는 바이너리 함수 • 에 대해 닫혀 있음• ∀ a,b ∈ S: a•b ∈ S
2. 바이너리 함수는 associative• ∀ a,b,c ∈ S: (a•b)•c = a•(b•c)
3. e 는 identity element
• ∃ e∈S: ∀ a∈S: e•a = a•e = a
Monoid 예제• +, 0 은 Monoid
• *, 1 은 Monoid
• (++) 과 [] 은 Monoid
Monoid> import Data.Monoid
> [1,2,3] `mappend` [4,5,6]
[1,2,3,4,5,6]
> [1,2,3] `mappend` mempty
[1,2,3]
> mempty `mappend` [1,2,3]
[1,2,3]
Maybe 다시 보기• Maybe a 타입의 값아 a -> Maybe b 인 함수가 있으면 Maybe b 를 어떻게 얻을 수 있을까 ?
• bind 함수• (>>=) :: (Monad m) => ma -> (a -> m
b) -> m b
bind 의 필요성f :: Int -> Maybe Int
f n = Just (n+1)
> f 1
Just 2
> f (Just 1)
ERROR!
bind (>>=)> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
> Just 1 >>= f
Just 2
Monad 타입 클래스class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
>> 함수> print "foo" >>= \_ -> print "bar"
"foo"
"bar"
> print "foo" >> print "bar"
"foo"
"bar"
main = do
print "foo"
print "bar"
bind 함수와 do 표기법main = do
putStrLn "Enter name:"
name <- getLine
putStrLn ("Hi " ++ name)
main = putStrLn "Enter name:" >>
getLine >>= putStrLn.("Hi " ++)
모나드 법칙1. left identity
• return a >>= f ≡ f a
2. Right Identity
• m >>= return ≡ m
3. Associativity
• (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
do 표기법으로 보는 모나드 법칙
합수 합성과 모나드
함수 합성• f :: A -> B
• g :: B -> C
• g . f :: A -> C
함수 합성의 법칙1. (f . g) . h = f . (g . h)
2. f . id = f
3. id . f = f
합수 합성의 예> (take 2 . filter (>=3) . map length) ["a", "ab", "abc", "abcd", "abcde"]
[3,4]
> map (negate . abs) [5, -3, -6, 7, -3, 2, -19, 24]
[-5,-3,-6,-7,-3,-2,-19,-24]
카테고리법칙1. (f . g) . h = f . (g .
h)
2. f . id = f
3. id . f = f
class Category cat where
id :: cat a a
(.) :: cat b c -> cat a b -> cat a c
instance Category (->) where
id x = x
(f . g) x = f (g x)
함수 합성의 장애물
Maybe 함수의 합성• f :: A -> Maybe B
• g :: B -> Maybe C
• g .f
• ERROR!
Maybe 함수의 합성(<=<) :: (b -> Maybe c)
-> (a -> Maybe b)
-> (a -> Maybe c)
(f <=< g) x = case g x of
Just y -> f y
Nothing -> Nothing
return :: (a -> Maybe a)
return = Just
함수 합성과의 비교id :: (a -> a)
return :: (a -> Maybe a)
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(<=<) :: (b -> Maybe c) -> (a -> Maybe b) -> (a -> Maybe c)
(f . g) x = f (g x)
(f <=< g) x = f =<< (g x)
Reader 함수의 합성• f :: A -> (R -> B)
• g :: B -> (R -> C)
• g . f
• ERROR!
Reader 함수의 합성(<=<) :: (b -> (r -> c))
-> (a -> (r -> b))
-> (a -> (r -> c))
(f <=< g) x = \r -> f ((g x) r) r
return :: a -> (r -> a)
return a = \r -> a
함수 합성과의 비교id :: (a -> a)
return :: (a -> (r-> a))
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(<=<) :: (b -> (r -> c)) -> (a -> (r -> b)) -> (a -> (r -> c))
(f . g) x = f (g x)
(f <=< g) x = \r -> f ((g x) r) r
Kleisli 카테고리newtype Kleisli m a b =
Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
(Kleisli f) . (Kleisli g) = Kleisli (f <=< g)
Kleisli 카테고리와 Monad
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
• (a >>= f) = (f <=< id) a
• (f <=< g) x = f =<< (g x)
Kleisli 카테고리로 보는 모나드 법칙 카테고리 법칙1. left identity
• return >=> g ≡ g
2. Right Identity
• f >=> return ≡ f
3. Associativity
• (f >=> g) >=> h ≡ f >=> (g >=> h)
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
모나드 법칙1. left identity
• m >>= return = m
2. Right Identity
• return x >>= f = f x
3. Associativity
• m >>= (\y -> g y >>= f) = (m >>= g) >>= f
모나드의 종류• Maybe
• Either
• IO
• Reader
• Writer
• State
• Continuation
• Parser
• Pipe
• …
Monad 의 함의사이드 이펙트가 있는 함수에 조합성을 주기 위한 구조
Functional Programming
Compositional Programming
숙제• CIS 194 Homework 4,5,7,8 번을 풉니다
• http://www.seas.upenn.edu/~cis194/lectures.html
참고 자료1. Slides of Programming in Haskell
• http://www.cs.nott.ac.uk/~pszgmh/book.html
2. Learn You a Haskell for Great Good!
• http://learnyouahaskell.com/chapters
3. A Gentle Introduction to Haskell 98
• https://www.haskell.org/tutorial/haskell-98-tutorial.pdf
4. Slides of Functional systems in Haskell (Stanford CS240h Spring 2014)
• http://www.scs.stanford.edu/14sp-cs240h/slides/
5. Slides of Introduction to Haskell (University of Virginia CS 1501 Lectures Spring 2013)
• http://shuklan.com/haskell/index.html