집합, 함수, 합성, 동형사상의 기본 개념을 통해 범주론의 핵심 아이디어를 소개합니다.
준비, 집합, 시작… (이 말장난을 참으려고 얼마나 애썼는지 모릅니다). 우리의 탐구는 집합론으로부터 시작합니다. 집합론과 범주론은 많은 유사점을 공유합니다. 우리는 범주론을 집합론의 일반화 로 볼 수 있습니다. 즉, 그것은 집합론이 설명하려는 것(모든 것?)과 같은 것을 설명하려 하지만, 더 추상적인 방식으로, 더 다재다능하고 (바라건대) 더 단순한 방식으로 설명하려는 것입니다.
또한 집합은 범주의 한 예 입니다(아마도 원형적 예시 라고 할 수 있겠죠), 그리고 예시를 갖는 것은 유용합니다.
처음에 가정된 것으로부터 무엇을 정의하고 무엇을 연역할 수 있는지를 묻는 대신, 우리는 오히려 더 일반적인 어떤 관념과 원리를 발견할 수 있는지를 묻는다. 그리고 그것을 통해 우리의 출발점을 정의하거나 연역할 수 있는지를 본다. Bertrand Russell, from Introduction to Mathematical Philosophy
대부분의 과학 이론과 수학 이론은 자신이 묶여 있고 그 안에서 유효한 특정한 영역 을 가집니다. 그것들은 이 영역을 염두에 두고 만들어지며, 그 바깥에서 사용되도록 의도되지 않습니다. 예를 들어, Darwin의 진화론은 서로 다른 생물 종 이 자연선택을 통해 어떻게 진화했는지를 설명하기 위해 만들어졌고, quantum mechanics는 특정한 규모에서 입자들이 어떻게 행동하는지를 설명합니다.
수학 이론조차도, 그것들이 과학 이론처럼 본질적으로 특정 영역에 속박 되어 있지는 않더라도, 적어도 어떤 영역과 강하게 관련되어 있습니다. 예를 들어 differential equations는 사건이 시간에 따라 어떻게 변하는지를 모형화하기 위해 만들어졌습니다.
집합론과 범주론은 다릅니다. 그것들은 어떤 특정 현상이 어떻게 작동하는지에 대해 엄밀한 설명을 제공하기 위해 만들어진 것이 아닙니다. 대신, 온갖 종류의 현상을 설명하기 위한 더 일반적인 틀을 제공합니다. 그것들은 도구라기보다 도구를 정의하는 언어처럼 작동합니다. 이런 이론들을 추상 이론이라고 부릅니다.
이 둘의 경계는 때때로 흐릿합니다. 모든 이론은 추상화 를 사용합니다. 그렇지 않다면 거의 쓸모가 없을 것입니다. 추상화가 없다면 Darwin은 특정 동물 종이나 심지어 개별 동물들에 대해 이야기해야 했을 것입니다. 차이는 추상 이론에는 어떤 특정한 것을 가리키지 않는 핵심 개념 이 있다는 점이며, 대신 사람들이 그것을 일반화하도록 남겨 둔다는 것입니다. 모든 이론은 자기 영역 밖에도 적용될 수 있지만, 집합론과 범주론은 애초에 정해진 영역이 없습니다.
진화론 같은 구체적 이론은 구체적 개념들로 이루어져 있습니다. 예를 들어 개체군 이라는 개념, 또는 유전자 풀 이라고도 불리는 개념은 서로 교배할 수 있는 개체들의 집단을 가리킵니다. 집합론 같은 추상 이론은 집합 개념처럼 추상 개념들로 이루어져 있습니다. 집합이라는 개념 자체는 아무것도 가리키지 않습니다. 하지만 그렇다고 그것이 텅 빈 개념이라고 말할 수는 없습니다. 집합으로 표현될 수 있는 것은 셀 수 없이 많기 때문입니다. 예를 들어 유전자 풀은 (아주 적절하게도) 개별 동물들의 집합으로 표현될 수 있습니다. 동물 종 역시 집합으로 표현될 수 있습니다 — 이론적으로 서로 교배할 수 있는 모든 개체군의 집합으로 말입니다.
추상 이론이 왜 유용할 수 있는지는 이미 보셨습니다. 그것들은 아주 단순하기 때문에 많은 구체적 이론의 구성 블록으로 사용될 수 있습니다. 또한 공통적이기 때문에 서로 다른 구체적 이론들을 공통의 기반 위에 놓음으로써 통합하고 비교하는 데 사용할 수 있습니다(이것은 나중에 보겠지만 범주론의 아주 전형적인 특징입니다). 더 나아가 좋은 (추상) 이론은 우리의 사고를 발전시키기 위한 정신적 모델 로 작용할 수 있습니다.
“집합이란 우리의 지각 또는 사고의 명확하고 구별되는 대상들을 하나의 전체로 모아 놓은 것이다. 이 대상들을 집합의 원소라고 부른다.” – Georg Cantor
아마도 놀랍지 않게도, 집합론에서는 모든 것이 집합의 관점에서 정의됩니다. 집합은 여러 사물의 모임인데, 여기서 “사물”은 여러분이 원하는 무엇이든 될 수 있습니다(개체, 개체군, 유전자 등). 예를 들어 다음 공들을 생각해 봅시다.
이제 이 모든 공을 원소로 포함하는 집합을 하나 만들어 봅시다. 그것을 (회색이므로) 라고 부릅시다. 이런 집합은 오직 하나만 있을 수 있습니다. 집합에는 구조가 없기 때문입니다(순서도 없고, 어떤 공이 다른 공보다 앞서거나 뒤서지도 않으며, 집합의 구성원이라는 점에서 “특별한” 원소도 없습니다). 같은 원소를 포함하는 두 집합은 사실 같은 집합을 그린 두 장의 그림일 뿐입니다.
이 예시는 지나치게 단순해 보일 수 있지만, 사실 다른 어떤 예시만큼이나 타당합니다.
이 개념을 유용하게 만드는 핵심 통찰은, 그것이 여러 개의 사물을 마치 하나인 것처럼 다루며 추론할 수 있게 해 준다는 점입니다.
집합을 하나 더 만들어 봅시다. 따뜻한 색을 가진 모든 공들의 집합입니다. 그림에서 yellow로 칠해져 있으므로 그것을 라고 부르겠습니다.
에는 에도 있는 원소들만 들어 있다는 점에 주목하세요. 즉, 집합 의 모든 원소는 집합 의 원소이기도 합니다. 두 집합이 이런 관계를 가질 때, 우리는 가 의 부분집합 이라고 말할 수 있습니다(또는 ). 두 집합을 함께 그리면 부분집합은 상위집합 내부에 완전히 들어가 있습니다.
빨간 공 모두의 집합에는 공이 하나만 들어 있습니다. 위에서 우리는 집합이 여러 원소를 하나로 요약한다고 말했습니다. 그래도 원소가 하나만 들어 있는 집합도 완전히 타당합니다 — 간단히 말해, 세상에는 유일무이한 것들이 있기 때문입니다. 어떤 왕국이 가진 왕/여왕들의 집합은 싱글턴 집합입니다.
싱글턴 집합의 요점은 무엇일까요? 음, 그것은 집합론의 언어의 일부입니다. 예를 들어 주어진 항목들의 집합을 기대하는 함수가 있는데, 기준을 만족하는 항목이 하나뿐이라면 그 항목으로 싱글턴 집합을 만들면 됩니다.
물론 하나가 유효한 답이라면, 영도 마찬가지입니다. 검은 공들 이나 흰 공들 의 집합, 를 원한다면, 이 모든 질문의 답은 같습니다 — 공집합입니다.
집합은 오직 자신이 포함하는 항목들로만 정의되기 때문에, 공집합은 유일 합니다 — 예를 들어 원소가 0개인 공들의 집합과 원소가 0개인 숫자들의 집합 사이에는 아무 차이도 없습니다. 형식적으로 공집합은 기호 로 표시합니다(즉 ).
공집합에는 몇 가지 특별한 성질이 있습니다. 예를 들어 그것은 모든 다른 집합의 부분집합입니다. 수학적으로는, ( 여기서 는 “모든”을 뜻합니다)
“내가 함수라고 부르는 것은 여러 표상을 하나의 공통 표상 아래 배열하는 행위의 통일성이다.” — Immanuel Kant, from “The Critique of Pure Reason”
이제 무언가를 인정해야 할 때가 왔습니다. 이 장은 사실 집합에 관한 장이 아닙니다. 함수에 관한 장입니다. 우리가 이렇게 시작한 이유는 집합을 설명하지 않고는 함수를 설명할 방법이 없기 때문입니다(계속 읽다 보면 사실 방법이 하나 있긴 하다는 것을 알게 되겠지만요).
함수란 두 집합 사이의 관계로서, 함수의 출발 집합 이라고 불리는 한 집합의 각 원소를, 다른 집합의 정확히 하나의 원소와 짝지어 주는 것입니다. 이 다른 집합은 함수의 도착 집합 이라고 불립니다.
이 두 집합은 함수의 정의역 과 공역 이라고도 하고, 또는 입력 과 출력 이라고도 합니다. 프로그래밍에서는 argument type 과 return type 이라는 이름으로 불립니다. 논리학에서는 premise 와 conclusion 에 해당합니다(거기까지도 갈 것입니다). 상황에 따라, 어떤 함수가 한 집합에서 다른 집합으로 간다 고, 이 집합을 저 집합과 연결한다 고, 혹은 이 집합의 값을 다른 집합의 값으로 변환한다 고 말할 수도 있습니다. 이런 여러 표현은 함수 개념의 다면성을 보여 줍니다.
여기 함수 가 있습니다. 이 함수는 집합 의 각 공을 다른 집합에서 그 반대 색의 공으로 바꿉니다(수학에서는 함수 이름 옆에 출발 집합과 도착 집합의 이름을 함께 쓰는 경우가 많습니다. 이렇게 말이죠: )
이것은 아마 존재하는 가장 단순한 종류의 함수 중 하나일 것입니다 — 집합들 사이의 일대일 관계 를 부호화합니다. 즉, 출발 집합의 하나 의 원소가 도착 집합의 정확히 하나 의 원소와 연결되고(그리고 반대 방향으로도 마찬가지입니다).
하지만 함수는 다대일 유형의 관계도 표현할 수 있습니다. 즉, 출발 집합의 여러 원소가 도착 집합의 하나 의 원소와 연결될 수 있습니다(하지만 반대 방향은 아닙니다). 아래는 그런 함수의 한 예입니다.
이런 함수는 어떤 기준에 따라 주어진 객체들의 모음을 분류 하거나, 그들이 가질 수 있는 어떤 성질을 바탕으로 그것들을 분할하는 연산을 나타낼 수 있습니다.
함수는 또한 도착 집합의 일부 원소가 아무 역할도 하지 않는 관계를 표현할 수도 있습니다.
한 예로는 어떤 종류의 패턴이나 구조와, 더 복잡한 맥락 속에서 그 패턴이 나타나는 것 사이의 관계를 생각할 수 있습니다.
함수가 얼마나 다재다능한지 보았습니다. 하지만 함수에서는 가질 수 없는 것이 하나 있습니다. 출발 원소가 아무 데에도 대응되지 않거나, 하나보다 많은 도착 원소에 대응되는 경우는 허용되지 않습니다 — 그런 것은 다대다 관계가 되며, 우리가 말했듯 함수는 다대일 관계를 표현합니다. 그런 “설계 결정”에는 이유가 있고, 곧 거기에 도달할 것입니다.
집합과 함수는 온갖 종류의 사물들, 심지어 사람들 사이의 관계도 표현할 수 있습니다. 여러분이 던지는 질문들 가운데 답이 있는 모든 질문은 함수로 표현될 수 있습니다.
“우리는 New York에서 얼마나 떨어져 있나?”라는 질문은 출발 집합이 세상의 모든 장소들의 집합이고, 도착 집합은 모든 양의 수의 집합인 함수입니다.
“내 아버지는 누구인가?”라는 질문은 출발 집합이 세상의 모든 사람들의 집합인 함수입니다.
과제 1: 이 함수의 도착 집합은 무엇일까요?
“내 아이는 누구인가?”라는 질문은 직접적인 함수가 아니라는 점에 유의하세요. 한 사람에게 아이가 없을 수도 있고, 여러 명 있을 수도 있기 때문입니다. 이런 종류의 질문을 함수로 표현하는 방법은 나중에 배우게 됩니다.
과제 2: 처음에 우리가 그린 함수들은 모두 무언가를 표현 하나요? 함수가 타당하려면 반드시 무언가를 표현해야 한다고 생각하나요?
집합 이 무엇을 나타내든, 에서 각 원소를 자기 자신으로 보내는, 아무 일도 하지 않는 함수를 정의할 수 있습니다. 이를 의 항등 함수 또는 라고 부릅니다.
를 함수의 세계에서 집합 을 대표하는 함수라고 생각할 수 있습니다. 이것의 존재는 우리가 직관으로는 “알고 있는” 많은 정리들을 형식적으로 증명하게 해 줍니다.
어떤 집합 과 그 부분집합 에 대해서도, 부분집합의 각 원소를 자기 자신으로 보내는 함수(부분집합의 상(image) 이라고 불립니다)를 정의할 수 있습니다:
모든 집합은 자기 자신의 부분집합이므로, 이 경우 이 함수는 항등 함수와 같습니다.
겉보기와는 달리, 공집합에서 임의의 다른 집합으로 가는 유일한 함수가 존재합니다.
과제 3: 이게 정말 타당할까요? 왜 그럴까요? 정의를 확인해 보세요.
아직도 확신이 서지 않는다면, 이것을 보세요:
그러므로, 분명히 이 함수는 존재해야 합니다!
과제 4: 반대 방향은 어떨까요? 공집합이 출발 집합이 아니라 도착 집합인 함수도 있을까요?
임의의 집합에서 임의의 싱글턴 집합으로 가는 유일한 함수가 존재합니다.
과제 5: 이것이 어떤 집합이든 싱글턴 집합에 타당하게 연결하는 유일한 방법이 정말 맞을까요?
과제 6: 다시, 반대 방향은 어떨까요?
모든 수치 연산은 (여러 종류의) 수들의 집합 위에서 작용하는 함수로 표현할 수 있습니다.
모든 함수가 모든 수에서 작동하는 것은 아니므로, 우리는 수의 집합을 여러 집합으로 나눕니다. 이들 가운데 많은 집합은 서로 부분집합 관계에 있습니다. 예를 들면 정수의 집합 , 양의 정수의 집합(“자연수”라고도 부릅니다) , 등이 있습니다.
각 수치 연산은 이런 집합 둘 사이의 함수입니다. 예를 들어 수를 제곱하는 것은 실수의 집합에서 음이 아닌 실수의 집합으로 가는 함수입니다(두 집합 모두 무한하므로 전체를 그릴 수는 없지만, 일부는 그릴 수 있습니다).
이 기회에 함수의 더 중요한 성질 몇 가지를 다시 강조하겠습니다:
전체적으로 보면, 각 값마다 항상 정확히 하나의 결과(일명 그 결과™)를 줄 수만 있다면 무엇이든 허용됩니다. 수치 연산에서는 수학이 바로 그런 식으로 설계되어 있기 때문에 이것이 항상 참입니다.
수의 모든 일반화는 처음에는 어떤 단순한 문제를 해결하기 위해 필요하다는 형태로 나타났다. 음수는 뺄셈이 항상 가능하도록 하기 위해 필요했고, 그렇지 않으면 a − b 는 a 가 b 보다 작을 때 무의미했을 것이다. 분수는 나눗셈이 항상 가능하도록 하기 위해 필요했다. 그리고 복소수는 근을 구하고 방정식을 푸는 일이 항상 가능하도록 하기 위해 필요하다.
Bertrand Russell, from Introduction to Mathematical Philosophy
덧셈, 곱셈 등 대부분의 수학 연산은 결과를 내기 위해 두 개의 수를 필요로 한다는 점에 유의하세요. 그렇다고 해서 그것들이 함수가 아니라는 뜻은 아닙니다. 그저 조금 더 정교할 뿐입니다. 필요에 따라 우리는 이런 연산들을 수들의 튜플 의 집합에서 수의 집합으로 가는 함수로 나타낼 수도 있고, 어떤 수를 받아 함수를 돌려준다고 말할 수도 있습니다. 이것은 나중에 더 다루겠습니다.
집합은 프로그래밍에서 매우 광범위하게 사용되며, 특히 타입 (또는 class)이라는 형태로 나타납니다. 앞에서 논의한 수의 집합들도 대부분의 언어에서 타입으로 존재합니다.
집합과 타입은 정확히 같은 것은 아니지만, 모든 타입은 집합이거나(또는 집합으로 볼 수 있습니다). 예를 들어 Boolean 타입은 true 와 false 라는 두 원소를 가진 집합으로 볼 수 있습니다.
프로그래밍에서 사용되는 또 다른 매우 기본적인 집합은 키보드 문자들의 집합, 즉 Char 입니다. 문자는 실제로 단독으로 쓰이는 경우는 드물고, 주로 문자열 같은 순서열의 일부로 사용됩니다.
프로그래밍에서 사용되는 대부분의 타입은 합성 타입입니다 — 여기 나열된 원시 타입들을 조합한 것입니다. 이것 역시 나중에 다루겠습니다.
과제 7: 프로그래밍에서 부분집합에 해당하는 타입의 대응물은 무엇일까요?
프로그래밍의 일부 함수들(메서드, 서브루틴 등이라고도 부릅니다)은 수학적 함수와 어느 정도 비슷합니다 — 주어진 타입의 값 하나를 받아(다른 말로, 어떤 집합에 속한 원소 하나를 받아) 다른 타입(또는 집합)에 속하는 정확히 하나의 원소를 항상 반환하기도 합니다. 예를 들어 다음은 Char 타입의 인자를 받아 그 문자가 글자인지를 나타내는 Boolean 을 반환하는 함수입니다.
하지만 대부분의 프로그래밍 언어에서 함수는 수학적 함수와 꽤 다를 수도 있습니다 — 값을 반환하는 것과는 무관한 여러 연산을 수행할 수 있습니다. 이런 연산은 종종 side effect라고 불립니다.
왜 프로그래밍의 함수는 다를까요? 음, 효과를 가진 함수를 수학적으로 타당한 방식으로 부호화하는 것은 쉽지 않은 일이고, 오늘날 널리 사용되는 대부분의 프로그래밍 패러다임이 만들어질 당시 사람들에게는 함수가 수학적으로 타당한지보다 더 큰 문제가 있었습니다(예를 들어 우선 어떤 프로그램이든 실제로 실행되게 만드는 것 같은 문제 말이죠).
오늘날에는 많은 사람이 수학적 함수가 너무 제한적이고 사용하기 어렵다고 느낍니다. 그리고 그들이 맞을 수도 있습니다. 하지만 수학적 함수는 비수학적 함수에 비해 한 가지 큰 장점이 있습니다 — 타입 시그니처만 보면 그 함수가 무엇을 하는지 거의 전부를 알 수 있다는 점입니다(아마도 이것이 대부분의 functional 언어가 강한 타입 시스템을 갖는 이유일 것입니다).
우리는 모든 수학적 함수는 프로그래밍 함수이기도 하지만, 그 역은 대부분의 프로그래밍 언어에서는 성립하지 않는다고 말했습니다. 그러나 수학적 함수만을 허용해서 이 등식이 성립하는 언어들도 있습니다. 이를 순수 함수형 프로그래밍 언어라고 합니다. 그런 언어의 예로 Haskell이 있으며, 나중에 만나게 될 것입니다.
이런 언어는 화면에 무언가를 그리거나, I/O를 수행하는 등의 연산을 직접 실행하는 함수를 지원하지 않습니다(이 문맥에서 그런 연산은 “side effects”라고 불립니다.
순수 함수형 프로그래밍 언어에서 그런 연산은 언어의 runtime에 위임 됩니다. 예를 들어 console.log('Hello') 처럼 side effect를 직접 수행하는 함수를 쓰는 대신, 그 side effect를 나타내는 타입을 반환하는 함수를 씁니다(Haskell에서는 side effect를 IO 타입이 다룹니다). 그러면 runtime이 그 함수들을 우리 대신 실행합니다.
그다음 우리는 흔히 continuation passing style 이라고 불리는 방식을 사용해 그 함수들을 하나의 전체 프로그램으로 연결합니다.
이제 우리는 함수라는 주제의 핵심에 거의 도달했습니다. 바로 함수 합성입니다. 두 함수 와 가 있고, 첫 번째 함수의 도착 집합이 두 번째 함수의 출발 집합과 같다고 가정합시다. 예를 들면 와 같습니다.
집합 의 어떤 원소에 첫 번째 함수를 적용하면, 집합 의 한 원소를 얻게 됩니다. 그런 다음 그 원소에 두 번째 함수를 적용하면, 타입 의 한 원소를 얻게 됩니다.
이렇게 해서 우리는 위의 과정을 수행한 것과 같은 함수를 정의할 수 있습니다. 즉, 집합 의 어떤 원소에서 시작해 화살표를 따라가면, 와 의 화살표를 둘 다 연달아 따라갔을 때 도달하는 집합 의 원소와 같은 원소에 도달하는 함수입니다. 이것을 라고 부릅시다. 우리는 가 와 의 합성 이라고 말할 수 있으며, 라고도 씁니다(첫 번째 함수가 오른쪽에 오므로 와 비슷합니다).
합성은 범주적인 모든 것의 본질입니다. 핵심 통찰은 두 부분의 합이 각 부분 자체보다 더 복잡하지 않다는 것(따라서 다시 합쳐질[합성될] 수 있다는 것)입니다. 이 통찰은 나중에 살펴볼 결합법칙이라는 성질로 포착됩니다.
과제 8: 함수의 어떤 성질이 합성을 가능하게 하는지 생각해 보세요. 예를 들어 다대다나 일대다 같은 다른 종류의 관계에서도 이것이 작동할까요?
합성이 얼마나 강력한지 이해하려면 다음을 생각해 보세요. 한 집합이 다른 집합과 연결되어 있다는 것은 두 번째 집합에서 출발하는 각 함수를 첫 번째 집합에서 출발하는 대응 함수로 옮길 수 있다는 뜻입니다.
집합 에서 집합 로 가는 함수가 있다면, 집합 에서 임의의 다른 집합으로 가는 모든 함수에 대해, 집합 에서 그 같은 집합으로 가는 대응 함수가 존재합니다. 다시 말해, 에서 어떤 다른 집합으로 가는 새 함수를 하나 정의할 때마다, 에서 그 같은 집합으로 가는 함수 하나를 공짜로 얻게 됩니다.
예를 들어, 어떤 사람과 그 사람의 어머니 사이의 관계를 출발 집합이 세상의 모든 사람들의 집합이고 도착 집합이 자녀를 가진 모든 사람들의 집합인 함수로 다시 생각해 봅시다. 이 함수를 다른 비슷한 함수들과 합성하면, 한 사람의 모계 쪽 친척들을 모두 얻게 됩니다.
비록 함수 합성을 처음 보는 것일지라도, 그 직관은 이미 우리 안에 있습니다 — 우리 모두는 어머니와 관계된 사람은 자동으로 우리와도 친척 관계가 된다는 것을 압니다. 어머니의 아버지는 우리의 외할아버지이고, 어머니의 배우자는 우리의 아버지인 식입니다.
합성의 원리는 이미 존재하는 관계를 분석 하는 데 유용할 뿐 아니라, 그러한 관계를 드러내는 대상을 구축 하는 실천, 즉 공학에도 도움을 줄 수 있습니다.
현대 공학이 고대의 장인 기술과 구별되는 주요 방식 가운데 하나는 부품/모듈/컴포넌트 라는 개념입니다 — 어떤 주어진 기능을 수행하지만 직접 사용되도록 만들어진 것이 아니라, 다른 그런 제품들과 결합되어 최종 사용자용 제품을 이루도록 최적화된 산물입니다. 예를 들어 espresso machine 은 적절한 방식으로 합성된 , pump, heater, grinder group 같은 컴포넌트들의 조합일 뿐입니다.
과제 9: 이 함수들의 출발 집합과 도착 집합이 무엇일지 생각해 보세요.
그런데 원소들을 보여 주지 않고 함수들만 보여 주는 “줌아웃된” 도표를 외적 도표 라고 하고, 앞에서 보았던 것들은 내적 도표 라고 합니다.
합성 함수의 연속 적용()과 새 함수()가 동등하다는 것을 보여 준 함수 합성 도표를 봅시다.
우리는 이 동등성을 내적 도표를 그려서 보여 주었습니다. 함수의 출발 집합과 도착 집합의 원소들을 명시적으로 그려, 두 경로가 동등하도록 한 것입니다.
또는 단지 화살표 경로들이 모두 동등하다고 말하고(주어진 집합 원소에서 시작하는 모든 화살표는 결국 결과 집합의 같은 대응 원소로 이어진다는 뜻입니다), 그 동등성을 외적 도표로 그릴 수도 있습니다.
또는 식으로 표현하고 싶다면(여기서 는 합성 연산자입니다) 이렇게 쓸 수도 있습니다.
외적 도표는 더 일반적이기 때문에 합성 개념을 표현하는 데 더 적절합니다. 사실 그것은 너무나도 일반적이어서 함수 합성의 정의 로도 쓸 수 있습니다.
두 함수 와 의 합성은, 이 도표의 모든 경로가 동등해지도록 정의되는 세 번째 함수이다.
이 책을 계속 읽는다면, 모든 경로가 동등한 도표들에 대해 더 많이 듣게 될 것입니다(참고로 그것들을 가환 도표 라고 부릅니다).
함수를 둘보다 많이 합성하고 싶다면, 함수를 합성하는 순서가 최종 결과에 영향을 주는지 궁금해질 수 있습니다. 즉, 두 함수 를 먼저 합성한 뒤 그 결과를 세 번째 함수와 합치는 것과…
…두 번째 함수와 세 번째 함수를 먼저 합성한 뒤 첫 번째 함수를 더하는 것이 같은 결과를 내는지 말입니다.
답은 합성 순서는 중요하지 않다는 것입니다 — 같은 함수들을 합성하기만 하면 결과는 언제나 같습니다.
함수의 이 성질을 결합법칙 이라고 합니다.
과제 10: 위 도표들을 내적 도표로 그려 보세요. 서로 합성 가능한 세 함수를 정의하고(앞에서 정의했던 두 함수를 써도 되고, 세 번째 함수 하나만 더 만들면 됩니다), 위에 보인 두 방식으로 합성한 뒤 결과가 같은지 확인해 보세요.
이쯤 되면 제가 범주론에 대해 이야기해야 한다는 사실을 잊어버리고 그저 관련 없는 개념들만 늘어놓고 있는 것 아닌가 걱정하실지도 모르겠습니다. 제가 가끔 그럴 수도 있긴 하지만, 지금은 아닙니다 — 함수 합성 이 범주론을 전혀 언급하지 않고도 제시될 수 있다는 사실이 그것이 범주론의 가장 중요한 개념들 가운데 하나 라는 사실을 막지는 못합니다.
실제로 (비공식적인 정의이긴 하지만) 범주론은 함수 같은 것들 (우리는 그것을 morphism 이라고 부릅니다)을 연구하는 학문이라고 말할 수 있습니다. 반드시 함수일 필요는 없지만 함수처럼 출발점과 도착점을 가지고, 함수처럼 서로 합성되며(결합적으로), 외적 도표로 표현될 수 있는 것들 말입니다.
또 범주론을 정의를 정의하지 않고 설명하는 또 다른 방법도 있습니다. 그것은 equality라는 개념을 isomorphism 이라는 개념으로 바꾸었을 때 얻어지는 것입니다.
우리는 아직 isomorphism에 대해 이야기하지 않았지만, 이 장의 나머지에서는 바로 그것을 하게 될 것입니다.
동형사상이 무엇인지 설명하기 위해, 함수가 표현할 수 있는 관계 유형의 예들로 다시 돌아가 봅시다. 그리고 그중 첫 번째이자 가장 기본적인 것, 즉 일대일 유형의 관계로 돌아갑니다. 우리는 모든 함수가 출발 집합의 정확히 하나의 원소에서 도착 집합의 하나의 원소로 향한다는 것을 압니다. 하지만 일대일 함수에서는 반대 방향도 마찬가지 입니다 — 도착 집합의 정확히 하나의 원소가 출발 집합의 하나의 원소에서 오는 것입니다.
같은 크기의 집합들을 연결하는 일대일 함수가 있다면(여기서는 그렇습니다), 그 함수는 다음 성질을 가집니다. 도착 집합의 모든 원소에는 정확히 하나의 화살표가 향합니다. 이런 경우 그 함수는 가역적 입니다. 즉, 함수의 화살표 방향과 출발/도착 집합을 뒤집으면 또 하나의 타당한 함수를 얻게 됩니다.
가역적인 함수를 동형사상 이라고 합니다. 두 집합 사이에 가역적 함수가 존재하면, 우리는 그 집합들이 동형 이라고 말합니다. 예를 들어 Celsius 로 측정한 온도를 Fahrenheit 로 측정한 온도로 바꾸는 가역 함수가 있고, 그 반대도 가능하므로, Celsius와 Fahrenheit로 측정된 온도는 동형이라고 말할 수 있습니다.
isomorphism은 그리스어로 “같은 형태”를 뜻합니다(실제로는 동형인 두 집합에서 다른 것은 오히려 형태뿐이지만), 더 형식적으로는 다음과 같습니다:
두 집합 와 가 동형(또는 )이라는 것은, 함수 와 그 역함수 가 존재하여, 와 가 성립한다는 뜻이다.
(여기서 항등 함수가 얼마나 유용한지 보세요).
자세히 보면 항등 함수도 가역적입니다(그 역은 자기 자신입니다). 따라서 각 집합은 그런 방식으로 자기 자신과 동형입니다.
그러므로 동형사상 개념은 equality의 개념을 포함합니다 — 서로 같은 것은 모두 동형이기도 합니다.
동형사상에 대한 흥미로운 사실 하나는, 집합 의 원소를 집합 의 원소로 바꾸는 함수와 그 반대 방향의 함수가 있다면, 함수 합성 덕분에 에서 나가는 임의의 함수에는 에서 나가는 대응 함수가 있고, 반대도 마찬가지라는 것입니다.
예를 들어 “~의 배우자이다”라는 함수가 모든 기혼자의 집합에서 같은 집합으로 가는 함수라면, 그 함수는 가역적입니다. 그렇다고 해서 여러분이 여러분의 배우자와 같은 사람이라는 뜻은 아닙니다. 오히려 여러분에 대한 모든 진술, 또는 여러분이 다른 사람이나 사물과 맺는 모든 관계는 그 사람과 그 다른 사람/사물 사이의 관계로도 옮겨질 수 있고, 그 반대도 가능하다는 뜻입니다.
동형사상에 대한 또 다른 흥미로운 사실은, 공통 집합 하나를 공유하는 두 동형사상이 있다면, 나머지 두 집합 사이에 세 번째 동형사상을 얻을 수 있다는 점입니다. 이것은 그들(그 동형사상들)의 합성의 결과가 됩니다.
두 동형사상을 또 다른 동형사상으로 합성하는 것은, 동형사상을 이루는 두 쌍의 함수를 양방향에서 각각 합성함으로써 가능합니다.
비형식적으로는 이 두 morphism이 실제로 서로의 역이므로 동형사상을 이룬다는 것을 알 수 있습니다. 이것을 형식적으로 증명하고 싶다면, 대략 다음과 같이 할 것입니다.
두 함수가 동형이라면, 그 합성은 항등 함수와 같습니다. 따라서 함수 와 가 동형임을 증명하는 것은 그 합성이 항등과 같음을 보이는 것과 같습니다.
하지만 우리는 이미 와 가 동형이라는 것을 알고 있으므로 , 위의 식은 다음과 동치입니다(무슨 뜻인지 보려면 도표를 참고하세요):
그리고 와 합성된 것은 자기 자신과 같다는 것도 알고 있으므로, 이것은 다음과 동치입니다:
이는 참입니다. 왜냐하면 와 가 동형이고, 동형인 함수들을 합성하면 항등과 같기 때문입니다.
그런데 동형사상을 얻는 또 다른 방법도 있습니다 — 두 morphism을 한 방향으로 합성해 세 번째 함수를 얻고, 그다음 그것의 역을 취하는 것입니다. 하지만 이렇게 하려면 두 동형사상을 합성해서 얻은 함수 역시 동형사상임을 먼저 증명해야 합니다.
임의의 두 싱글턴 집합 사이에는 유일하게 가능한 함수를 정의할 수 있습니다.
이 함수는 가역적입니다. 즉, 모든 싱글턴 집합은 서로 동형이며, 더 나아가(이 점이 중요합니다) 유일한 한 가지 방식으로 동형입니다.
앞 단락의 논리를 따르면, 유일무이한 어떤 것에 대한 모든 진술은 또 다른 유일무이한 것에 대한 진술로 옮길 수 있습니다.
우리는 동형인 집합들이 반드시 같은 집합은 아니라고 말했습니다(반대는 참이지만). 그러나 동형이라는 것이 어떤 의미에서는 그것들이 같다 또는 동등하다 는 뜻이라는 생각에서 완전히 벗어나기는 어렵습니다. 예를 들어, 동형인 어머니/자녀 관계로 연결된 모든 사람은 같은 유전자를 일부 공유합니다.
또 computer science에서, 타입 의 객체를 타입 의 객체로 바꾸는 함수와 그 반대 방향의 함수가 있다면(예를 들어 어떤 데이터 구조와 그 id 사이의 함수처럼), 와 를 사실상 같은 것의 두 형식으로 간주할 수도 있습니다. 하나를 가지면 다른 하나를 쉽게 얻을 수 있기 때문입니다.
두 사물이 동등하다는 것은 무슨 뜻일까요? 꽤 철학적인 질문처럼 들리지만, 사실 이에 답하는 형식적인 방법이 있습니다. 즉, equality의 개념을 매우 우아하게 포착하는 수학적 개념이 있습니다 — 바로 동치 관계 입니다.
그렇다면 동치 관계란 무엇일까요? 우리는 이미 관계가 무엇인지 압니다 — 그것은 두 집합 사이의 연결입니다(함수도 그 한 예입니다). 그렇다면 언제 관계가 동치 관계가 될까요? 정의에 따르면, 그것은 세 가지 법칙을 따를 때입니다. 그리고 이 법칙들은 equality에 대한 세 가지 직관적 관념에 대응합니다. 하나씩 살펴봅시다.
동치를 정의하는 첫 번째 관념은 모든 것은 자기 자신과 동등하다 는 것입니다.
이 단순한 원리는 똑같이 단순한 반사성 법칙으로 옮겨집니다: 모든 집합 에 대해, .
기독교 신학의 삼위일체에 따르면, 예수의 아버지는 God이고, Jesus도 God이며, 성령도 God입니다. 그러나 아버지는 Jesus와 같은 인격이 아니고(예수도 성령과 같지 않습니다). 이것이 이상하게 느껴진다면, 그것은 동치 관계의 두 번째 법칙인 추이성을 깨기 때문입니다. 추이성은 어떤 두 사물이 모두 세 번째 사물과 같다면, 그 둘도 서로 같아야 한다는 생각입니다.
수학적으로는, 모든 집합 와 에 대해, 만약 와 이면 .
셋보다 많은 집합이 얽힌 비슷한 상황에서 무엇이 일어나는지를 따로 정의할 필요는 없다는 점에 유의하세요. 같은 법칙을 여러 번 적용하면 해결되기 때문입니다.
어떤 것이 다른 것과 같다면, 반대 방향도 참입니다. 즉, 그 다른 것도 첫 번째 것과 같습니다. 이 관념을 대칭성 이라고 합니다. 대칭성은 동치 관계의 가장 특징적인 성질일 것입니다. 거의 다른 어떤 관계에도 성립하지 않기 때문입니다.
수학적으로는: 이면 .
동형사상은 실제로 동치 관계입니다. 그리고 “우연히도”, 우리는 그것을 증명하는 데 필요한 모든 정보를 이미 갖고 있습니다(마치 James Bond가 임무 완수에 필요한 장비를 언제나 우연히 다 갖고 있는 것처럼 말이죠).
우리는 동치 관계의 가장 특징적인 성질이 대칭성 이라고 말했습니다. 그리고 동형사상은 그 자체의 가장 특징적인 성질, 즉 가역성 덕분에 이 성질을 만족합니다.
과제 11: 한 법칙은 끝났고, 두 개가 남았습니다. 앞 절을 다시 살펴보며 동형사상이 다른 동치 관계 법칙들도 만족하는지 확인해 보세요.
제가 이 모든 것으로 말하고자 하는 것은, 어떤 동형사상이든 equality로 취급하는 것이 타당하다 는 것입니다. 이런 이유로 동형사상을 사용해 동치 관계를 정의하는 관행은 범주론에서 매우 두드러지며, 범주론에서는 동형사상을 로 표기합니다. 이것은 equality를 표기하는 방식과 거의 같습니다(이 기호가 두 집합을 잇는 평행한 두 화살표와도 비슷하다는 점에 주목하세요).
구조화되지 않은 모놀리식 설계는 좋은 생각이 아니다. 아주 작은 operating system을, 이를테면 토스터 안에 넣는 경우라면 몰라도, 심지어 그 경우에도 논쟁의 여지가 있다.— Andrew S. Tanenbaum
소프트웨어 개발은 특이한 분야입니다 — 이론적으로는 그저 일종의 공학 이어야 하지만, 실제로 수행되는 방식은 때때로 장인 기술 에 더 가깝고, 합성의 원리가 충분히 활용되지 않기도 합니다.
왜 그런지 보려면, 어떤 사람이(예를 들어 저 같은 사람이) 일종의 공학 문제를 손보는 장면을 상상해 보세요. 예를 들어 어떤 기계를 고치거나 새 목적에 맞게 개조하려고 한다고 합시다. 그 기계가 기계식이거나 전기식이라면, 그 사람은 이미 존재하는 부품들로 거의 어떻게든 해내야만 할 것입니다. 왜냐하면 스스로 새 부품을 제조할 여유가 거의 없기 때문입니다(적어도 가능하다면 그렇게 하는 것을 피하려 할 것입니다). 이런 제약은 부품 제조업체들로 하여금 다재다능하고 서로 잘 맞물리는 부품을 만들게 합니다. 마치 순수 함수들이 서로 잘 어울리는 것처럼 말이죠. 그리고 이것은 공학자들이 모든 일을 혼자 하지 않고도 더 나은 기계를 만들기 쉽게 해 줍니다.
하지만 문제의 기계가 소프트웨어 기반이라면 상황은 다릅니다 — 새 소프트웨어 부품을 아주 쉽게 만들어 낼 수 있기 때문에, 설계는 어떤 부품들 사이의 경계를 흐리게 하거나, 심지어 부품이라는 개념 자체를 없애 버리고 프로그램 전체를 하나의 거대한 부품(monolithic design)으로 만들어 버릴 수 있습니다. 더 나쁘게는, 준비된 부품이 없을 때는 이런 접근이 앞 단락에서 설명한 컴포넌트 기반 접근보다 실제로 더 쉽기도 해서 많은 사람이 그렇게 합니다.
이것은 좋지 않습니다. 모놀리식 설계의 이점은 대체로 단기적이기 때문입니다 — 컴포넌트로 분리되어 있지 않으면 프로그램은 추론하기 더 어렵고, 수정하기도 더 어려우며(예를 들어 고장 난 컴포넌트를 새 것으로 교체할 수 없습니다), 전반적으로 컴포넌트 기반 프로그램보다 더 원시적입니다. 이런 이유로 저는 프로그래머들이 함수 합성의 원리를 활용하지 않으면 손해를 본다고 생각합니다. 사실 저는 이런 상황이 너무 못마땅해서, 사람들이 합성의 원리를 더 잘 이해하도록 돕기 위해 응용 범주론에 관한 책 한 권을 쓰기로 결심했습니다. 제목은 Category Theory Illustrated입니다(아, 잠깐, 제가 지금 바로 그걸 쓰고 있는 거군요?)
어쨌든, 합성 접근은 프로그래밍에서 가끔 사용되며, 사용될 때는 대체로 꽤 잘 작동합니다. 몇 가지 예를 보려면 Unix의 파이프 연산자 (|)만 봐도 충분합니다. 이것은 한 프로그램의 표준 출력을 다른 프로그램의 표준 입력으로 넣어 줍니다.
더 찾아보고 싶다면, Haskell 프로그래밍 언어 또는 다른 언어들을 위한 여러 functional programming 라이브러리를 살펴보세요. 함수 합성에 기반한 전체 프로그래밍 패러다임도 있는데, “concatenative programming”이라고 하며 Forth나 Factor 같은 언어에서 사용됩니다.