SlideShare una empresa de Scribd logo
1 de 51
Descargar para leer sin conexión
Clojure Monad
느슨한 타입 언어로 모나드 맛보기
김은민
당신이 만약 모나드를 이해하고 나면,
그걸 다른 사람에게 설명할 수 있는 능력을 잃어버리게 된다.
이것이 모나드의 저주다.
Douglas Crokford
그래서
저는 모나드를 설명하려고 하기 때문에
잘 이해하고 있지 않습니다.
양해 바랍니다. ㅋㅋㅋ
Eunmin Kim
여러가지 모나드 설명, 또 다른 모나드 설명
시작하기 전에
• 이해하기 쉬운 모나드
• 구현의 관점 / 타입을 지우자
• 하스켈 용어를 사용
• 클로저 맛보기
(def x 1)
(defn inc [x]
(+ x 1))
(fn [x]
(inc x))
var x = 1;
function inc(x) {
return x + 1;
}
function (x) {
return inc(x);
}
내 맘대로 모나드란?
컨텍스트가 있는
연속된 계산을
읽기 쉽게 작성하기 위한
순수한 함수형 패턴
컨텍스트가 있는
연속된 계산을
읽기 쉽게 작성하기 위한
순수한 함수형 패턴
var value1 = getValue("key1");
var value2 = getValue(value1);
var value3 = getValue(value2);
…
계산 중에 값이 null이면 전체 계산 결과가 null이면...
컨텍스트가 있는
연속된 계산을
읽기 쉽게 작성하기 위한
순수한 함수형 패턴
var value1 = getValue("key1");
if (value1 !== null) {
var value2 = getValue(value1);
if (value2 !== null) {
...
}
}
else {
return null;
}
해법
• 객체 지향과 같은 언어에서도 다양한 방식으로 문제를 풀고 있다.
• 계산 속에 컨텍스트가 섞여 있다면 계산의 본질을 파악하기(읽기) 

어렵다.
• 컨텍스트와 계산을 분리하면 컨텍스트를 재사용 할 수 있다.
• 순수 함수 스타일로 연속된 계산은 알아보기 힘들다.
... 읽기 쉽게 작성하기 위한 순수한 함수형 패턴
• 복잡한 코드를 이해하기 쉽게 하기 위해 추상화

(분리된 개념)
• 작은 함수를 조합해 큰 함수로 추상화
• 작은 데이터를 조합해 큰 데이터로 추상화
추상화의 예
(defn str
"With no args, returns the empty string. With one arg x, returns
x.toString(). (str nil) returns the empty string. With more than
one arg, returns the concatenation of the str values of the args."
{:tag String
:added "1.0"
:static true}
(^String [] "")
(^String [^Object x]
(if (nil? x) "" (. x (toString))))
(^String [x & ys]
((fn [^StringBuilder sb more]
(if more
(recur (. sb (append (str (first more)))) (next more))
(str sb)))
(new StringBuilder (str x)) ys)))
추상화의 예
(str "Hello" "World" "!")
str 이란 기호는 문자열을 합쳐주는 개념
... 읽기 쉽게 작성하기 위한 순수한 함수형 패턴
• 부수효과가 없다.
• 상태가 없다.
• 전역 값도 없다.
(fn [x]
(println "x:" x)
(+ x 1))
var state = 1;
state = 2; // 상태를 변경!
;; 부수효과
(def y 1)
(fn [x]
(+ x y)) ;; y는 전역 값
... 읽기 쉽게 작성하기 위한 순수한 함수형 패턴
• 대부분의 언어는 순수하지 않은 부분을 허용한다.
• 순수한 것이 좋은 지는 여기서 논하지 않는다.
• 모나드는 순수의 감옥에 살고 있다.
컨텍스트가 있는 연속된 계산을 ...
• 절차적 형태
• 순수한 함수 형태
var x = 1;
var y = 2;
x + y;
(((fn [x]
(fn [y]
(+ x y))) 1) 2)
컨텍스트가 있는 연속된 계산을 ...
• 들여쓰기를 해서 보기 좋게 만들기
(((fn [x]
(fn [y]
(+ x y)))
1)
2)
컨텍스트가 있는 연속된 계산을 ...
• 들여쓰기를 해서 보기 좋게 만들기
• 절차를 추상화
(((fn [x]
(fn [y]
(+ x y)))
1)
2)
(((fn [x]
(fn [y]
결과))
계산)
계산)
컨텍스트가 있는 연속된 계산을 ...
• Helper 함수를 만들어서 더 보기 좋게
(defn bind [mv f]
(f mv))
(bind 1 (fn [x]
(bind 2 (fn [y]
(+ x y)))))
컨텍스트가 있는 연속된 계산을 ...
• 들여쓰기를 해서 보기 좋게 만들기
(defn bind [mv f]
(f mv))
(bind 1 (fn [x]
(bind 2 (fn [y]
(+ x y)))))
컨텍스트가 있는 연속된 계산을 ...
• 들여쓰기를 해서 보기 좋게 만들기
• 연속된 계산 패턴을 추상화
(bind 1 (fn [x]
(bind 2 (fn [y]
(+ x y)))))
var x = 1;
var y = 2;
x + y;
(bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄
(bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄
… ;; 줄
… ;; 줄
결과)))) ;; 줄
컨텍스트가 있는 연속된 계산을 ...
• 모나드
• bind 함수에 따라 모나드 동작(특성)이 달라진다.
• identity 모나드
(defn bind [mv f]
(f mv))
(bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄
(bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄
… ;; 줄
… ;; 줄
결과)))) ;; 줄
컨텍스트가 있는 연속된 계산을 ...
• 컨텍스트는 연속된 계산 과정을 감싸고 있는 생각(로직, 데이터)
• 예
• Maybe 모나드
• 연속된 계산 중 결과가 nil 이면 전체 결과가 nil
• Writer 모나드
• 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우
• Reader 모나드
• 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우
• State 모나드
• 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우
Maybe 연속된 계산 중 결과가 nil 이면 전체 결과가 nil
• nil을 리턴 할 수도 있는 value 함수
(defn value [x]
(if (pos? x)
x
nil))
(bind (value 0) (fn [x]
(bind (inc x) (fn [y]
(+ x y)))))
1. Unhandled java.lang.NullPointerException
(No message)
(defn value [x]
(when (pos? x)
(f mv)))
Maybe 연속된 계산 중 결과가 nil 이면 전체 결과가 nil
• nil을 리턴 할 수도 있는 value 함수
(defn value [x]
(if (pos? x)
x
nil))
(bind (value 0) (fn [x]
(bind (inc x) (fn [y] ;; (inc nil) 은 예외를 발생
(+ x y)))))
1. Unhandled java.lang.NullPointerException
(No message)
Maybe 연속된 계산 중 결과가 nil 이면 전체 결과가 nil
• bind 함수를 고쳐보자.
• 하스켈 Maybe 모나드 예
(defn bind [mv f]
(if mv
(f mv)
nil))
(bind (value 0) (fn [x]
(bind (inc x) (fn [y]
(+ x y))))) ;; => nil
Just 3 >>= (x ->
Nothing >>= (y ->
Just (x + y))) -- Nothing
Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우
• 계산 과정 중 로그를 기록 하는 예
• tell 함수로 계산 중에 값을 기록 한다.
• 새로운 함수 tell, logs, value 구현
(def result
(bind 1 (fn [x]
(bind (tell (str "x:" x)) (fn [_]
(bind 2 (fn [y]
(+ x y))))))))
(logs result) ;; ["x: 1"]
(value result) ;; 3
Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우
• logs와 value 함수 구현
• result 데이터 타입
• logs와 value 함수
(logs result) ;; ["x: 1"]
(value result) ;; 3
[3 ["x: 1"]]
(defn logs [mv]
(second mv))
(defn value [mv]
(first mv))
Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우
• bind 함수 구현
• bind 함수
(def result
(bind 1 (fn [x]
(bind (tell (str "x:" x)) (fn [_]
(bind 2 (fn [y]
(+ x y))))))))
(defn bind [mv f]
(let [[v w] mv
[vv ww] (f v)]
[vv (concat w ww)]))
계산 값을 담고 있는 mv 에서 값을 꺼내 f를 적용한다.

