어떤 구조가 교환 법칙을 만족한다든지, 결합 법칙을 만족한다든지 하면 어떤 효과가 있을까요?
프로그래밍에선 결합 법칙이 있고, 항등원이 있는 모노이드가 매우 중요한 대접을 받고 있습니다. 왜 그럴까요?
실행 시점에 자유롭지만 순서는 지켜야 하는 구조가 주는 이점을 고민해 봤습니다. (역시나, 관련 텍스트나 언급하는 자료들을 거의 볼 수 없어 상상 반입니다.)
f1 = \x -> x * 2
f2 = \x -> x + 1
f3 = \x -> x +10
odot f1 f2 = \x -> f2 (f1 x)
ndot f1 f2 = \x -> f2 (f1 $ x + 2)
main = do
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
ghci> main
13
13
21
19odot은 결합 법칙이 성립하지만, 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은 그렇지 않습니다.
\x -> f3 ( (\y -> f2 (f1 $ y + 2)) $ x + 2)
*2
+1
+101을 넣으면 결과는 21
\x -> (\y -> f3 (f2 $ y + 2)) (f1 $ x + 2)
*2
+1
+101을 넣으면 결과는 19
f1, f2, f3 실행 순서는 둘 다 같지만, ndot이 +2를 몇 번 하고 시작하는지가 다릅니다.