어떤 구조가 교환 법칙을 만족한다든지, 결합 법칙을 만족한다든지 하면 어떤 효과가 있을까요?
프로그래밍에선 결합 법칙이 있고, 항등원이 있는 모노이드가 매우 중요한 대접을 받고 있습니다. 왜 그럴까요?
실행 시점에 자유롭지만 순서는 지켜야 하는 구조가 주는 이점을 고민해 봤습니다. (역시나, 관련 텍스트나 언급하는 자료들을 거의 볼 수 없어 상상 반입니다.)
= \x -> x * 2
f1 = \x -> x + 1
f2 = \x -> x +10
f3
= \x -> f2 (f1 x)
odot f1 f2 = \x -> f2 (f1 $ x + 2)
ndot f1 f2
= do
main print $ (f1 `odot` f2) `odot` f3 $ 1
print $ f1 `odot` (f2 `odot` f3) $ 1
print $ (f1 `ndot` f2) `ndot` f3 $ 1
print $ f1 `ndot` (f2 `ndot` f3) $ 1
> main
ghci13
13
21
19
odot
은 결합 법칙이 성립하지만, ndot
은 그렇지 않습니다.
※ odot은 original dot (.)을 의미하고, ndot은 new dot이란 의미입니다.
bind의 경우 결합 법칙을 만족하려면, 새로 effect가 만들어 질 때는 어디에도 의존하지 않고 effect가 만들어져야 합니다. bind 타입을 봐도 알 수 있습니다. 두 번 째 인자로 effect를 새로 만드는 a -> m b
함수를 받는데, m
은 이 전 effect에 의존하지 않고 만들어집니다. 만일, 이전 effect에 의존한다면 m a -> m b
함수 모양이었을 겁니다.
값을 받기 전 함수들을 먼저 Compose한 걸 함수에 넘겨 주거나 받기 어렵습니다. 즉 고차 함수에 쓰기 어렵습니다.
아직 들어오지 않은(알지 못한 값에 의존하는) 경우와 그렇지 않은 경우의 차이를 보겠습니다. 말만 들어서는 금방 이해가지 않을 수 있습니다. 아래 예를 보겠습니다.
odot
은 결합 법칙이 성립하지만, ndot
은 그렇지 않습니다.
-> f3 ( (\y -> f2 (f1 $ y + 2)) $ x + 2)
\x *2
+1
+10
1
을 넣으면 결과는 21
-> (\y -> f3 (f2 $ y + 2)) (f1 $ x + 2)
\x *2
+1
+10
1
을 넣으면 결과는 19
f1
, f2
, f3
실행 순서는 둘 다 같지만, ndot
이 +2
를 몇 번 하고 시작하는지가 다릅니다.