그런 의미에서 스칼라는 bind라는 이름 대신 flatMap이라는 이름을 사용한다.
Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우
• bind 함수 구현
• bind 함수
(def result
(bind 1 (fn [x] ;; bind 함수는 1을 받지 못함!
(bind (tell (str "x:" x)) (fn [_]
(bind 2 (fn [y] ;; bind 함수는 2을 받지 못함!
(+ x y)))))))) ;; 최종 결과는 3이면 안됨!
(defn bind [mv f]
(let [[v w] mv
[vv ww] (f v)]
[vv (concat w ww)]))
Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우
• bind에 넘겨 줄 Helper 함수 만들기
• 계산 고쳐 쓰기
(defn return [v]
[v []])
(def result
(bind (return 1) (fn [x]
(bind (tell (str "x:" x)) (fn [_]
(bind (return 2) (fn [y]
(return (+ x y)))))))))
Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우
• tell 함수 만들기
• 완성
(def result
(bind (return 1) (fn [x]
(bind (tell (str "x:" x)) (fn [_]
(bind (return 2) (fn [y]
(return (+ x y)))))))))
(defn tell [v]
[nil [v]])
(logs result) ;; ["x: 1"]
(value result) ;; 3
Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우
• 일반 값을 bind 함수에 넘기기 위한 return 함수
• 모노이드 : 합칠 수 있는 값의 일반화
• concat -> append
• [] -> emtpy
(defn bind [mv f]
(let [[v w] mv
[vv ww] (f v)]
[vv (concat w ww)]))
(defn return [v]
[v []])
Reader 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우
• 전역 설정의 예
(def config {:debug true})
(defn calc1 [x]
(+ x 1))
(defn calc2 [y]
(if (:debug config)
(+ y 2)
0))
(let [x (calc 1)
y (calc x)]
(+ x y))
Reader 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우
• 전역 값을 사용하지 않는 예
(defn calc1 [x]
(+ x 1))
(defn calc2 [y config]
(if (:debug config)
(+ y 2)
0))
(let [config {:debug true}
x (calc1 1)
y (calc2 x config)]
(+ x y))
Reader 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우
• 모나드 형태로 써 보면
• ask 함수로 계산 과정 중에 컨텍스트 값을 쓸 수 있다.
(def result
(bind (return (calc1 1)) (fn [x]
(bind (ask) (fn [config]
(bind (return (calc2 2 config)) (fn [y]
(return (+ x y)))))))))
(result {:debug true}) ;; 6
Reader 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우
• bind와 return 함수
• ask 함수
(defn bind [mv f]
(fn [r]
((f (mv r)) r)))
(defn return [v]
(fn [r]
v))
(defn ask []
(fn [r]
r))
State 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우
• 상태
var state = undefined;
var x = 1;
state = 1; // put state
y = state; // get state
var result = x + y;
State 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우
• 모나드 형태로 써보기
• put-state 함수로 상태 값을 바꾼다.
• get-state 함수로 상태 값을 가져온다.
(def result
(bind (return 1) (fn [x]
(bind (put-state x) (fn [_]
(bind (get-state) (fn [y]
(return (+ x y)))))))))
(value (result nil)) ;; 2
(state (result nil)) ;; 1
var state = undefined;
var x = 1;
state = 1; // put state
y = state; // get state
var result = x + y;
result; // 2
state; // 1
State 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우
• bind와 return 함수
• get-state, put-state 함수
(defn bind [mv f]
(fn [s]
(let [[v ss] (mv s)]
((f v) ss))))
(defn return [v]
(fn [s]
[v s])) ;; v 값, s 상태 값
(defn put-state [v]
(fn [_]
[nil v]))
(defn get-state []
(fn [s]
[s s]))
State 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우
• 결과에서 value, state를 가져오는 함수와 사용법
(defn value [s]
(first s))
(defn state [s]
(second s))
(value (result nil)) ;; nil은 초기 상태 값
(state (result nil))
Haskell에서 State 모나드
(def result
(bind (return 1) (fn [x]
(bind (put-state x) (fn [_]
(bind (get-state) (fn [y]
(return (+ x y)))))))))
stateMonadWithBind :: State Int Int
stateMonadWithBind =
return 1 >>= x ->
put x >>= _ ->
get >>= y ->
return (x + y)
stateMonad :: State Int Int
stateMonad = do
x <- return 1
put x
y <- get
return (x + y)
runState stateMonad 0 — (2,1)
bind의 다형성
• bind 함수는 다형성을 가져야 모나드 패턴을 다시 쓸 수 있다.
• Writer / State bind
(bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄
(bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄
… ;; 줄
… ;; 줄
결과)))) ;; 줄
(defn bind [mv f]
(fn [s]
(let [[v ss] (mv s)]
((f v) ss))))
(defn bind [mv f]
(let [[v w] mv
[vv ww] (f v)]
[vv (concat w ww)]))
bind의 다형성
• clojure.algo.monad 에서 사용하는 재사용 방법
• domonad는 bind 함수를 감춰주는 syntactic sugar
• domonad 인자로 넘기는 monad 종류에 따라서 모나드의

