2022. 10. 3. 00:00ㆍFunctional Programming/Category Theory
참고 : https://bartoszmilewski.com/2014/11/24/types-and-functions/
동적 type 보다 정적 type 이 좋은 이유 (컴파일 타임에 오류를 발견한다)
Type 은 Composition 가능성에 관한 것이다.
Category Theory 는 함수를 합성하는 것이다.
그러나 모든 함수를 합성할수 있는것은 아니다.
한 함수의 대상(return)개체는 다른 함수의 원본(parameter) 개체와 같아야 한다.
Composition 이 작동하려면 이처럼 두 끝이 맞아야 한다.
언어의 type system 이 강할수록 이 일치를 더 잘 설명하고 기계적으로 확인할 수 있다.
유형을 다루는 것이 프로그래머에게 너무 많을 부담을 준다는 주장이 있지만
실제로 대부분의 컴파일러는 유형 추론을 할 수 있다.
C++ 같은 경우도 auto 변수를 선언하고 컴파일러가 해당 유형을 파악하도록 한다.
C# 같은 경우는 var 변수를 이용하여 컴파일러가 유추하도록 한다.
이러한 주장에도 불구하고 강력한 정적 유형 검사는
거대한 시스템의 복잡한 버그로 부터 프로그램을 보호한다.
하지만 이러한 정적 유형 시스템을 사용하여
컴파일러가 미리 많은 오류를 발견한다고 하더라도
프로그램의 품질을 높이기 위해서 Test 는 꼭 필요하다.
또한 단위 테스트만으로 강력한 type 을 대체할 수 있다는 주장은
단위 테스트 자체가 버그를 양산할 수 있다는 점에서 모순이다.
또한 Refactoring 이라는 관점에서 보았을때 특정 함수의 인수 유형을 변경하는 것이
강력한 형식의 언어에서는 compile time 에러로 나타나거나
compiler 에 의해 참조를 찾아 자동으로 변경한다.
하지만 약한 형식의 언어에서는 단위테스트가 일부 불일치를 포착하지만
포착하지 못하는 경우가 많다. 테스트만 으로는 이러한 부분을 대체할 수 없다.
Types (유형) 란 무엇인가?
유형은 값의 집합이다.
Bool 유형은 True, False 의 집합이다.
Integer 를 예로 들어 보자
우리는 이것을 정수 집합이라고 말하고 있다.
Haskell 에서 정수는 무한 집합이며 임의의 정밀도 산술을 수행할 수 있다.
C++ int 는 유한한 값을 갖는 집합 이다.
집합은 매우 특별한 Category 이다.
두 요소를 하나로 mapping 할 수는 있지만
하나의 요소를 둘로 mapping 할 수는 없다.
수학적 모델이 필요한 이유
프로그래머가 자신이 만든 프로그램에 정확성을 증명하기 위해서
몇줄의 코드를 던져보고 정확히 동작한다 안한다 라고 판단하는 것은
정확성 면에서 많이 떨어진다. 그렇다고 해서 완벽하게 정확성을 증명하는 것 또한 어렵다.
하지만 방법이 전혀 없는 것은 아니다. 표시적 의미론 (denotational semantics) 이 존재한다.
컴퓨터 과학에서 표시론적 의미론은 언어에서 수식의 뜻을 표현하는
수학적인 기호들로부터 만들어진 프로그래밍 언어의 의미를 형식화하는 접근법이다.
다른 접근법은 공리적인 의미론과 연산 의미론을 포함하는 프로그래밍 언어의 형식적 의미를 제공한다. 위키백과
즉 수학을 이용하는 것이다. 정리 증명이 어렵다고 생각할 수 있지만 프로그래밍에서의 문제는 수학적으로 보았을때 일반적으로 매우 간단하다.
Haskell 에서 factorial 은 다음과 같이 표현된다.
fact n = product [1..n]
그렇다면 키보드에서 문자를 읽거나 네트워크를 통해 패킷을 보내는 수학적 모델은 무엇인가?
이것은 쉽지 않았으며 표시적 의미론을 통해 표현하기 힘든 것이었다.
그러나 범주이론(Category Theory)에서 돌파구가 나왔다.
Computational Effect 가 Monad 에 매핑될 수 있음을 발견한 것이다.
프로그래밍을 위한 수학적 모델을 갖는 것은
소프트웨어의 정확성에 대해 공식 증명을 수행할 수 있다는 의미이다.
일반적인 고객용 프로그램에서는 이러한 증명이 그다지 중요하지 않을것 같지만
인간의 생명을 다루는 의료 시스템 같은 경우에는 아주 중요하다.
Pure and Dirty Functions
프로그래밍 언어에서 동일한 입력에 대해 항상 동일한 결과를 생성하고 부작용이 없는 함수를 순수 함수라고 한다. Haskell과 같은 순수 함수형 언어에서는 모든 함수가 순수하다. 이러한 언어는 표시적 의미론과 범주 이론을 사용하여 모델링하는 것이 더 쉽다. 나중에 우리는 모나드가 순수 함수만을 사용하여 모든 종류의 효과를 모델링하는 방법을 볼 것이다.
Example of Type
빈집합은 Void 로 표현 할수 있다.
Void를 사용하는 함수를 정의할 수 있지만 호출할 수는 없다.
그것을 호출하려면 Void 유형의 값을 제공해야 하며 그것은 아무것도 없다.
이 함수가 반환할 수 있는 항목에 대해서는 제한이 없다.
모든 유형을 반환할 수 있다(하지만 호출할 수 없기 때문에 절대 반환하지 않음).
즉, 반환 유형에서 다형성인 함수이다.
Void 형태는 호출할 수 없다고 했으니 프로그래밍에서는 다음과 같은 형태로 dummy 를 만들어 낼 수 있다.
int f44() { return 44; }
즉 전달하는 인수 없이 return 을 하는 함수는 void 형태라고 할 수 있다.
만약 return 을 void 형태로 표현 하고 싶다면
Haskell 은 _ 를 사용하여 처리 한다.
fInt :: Integer -> ()
fInt _ = ()
다형성을 적용하여 모든 유형에 대해 함수를 정의 한다면 다음과 같다.
unit :: a -> ()
unit _ = ()
C++ 은 다음과 같이 처리 할 수 있다.
template<class T>
void unit(T) {}
c++17 부터는 다음을 지원 한다.
https://en.cppreference.com/w/cpp/utility/variant/monostate
std::monostate - cppreference.com
struct monostate { }; (since C++17) Unit type intended for use as a well-behaved empty alternative in std::variant. In particular, a variant of non-default-constructible types may list std::monostate as its first alternative: this makes the variant itself
en.cppreference.com
또는 class 를 빈 값으로 구현하여 다음과 같이 처리 하기도 한다.
class unit_type {};
const unit_type the_unit;
unit_type f(unit_type) { return the_unit; }
unit_type g(unit_type) { return the_unit; }
int main()
{
f(g(the_unit)); // f 나 g 가 void 형태였다면 이것은 compile time 에 error 가 된다.
return 0;
}
/// 이것과 같이 error 가 발생한다.
void f(void) {}
void g(void) {}
int main(void)
{
f(g()); // compile-time error here
return 0;
}
c# 에서는 Action<T> 를 통해 위에 기능을 처리 할 수 있다.
public delegate void Action<in T>(T obj);
과제
1. 선호하는 언어로 고차 함수(또는 함수 객체) 메모이즈를 정의하자.
이 함수는 순수 함수 f를 인수로 사용하고 모든 인수에 대해 원래 함수를 한 번만 호출하고 결과를 내부적으로 저장한 후 다음과 같이 호출될 때마다 저장된 결과를 반환한다는 점을 제외하고는 f와 거의 똑같이 동작하는 함수를 반환한다.처음 호출할 때는 결과를 기다려야 하지만 동일한 인수로 후속 호출에서 결과를 즉시 가져와야한다.
2. 난수를 생성하는 데 일반적으로 사용하는 표준 라이브러리의 함수를 메모해 보자. 작동하나?
3. 대부분의 난수 생성기는 시드로 초기화할 수 있다.
시드를 가져오고 시드로 난수 생성기를 호출하고 결과를 반환하는 함수를 구현하자.
해당 기능을 Memoize 하자. 작동하나?
4. Bool에서 Bool까지 얼마나 많은 다른 함수가 있습니까? 모두 구현할 수 있습니까?
5. 개체가 Void, ()(단위) 및 Bool 유형뿐인 범주의 그림을 그립니다. 이러한 유형 간의 가능한 모든 기능에 해당하는 화살표가 있습니다. 함수의 이름으로 화살표에 레이블을 지정합니다.
관련영상
'Functional Programming > Category Theory' 카테고리의 다른 글
Product and CoProduct (0) | 2022.10.24 |
---|---|
Kleisli Catogories (0) | 2022.10.17 |
Monoid as Category (0) | 2022.10.10 |
Category : The Essence of Composition (Category: Composition 의 본질) (0) | 2022.09.26 |
잠시 멈추어서... (0) | 2022.09.26 |