특성이 달라진다. 하지만 타입 있는 언어들이 가지는 값에 따른
bind의 다형성 효과를 누리지 못한다.
(def state-monad
(domonad state-m
[x (m-result 1)
_ (set-state x)
y (fetch-state)]
(+ x y)))
(first (state-monad 0))
타입 따른 다형성과 모나드
• 클로저는 protocol과 multimethod로 다형성을 지원한다.
;; protocol / defrecord
(defprotocol Monad
(bind [mv f]))
(defrecord Writer [v mv]
Monad
(bind [mv f]
(let [ww (f v)]
(->Writer (:v ww) (concat mv (:mv ww))))))
;; multimethod
(defmulti bind (fn [mv f] (class mv)))
(defmethod bind Writer [{:keys [v mv]} f]
(let [ww (f v)]
(->Writer (:v ww) (concat mv (:mv ww)))))
타입 따른 다형성과 모나드
• 클로저에서 deftype이나 defrecord로 만든 타입은 함수 값을
표현하기 어렵다. (Reader / State 모나드의 값은 함수)
• 아래 두 함수를 다른 타입으로 인식하기 어렵다.
;; Long -> Long
(def a (fn [x] (inc x)))
;; String -> String
(def b (fn [x] (str x "!")))
(class a) ;; clojure.lang.IFn
(class b) ;; clojure.lang.IFn
타입과 클로저
• 타입을 표현하기 위한 (제약은 선택 사항) 라이브러리
• core.typed
• spec
(ann bind [[Number -> State] -> [Number -> [Number -> State]]])
(fn [x :- Number]
…)
(s/fdef bind
:args (s/cat :mv (s/fspec :args (s/cat :v number?)
:ret state?)
:f (s/fspec :args (s/cat :v number?)
:ret state?)))
정리
• 컨텍스트가 있는 연속된 계산을 읽기 쉽게 만들기 위한 

순수한 함수형 패턴
• bind와 return 함수의 구성에 따라 모나드의 특성이 달라진다.
• 모나드 값은 함수일 수도 있다. (Reader / State의 예)
• 클로저는 algo.monad 라이브러에 모나드 패턴을 사용하기 위
한 domonad 라는 Syntactic Sugar와 자주 사용하는 모나드
를 만들어 놨다.
• 타입 없는 언어는 타입에 따른 다형성의 제한으로 모나드 계산
과정에서 다른 모나드를 함께 사용하기 힘들다.
다루지 않은 부분
• 모나드의 다른 인터페이스
• 펑터
• 어플리커티브 펑터
• 모노이드
• 모나드 트랜스포머
• 모나드 3법칙
• List, I/O, Continuation, Free … 수 많은 모나드들
• …
가치
• 순수 함수형 안에서 모나드는 가치가 있다.
• 컨텍스트와 계산 과정을 분리
• DSL interpreter
• 순수한 사람이라면 더 좋은 코드를 만들기 위해 모나드를 사용
하는 것이 좋다.
• 순수하지 않은 사람이라면 학습용으로 가치가 있다.
• 순수한 것은 좋은 것인가?
감사합니다.
Eunmin Kim

Más contenido relacionado

La actualidad más candente

コホート研究 isseing333
コホート研究 isseing333コホート研究 isseing333
コホート研究 isseing333Issei Kurahashi
 
Rの環境とスコープ
Rの環境とスコープRの環境とスコープ
Rの環境とスコープItoshi Nikaido
 
PRML 12-12.1.4 主成分分析 (PCA) / Principal Component Analysis (PCA)
PRML 12-12.1.4 主成分分析 (PCA) / Principal Component Analysis (PCA)PRML 12-12.1.4 主成分分析 (PCA) / Principal Component Analysis (PCA)
PRML 12-12.1.4 主成分分析 (PCA) / Principal Component Analysis (PCA)Akihiro Nitta
 
PRML勉強会@長岡 第4章線形識別モデル
PRML勉強会@長岡 第4章線形識別モデルPRML勉強会@長岡 第4章線形識別モデル
PRML勉強会@長岡 第4章線形識別モデルShohei Okada
 
Prml4.4 ラプラス近似~ベイズロジスティック回帰
Prml4.4 ラプラス近似~ベイズロジスティック回帰Prml4.4 ラプラス近似~ベイズロジスティック回帰
Prml4.4 ラプラス近似~ベイズロジスティック回帰Yuki Matsubara
 
2013.12.26 prml勉強会 線形回帰モデル3.2~3.4
2013.12.26 prml勉強会 線形回帰モデル3.2~3.42013.12.26 prml勉強会 線形回帰モデル3.2~3.4
2013.12.26 prml勉強会 線形回帰モデル3.2~3.4Takeshi Sakaki
 
自由エネルギー原理入門: 正規分布を仮定した場合
自由エネルギー原理入門: 正規分布を仮定した場合自由エネルギー原理入門: 正規分布を仮定した場合
自由エネルギー原理入門: 正規分布を仮定した場合Masatoshi Yoshida
 
PRML10.6 変分ロジスティック回帰
PRML10.6 変分ロジスティック回帰PRML10.6 変分ロジスティック回帰
PRML10.6 変分ロジスティック回帰Yo Ehara
 
PRML輪読#10
PRML輪読#10PRML輪読#10
PRML輪読#10matsuolab
 
PRML輪読#9
PRML輪読#9PRML輪読#9
PRML輪読#9matsuolab
 
Relations and functions remediation notes
Relations and functions remediation notesRelations and functions remediation notes
Relations and functions remediation notescarolinevest77
 
ガウス過程入門
ガウス過程入門ガウス過程入門
ガウス過程入門ShoShimoyama
 
Rでisomap(多様体学習のはなし)
Rでisomap(多様体学習のはなし)Rでisomap(多様体学習のはなし)
Rでisomap(多様体学習のはなし)Kohta Ishikawa
 
PRML上巻勉強会 at 東京大学 資料 第1章後半
PRML上巻勉強会 at 東京大学 資料 第1章後半PRML上巻勉強会 at 東京大学 資料 第1章後半
PRML上巻勉強会 at 東京大学 資料 第1章後半Ohsawa Goodfellow
 
Rでのtry関数によるエラー処理
Rでのtry関数によるエラー処理Rでのtry関数によるエラー処理
Rでのtry関数によるエラー処理wada, kazumi
 
第5章混合分布モデルによる逐次更新型異常検知
第5章混合分布モデルによる逐次更新型異常検知第5章混合分布モデルによる逐次更新型異常検知
第5章混合分布モデルによる逐次更新型異常検知Tetsuma Tada
 
PRML 2.3節 - ガウス分布
PRML 2.3節 - ガウス分布PRML 2.3節 - ガウス分布
PRML 2.3節 - ガウス分布Yuki Soma
 

La actualidad más candente (20)

コホート研究 isseing333
コホート研究 isseing333コホート研究 isseing333
コホート研究 isseing333
 
Rの環境とスコープ
Rの環境とスコープRの環境とスコープ
Rの環境とスコープ
 
PRML 12-12.1.4 主成分分析 (PCA) / Principal Component Analysis (PCA)
PRML 12-12.1.4 主成分分析 (PCA) / Principal Component Analysis (PCA)PRML 12-12.1.4 主成分分析 (PCA) / Principal Component Analysis (PCA)
PRML 12-12.1.4 主成分分析 (PCA) / Principal Component Analysis (PCA)
 
PRML勉強会@長岡 第4章線形識別モデル
PRML勉強会@長岡 第4章線形識別モデルPRML勉強会@長岡 第4章線形識別モデル
PRML勉強会@長岡 第4章線形識別モデル
 
Prml4.4 ラプラス近似~ベイズロジスティック回帰
Prml4.4 ラプラス近似~ベイズロジスティック回帰Prml4.4 ラプラス近似~ベイズロジスティック回帰
Prml4.4 ラプラス近似~ベイズロジスティック回帰
 
2013.12.26 prml勉強会 線形回帰モデル3.2~3.4
2013.12.26 prml勉強会 線形回帰モデル3.2~3.42013.12.26 prml勉強会 線形回帰モデル3.2~3.4
2013.12.26 prml勉強会 線形回帰モデル3.2~3.4
 
PRML 10.4 - 10.6
PRML 10.4 - 10.6PRML 10.4 - 10.6
PRML 10.4 - 10.6
 
自由エネルギー原理入門: 正規分布を仮定した場合
自由エネルギー原理入門: 正規分布を仮定した場合自由エネルギー原理入門: 正規分布を仮定した場合
自由エネルギー原理入門: 正規分布を仮定した場合
 
PRML10.6 変分ロジスティック回帰
PRML10.6 変分ロジスティック回帰PRML10.6 変分ロジスティック回帰
PRML10.6 変分ロジスティック回帰
 
PRML輪読#10
PRML輪読#10PRML輪読#10
PRML輪読#10
 
PRML輪読#9
PRML輪読#9PRML輪読#9
PRML輪読#9
 
Relations and functions remediation notes
Relations and functions remediation notesRelations and functions remediation notes
Relations and functions remediation notes
 
ガウス過程入門
ガウス過程入門ガウス過程入門
ガウス過程入門
 
Rでisomap(多様体学習のはなし)
Rでisomap(多様体学習のはなし)Rでisomap(多様体学習のはなし)
Rでisomap(多様体学習のはなし)
 
mcmc
mcmcmcmc
mcmc
 
PRML上巻勉強会 at 東京大学 資料 第1章後半
PRML上巻勉強会 at 東京大学 資料 第1章後半PRML上巻勉強会 at 東京大学 資料 第1章後半
PRML上巻勉強会 at 東京大学 資料 第1章後半
 
Rでのtry関数によるエラー処理
Rでのtry関数によるエラー処理Rでのtry関数によるエラー処理
Rでのtry関数によるエラー処理
 
第5章混合分布モデルによる逐次更新型異常検知
第5章混合分布モデルによる逐次更新型異常検知第5章混合分布モデルによる逐次更新型異常検知
第5章混合分布モデルによる逐次更新型異常検知
 
Chainerで流体計算
Chainerで流体計算Chainerで流体計算
Chainerで流体計算
 
PRML 2.3節 - ガウス分布
PRML 2.3節 - ガウス分布PRML 2.3節 - ガウス分布
PRML 2.3節 - ガウス分布
 

Similar a Clojure Monad

7가지 동시성 모델 - 3장. 함수형 프로그래밍
7가지 동시성 모델 - 3장. 함수형 프로그래밍7가지 동시성 모델 - 3장. 함수형 프로그래밍
7가지 동시성 모델 - 3장. 함수형 프로그래밍Hyunsoo Jung
 
스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오Taeoh Kim
 
7가지 동시성 모델 4장
7가지 동시성 모델 4장7가지 동시성 모델 4장
7가지 동시성 모델 4장HyeonSeok Choi
 
R 프로그래밍 기본 문법
R 프로그래밍 기본 문법R 프로그래밍 기본 문법
R 프로그래밍 기본 문법Terry Cho
 
NDC11_슈퍼클래스
NDC11_슈퍼클래스NDC11_슈퍼클래스
NDC11_슈퍼클래스noerror
 
파이썬+주요+용어+정리 20160304
파이썬+주요+용어+정리 20160304파이썬+주요+용어+정리 20160304
파이썬+주요+용어+정리 20160304Yong Joon Moon
 
[0312 조진현] good bye dx9
[0312 조진현] good bye dx9[0312 조진현] good bye dx9
[0312 조진현] good bye dx9진현 조
 
파이썬 스터디 2주차
파이썬 스터디 2주차파이썬 스터디 2주차
파이썬 스터디 2주차Han Sung Kim
 
Scala, Scalability
Scala, ScalabilityScala, Scalability
Scala, ScalabilityDongwook Lee
 
R 기초 : R Basics
R 기초 : R BasicsR 기초 : R Basics
R 기초 : R BasicsYoonwhan Lee
 
[강릉원주대 대기환경과학과] 대기과학전산입문 설명서
[강릉원주대 대기환경과학과] 대기과학전산입문 설명서[강릉원주대 대기환경과학과] 대기과학전산입문 설명서
[강릉원주대 대기환경과학과] 대기과학전산입문 설명서Lee Sang-Ho
 
3ds maxscript 튜토리얼_20151206_서진택
3ds maxscript 튜토리얼_20151206_서진택3ds maxscript 튜토리얼_20151206_서진택
3ds maxscript 튜토리얼_20151206_서진택JinTaek Seo
 
파이썬+함수이해하기 20160229
파이썬+함수이해하기 20160229파이썬+함수이해하기 20160229
파이썬+함수이해하기 20160229Yong Joon Moon
 
R 스터디 첫번째
R 스터디 첫번째R 스터디 첫번째
R 스터디 첫번째Jaeseok Park
 
R 기본-데이타형 소개
R 기본-데이타형 소개R 기본-데이타형 소개
R 기본-데이타형 소개Terry Cho
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++KWANGIL KIM
 

Similar a Clojure Monad (20)

7가지 동시성 모델 - 3장. 함수형 프로그래밍
7가지 동시성 모델 - 3장. 함수형 프로그래밍7가지 동시성 모델 - 3장. 함수형 프로그래밍
7가지 동시성 모델 - 3장. 함수형 프로그래밍
 
스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오
 
7가지 동시성 모델 4장
7가지 동시성 모델 4장7가지 동시성 모델 4장
7가지 동시성 모델 4장
 
R 프로그래밍 기본 문법
R 프로그래밍 기본 문법R 프로그래밍 기본 문법
R 프로그래밍 기본 문법
 
NDC11_슈퍼클래스
NDC11_슈퍼클래스NDC11_슈퍼클래스
NDC11_슈퍼클래스
 
파이썬+주요+용어+정리 20160304
파이썬+주요+용어+정리 20160304파이썬+주요+용어+정리 20160304
파이썬+주요+용어+정리 20160304
 
[0312 조진현] good bye dx9
[0312 조진현] good bye dx9[0312 조진현] good bye dx9
[0312 조진현] good bye dx9
 
파이썬 스터디 2주차
파이썬 스터디 2주차파이썬 스터디 2주차
파이썬 스터디 2주차
 
R 시작해보기
R 시작해보기R 시작해보기
R 시작해보기
 
7장매크로
7장매크로7장매크로
7장매크로
 
Scalability
ScalabilityScalability
Scalability
 
Scala, Scalability
Scala, ScalabilityScala, Scalability
Scala, Scalability
 
R 기초 : R Basics
R 기초 : R BasicsR 기초 : R Basics
R 기초 : R Basics
 
Python
PythonPython
Python
 
[강릉원주대 대기환경과학과] 대기과학전산입문 설명서
[강릉원주대 대기환경과학과] 대기과학전산입문 설명서[강릉원주대 대기환경과학과] 대기과학전산입문 설명서
[강릉원주대 대기환경과학과] 대기과학전산입문 설명서
 
3ds maxscript 튜토리얼_20151206_서진택
3ds maxscript 튜토리얼_20151206_서진택3ds maxscript 튜토리얼_20151206_서진택
3ds maxscript 튜토리얼_20151206_서진택
 
파이썬+함수이해하기 20160229
파이썬+함수이해하기 20160229파이썬+함수이해하기 20160229
파이썬+함수이해하기 20160229
 
R 스터디 첫번째
R 스터디 첫번째R 스터디 첫번째
R 스터디 첫번째
 
R 기본-데이타형 소개
R 기본-데이타형 소개R 기본-데이타형 소개
R 기본-데이타형 소개
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++
 

Clojure Monad

  • 1. Clojure Monad 느슨한 타입 언어로 모나드 맛보기 김은민
  • 2. 당신이 만약 모나드를 이해하고 나면, 그걸 다른 사람에게 설명할 수 있는 능력을 잃어버리게 된다. 이것이 모나드의 저주다. Douglas Crokford
  • 3. 그래서 저는 모나드를 설명하려고 하기 때문에 잘 이해하고 있지 않습니다. 양해 바랍니다. ㅋㅋㅋ Eunmin Kim
  • 4. 여러가지 모나드 설명, 또 다른 모나드 설명
  • 5. 시작하기 전에 • 이해하기 쉬운 모나드 • 구현의 관점 / 타입을 지우자 • 하스켈 용어를 사용 • 클로저 맛보기 (def x 1) (defn inc [x] (+ x 1)) (fn [x] (inc x)) var x = 1; function inc(x) { return x + 1; } function (x) { return inc(x); }
  • 7. 컨텍스트가 있는 연속된 계산을 읽기 쉽게 작성하기 위한 순수한 함수형 패턴
  • 8. 컨텍스트가 있는 연속된 계산을 읽기 쉽게 작성하기 위한 순수한 함수형 패턴 var value1 = getValue("key1"); var value2 = getValue(value1); var value3 = getValue(value2); … 계산 중에 값이 null이면 전체 계산 결과가 null이면...
  • 9. 컨텍스트가 있는 연속된 계산을 읽기 쉽게 작성하기 위한 순수한 함수형 패턴 var value1 = getValue("key1"); if (value1 !== null) { var value2 = getValue(value1); if (value2 !== null) { ... } } else { return null; }
  • 10. 해법 • 객체 지향과 같은 언어에서도 다양한 방식으로 문제를 풀고 있다. • 계산 속에 컨텍스트가 섞여 있다면 계산의 본질을 파악하기(읽기) 
 어렵다. • 컨텍스트와 계산을 분리하면 컨텍스트를 재사용 할 수 있다. • 순수 함수 스타일로 연속된 계산은 알아보기 힘들다.
  • 11. ... 읽기 쉽게 작성하기 위한 순수한 함수형 패턴 • 복잡한 코드를 이해하기 쉽게 하기 위해 추상화
 (분리된 개념) • 작은 함수를 조합해 큰 함수로 추상화 • 작은 데이터를 조합해 큰 데이터로 추상화
  • 12. 추상화의 예 (defn str "With no args, returns the empty string. With one arg x, returns x.toString(). (str nil) returns the empty string. With more than one arg, returns the concatenation of the str values of the args." {:tag String :added "1.0" :static true} (^String [] "") (^String [^Object x] (if (nil? x) "" (. x (toString)))) (^String [x & ys] ((fn [^StringBuilder sb more] (if more (recur (. sb (append (str (first more)))) (next more)) (str sb))) (new StringBuilder (str x)) ys)))
  • 13. 추상화의 예 (str "Hello" "World" "!") str 이란 기호는 문자열을 합쳐주는 개념
  • 14. ... 읽기 쉽게 작성하기 위한 순수한 함수형 패턴 • 부수효과가 없다. • 상태가 없다. • 전역 값도 없다. (fn [x] (println "x:" x) (+ x 1)) var state = 1; state = 2; // 상태를 변경! ;; 부수효과 (def y 1) (fn [x] (+ x y)) ;; y는 전역 값
  • 15. ... 읽기 쉽게 작성하기 위한 순수한 함수형 패턴 • 대부분의 언어는 순수하지 않은 부분을 허용한다. • 순수한 것이 좋은 지는 여기서 논하지 않는다. • 모나드는 순수의 감옥에 살고 있다.
  • 16. 컨텍스트가 있는 연속된 계산을 ... • 절차적 형태 • 순수한 함수 형태 var x = 1; var y = 2; x + y; (((fn [x] (fn [y] (+ x y))) 1) 2)
  • 17. 컨텍스트가 있는 연속된 계산을 ... • 들여쓰기를 해서 보기 좋게 만들기 (((fn [x] (fn [y] (+ x y))) 1) 2)
  • 18. 컨텍스트가 있는 연속된 계산을 ... • 들여쓰기를 해서 보기 좋게 만들기 • 절차를 추상화 (((fn [x] (fn [y] (+ x y))) 1) 2) (((fn [x] (fn [y] 결과)) 계산) 계산)
  • 19. 컨텍스트가 있는 연속된 계산을 ... • Helper 함수를 만들어서 더 보기 좋게 (defn bind [mv f] (f mv)) (bind 1 (fn [x] (bind 2 (fn [y] (+ x y)))))
  • 20. 컨텍스트가 있는 연속된 계산을 ... • 들여쓰기를 해서 보기 좋게 만들기 (defn bind [mv f] (f mv)) (bind 1 (fn [x] (bind 2 (fn [y] (+ x y)))))
  • 21. 컨텍스트가 있는 연속된 계산을 ... • 들여쓰기를 해서 보기 좋게 만들기 • 연속된 계산 패턴을 추상화 (bind 1 (fn [x] (bind 2 (fn [y] (+ x y))))) var x = 1; var y = 2; x + y; (bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄 (bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄 … ;; 줄 … ;; 줄 결과)))) ;; 줄
  • 22. 컨텍스트가 있는 연속된 계산을 ... • 모나드 • bind 함수에 따라 모나드 동작(특성)이 달라진다. • identity 모나드 (defn bind [mv f] (f mv)) (bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄 (bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄 … ;; 줄 … ;; 줄 결과)))) ;; 줄
  • 23. 컨텍스트가 있는 연속된 계산을 ... • 컨텍스트는 연속된 계산 과정을 감싸고 있는 생각(로직, 데이터) • 예 • Maybe 모나드 • 연속된 계산 중 결과가 nil 이면 전체 결과가 nil • Writer 모나드 • 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우 • Reader 모나드 • 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우 • State 모나드 • 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우
  • 24. Maybe 연속된 계산 중 결과가 nil 이면 전체 결과가 nil • nil을 리턴 할 수도 있는 value 함수 (defn value [x] (if (pos? x) x nil)) (bind (value 0) (fn [x] (bind (inc x) (fn [y] (+ x y))))) 1. Unhandled java.lang.NullPointerException (No message) (defn value [x] (when (pos? x) (f mv)))
  • 25. Maybe 연속된 계산 중 결과가 nil 이면 전체 결과가 nil • nil을 리턴 할 수도 있는 value 함수 (defn value [x] (if (pos? x) x nil)) (bind (value 0) (fn [x] (bind (inc x) (fn [y] ;; (inc nil) 은 예외를 발생 (+ x y))))) 1. Unhandled java.lang.NullPointerException (No message)
  • 26. Maybe 연속된 계산 중 결과가 nil 이면 전체 결과가 nil • bind 함수를 고쳐보자. • 하스켈 Maybe 모나드 예 (defn bind [mv f] (if mv (f mv) nil)) (bind (value 0) (fn [x] (bind (inc x) (fn [y] (+ x y))))) ;; => nil Just 3 >>= (x -> Nothing >>= (y -> Just (x + y))) -- Nothing
  • 27. Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우 • 계산 과정 중 로그를 기록 하는 예 • tell 함수로 계산 중에 값을 기록 한다. • 새로운 함수 tell, logs, value 구현 (def result (bind 1 (fn [x] (bind (tell (str "x:" x)) (fn [_] (bind 2 (fn [y] (+ x y)))))))) (logs result) ;; ["x: 1"] (value result) ;; 3
  • 28. Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우 • logs와 value 함수 구현 • result 데이터 타입 • logs와 value 함수 (logs result) ;; ["x: 1"] (value result) ;; 3 [3 ["x: 1"]] (defn logs [mv] (second mv)) (defn value [mv] (first mv))
  • 29. Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우 • bind 함수 구현 • bind 함수 (def result (bind 1 (fn [x] (bind (tell (str "x:" x)) (fn [_] (bind 2 (fn [y] (+ x y)))))))) (defn bind [mv f] (let [[v w] mv [vv ww] (f v)] [vv (concat w ww)])) 계산 값을 담고 있는 mv 에서 값을 꺼내 f를 적용한다.
 그런 의미에서 스칼라는 bind라는 이름 대신 flatMap이라는 이름을 사용한다.
  • 30. Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우 • bind 함수 구현 • bind 함수 (def result (bind 1 (fn [x] ;; bind 함수는 1을 받지 못함! (bind (tell (str "x:" x)) (fn [_] (bind 2 (fn [y] ;; bind 함수는 2을 받지 못함! (+ x y)))))))) ;; 최종 결과는 3이면 안됨! (defn bind [mv f] (let [[v w] mv [vv ww] (f v)] [vv (concat w ww)]))
  • 31. Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우 • bind에 넘겨 줄 Helper 함수 만들기 • 계산 고쳐 쓰기 (defn return [v] [v []]) (def result (bind (return 1) (fn [x] (bind (tell (str "x:" x)) (fn [_] (bind (return 2) (fn [y] (return (+ x y)))))))))
  • 32. Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우 • tell 함수 만들기 • 완성 (def result (bind (return 1) (fn [x] (bind (tell (str "x:" x)) (fn [_] (bind (return 2) (fn [y] (return (+ x y))))))))) (defn tell [v] [nil [v]]) (logs result) ;; ["x: 1"] (value result) ;; 3
  • 33. Writer 연속된 계산 중 다른 환경에 값을 기록하고 싶은 경우 • 일반 값을 bind 함수에 넘기기 위한 return 함수 • 모노이드 : 합칠 수 있는 값의 일반화 • concat -> append • [] -> emtpy (defn bind [mv f] (let [[v w] mv [vv ww] (f v)] [vv (concat w ww)])) (defn return [v] [v []])
  • 34. Reader 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우 • 전역 설정의 예 (def config {:debug true}) (defn calc1 [x] (+ x 1)) (defn calc2 [y] (if (:debug config) (+ y 2) 0)) (let [x (calc 1) y (calc x)] (+ x y))
  • 35. Reader 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우 • 전역 값을 사용하지 않는 예 (defn calc1 [x] (+ x 1)) (defn calc2 [y config] (if (:debug config) (+ y 2) 0)) (let [config {:debug true} x (calc1 1) y (calc2 x config)] (+ x y))
  • 36. Reader 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우 • 모나드 형태로 써 보면 • ask 함수로 계산 과정 중에 컨텍스트 값을 쓸 수 있다. (def result (bind (return (calc1 1)) (fn [x] (bind (ask) (fn [config] (bind (return (calc2 2 config)) (fn [y] (return (+ x y))))))))) (result {:debug true}) ;; 6
  • 37. Reader 연속된 계산 중 다른 환경에서 값을 읽어 오고 싶은 경우 • bind와 return 함수 • ask 함수 (defn bind [mv f] (fn [r] ((f (mv r)) r))) (defn return [v] (fn [r] v)) (defn ask [] (fn [r] r))
  • 38. State 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우 • 상태 var state = undefined; var x = 1; state = 1; // put state y = state; // get state var result = x + y;
  • 39. State 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우 • 모나드 형태로 써보기 • put-state 함수로 상태 값을 바꾼다. • get-state 함수로 상태 값을 가져온다. (def result (bind (return 1) (fn [x] (bind (put-state x) (fn [_] (bind (get-state) (fn [y] (return (+ x y))))))))) (value (result nil)) ;; 2 (state (result nil)) ;; 1 var state = undefined; var x = 1; state = 1; // put state y = state; // get state var result = x + y; result; // 2 state; // 1
  • 40. State 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우 • bind와 return 함수 • get-state, put-state 함수 (defn bind [mv f] (fn [s] (let [[v ss] (mv s)] ((f v) ss)))) (defn return [v] (fn [s] [v s])) ;; v 값, s 상태 값 (defn put-state [v] (fn [_] [nil v])) (defn get-state [] (fn [s] [s s]))
  • 41. State 연속된 계산 중 읽고 쓰고 하는 환경이 필요한 경우 • 결과에서 value, state를 가져오는 함수와 사용법 (defn value [s] (first s)) (defn state [s] (second s)) (value (result nil)) ;; nil은 초기 상태 값 (state (result nil))
  • 42. Haskell에서 State 모나드 (def result (bind (return 1) (fn [x] (bind (put-state x) (fn [_] (bind (get-state) (fn [y] (return (+ x y))))))))) stateMonadWithBind :: State Int Int stateMonadWithBind = return 1 >>= x -> put x >>= _ -> get >>= y -> return (x + y) stateMonad :: State Int Int stateMonad = do x <- return 1 put x y <- get return (x + y) runState stateMonad 0 — (2,1)
  • 43. bind의 다형성 • bind 함수는 다형성을 가져야 모나드 패턴을 다시 쓸 수 있다. • Writer / State bind (bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄 (bind 계산 (fn [계산 결과를 묶을 기호] ;; 줄 … ;; 줄 … ;; 줄 결과)))) ;; 줄 (defn bind [mv f] (fn [s] (let [[v ss] (mv s)] ((f v) ss)))) (defn bind [mv f] (let [[v w] mv [vv ww] (f v)] [vv (concat w ww)]))
  • 44. bind의 다형성 • clojure.algo.monad 에서 사용하는 재사용 방법 • domonad는 bind 함수를 감춰주는 syntactic sugar • domonad 인자로 넘기는 monad 종류에 따라서 모나드의
 특성이 달라진다. 하지만 타입 있는 언어들이 가지는 값에 따른 bind의 다형성 효과를 누리지 못한다. (def state-monad (domonad state-m [x (m-result 1) _ (set-state x) y (fetch-state)] (+ x y))) (first (state-monad 0))
  • 45. 타입 따른 다형성과 모나드 • 클로저는 protocol과 multimethod로 다형성을 지원한다. ;; protocol / defrecord (defprotocol Monad (bind [mv f])) (defrecord Writer [v mv] Monad (bind [mv f] (let [ww (f v)] (->Writer (:v ww) (concat mv (:mv ww)))))) ;; multimethod (defmulti bind (fn [mv f] (class mv))) (defmethod bind Writer [{:keys [v mv]} f] (let [ww (f v)] (->Writer (:v ww) (concat mv (:mv ww)))))
  • 46. 타입 따른 다형성과 모나드 • 클로저에서 deftype이나 defrecord로 만든 타입은 함수 값을 표현하기 어렵다. (Reader / State 모나드의 값은 함수) • 아래 두 함수를 다른 타입으로 인식하기 어렵다. ;; Long -> Long (def a (fn [x] (inc x))) ;; String -> String (def b (fn [x] (str x "!"))) (class a) ;; clojure.lang.IFn (class b) ;; clojure.lang.IFn
  • 47. 타입과 클로저 • 타입을 표현하기 위한 (제약은 선택 사항) 라이브러리 • core.typed • spec (ann bind [[Number -> State] -> [Number -> [Number -> State]]]) (fn [x :- Number] …) (s/fdef bind :args (s/cat :mv (s/fspec :args (s/cat :v number?) :ret state?) :f (s/fspec :args (s/cat :v number?) :ret state?)))
  • 48. 정리 • 컨텍스트가 있는 연속된 계산을 읽기 쉽게 만들기 위한 
 순수한 함수형 패턴 • bind와 return 함수의 구성에 따라 모나드의 특성이 달라진다. • 모나드 값은 함수일 수도 있다. (Reader / State의 예) • 클로저는 algo.monad 라이브러에 모나드 패턴을 사용하기 위 한 domonad 라는 Syntactic Sugar와 자주 사용하는 모나드 를 만들어 놨다. • 타입 없는 언어는 타입에 따른 다형성의 제한으로 모나드 계산 과정에서 다른 모나드를 함께 사용하기 힘들다.
  • 49. 다루지 않은 부분 • 모나드의 다른 인터페이스 • 펑터 • 어플리커티브 펑터 • 모노이드 • 모나드 트랜스포머 • 모나드 3법칙 • List, I/O, Continuation, Free … 수 많은 모나드들 • …
  • 50. 가치 • 순수 함수형 안에서 모나드는 가치가 있다. • 컨텍스트와 계산 과정을 분리 • DSL interpreter • 순수한 사람이라면 더 좋은 코드를 만들기 위해 모나드를 사용 하는 것이 좋다. • 순수하지 않은 사람이라면 학습용으로 가치가 있다. • 순수한 것은 좋은 것인가?