sehyunny (sehyun hwang) / 작성글 - velog
A different way to think about TypeScript (rob.directory)
"타입스크립트의 타입 시스템은 타입에 대해 동작하는 순수 함수형 언어로 생각할 수 있습니다"라는 말은, 타입스크립트의 타입 시스템이 일종의 함수형 프로그래밍 언어처럼 작동한다는 것을 의미합니다. 이를 이해하기 위해서는 먼저 순수 함수형 언어가 무엇인지 알아야 합니다.
순수 함수형 언어란?
순수 함수형 언어는 함수형 프로그래밍 패러다임을 따르는 언어로, 다음과 같은 특징이 있습니다:
- 순수 함수: 동일한 입력에 대해 항상 동일한 출력을 반환하며, 함수 외부의 상태를 변경하지 않는 함수입니다. 즉, 부작용이 없습니다.
- 불변성: 데이터는 한 번 정의되면 변경할 수 없습니다. 모든 데이터는 불변(immutable)입니다.
- 함수 일급 객체: 함수가 다른 함수의 인자로 전달되거나 반환될 수 있으며, 변수에 할당될 수도 있습니다.
- 고차 함수: 함수를 인자로 받거나 함수를 반환하는 함수를 만들 수 있습니다.
타입스크립트의 타입 시스템과 순수 함수형 언어
타입스크립트의 타입 시스템은 코드의 타입을 체크하고 추론하는 데 사용됩니다. 타입스크립트에서 타입은 런타임에 영향을 미치는 요소가 아니라, 컴파일 타임에 코드의 정확성을 보장하기 위한 도구입니다.
이 시스템이 순수 함수형 언어와 비슷하다고 말하는 이유는 다음과 같습니다:
- 타입 연산의 순수성: 타입스크립트의 타입 시스템에서 타입은 불변적이며, 주어진 입력 타입에 대해 항상 동일한 결과를 생성합니다. 이는 순수 함수형 언어의 순수 함수와 유사합니다.
- 타입 레벨 함수: 타입스크립트에서는 타입을 다루는 함수(타입 매핑, 제네릭 함수 등)를 사용할 수 있습니다. 이는 함수형 프로그래밍의 고차 함수 개념과 비슷하게 동작합니다.
- 타입 연산: 타입스크립트에서는 조건부 타입, 제네릭, 유니언 타입 등을 사용해 복잡한 타입 연산을 수행할 수 있는데, 이는 마치 함수형 프로그래밍에서 데이터를 변환하고 처리하는 것과 유사합니다.
요약
따라서 "타입스크립트의 타입 시스템은 타입에 대해 동작하는 순수 함수형 언어로 생각할 수 있습니다"라는 말은, 타입스크립트의 타입 시스템이 마치 순수 함수형 언어처럼 타입을 입력으로 받아 일정한 규칙에 따라 타입을 반환하는 순수한 함수의 집합으로 작동한다는 것을 의미합니다. 이를 통해 코드의 타입 안정성을 보장하고, 복잡한 타입 연산을 함수형 프로그래밍 방식으로 처리할 수 있게 되는 것이죠.
순수 함수형 언어에는 다음과 같은 언어들이 있습니다:
- Haskell:
- 가장 유명한 순수 함수형 프로그래밍 언어 중 하나입니다.
- 모든 함수가 순수하며, 부작용이 발생하는 코드는 IO Monad와 같은 특별한 구조로 처리됩니다.
- PureScript:
- Haskell의 영향을 많이 받은 언어로, JavaScript로 컴파일되는 순수 함수형 언어입니다.
- JavaScript 환경에서 순수 함수형 프로그래밍을 적용하고자 하는 경우에 사용됩니다.
- Elm:
- 웹 개발을 위해 설계된 함수형 언어입니다.
- 부작용을 제거하여 안정적인 웹 애플리케이션을 만들 수 있습니다.
- Idris:
- 타입 시스템이 매우 강력한 순수 함수형 언어입니다.
- 증명 기반 개발을 지원하여, 프로그램의 논리적 정확성을 검증하는 데 유용합니다.
- Agda:
- 증명 가능 프로그래밍 언어로, 타입과 프로그램을 증명과 일치시켜 개발할 수 있습니다.
- 순수 함수형의 강력한 타입 시스템을 활용합니다.
- *F (F star)**:
- 타입 안전성을 보장하기 위해 설계된 함수형 프로그래밍 언어입니다.
- 보안과 안전성이 중요한 시스템에서 많이 사용됩니다.
이 외에도 다양한 언어들이 함수형 프로그래밍 패러다임을 따르고 있지만, 위에 나열된 언어들은 "순수 함수형"의 특징을 가장 잘 반영하는 언어들입니다. 이들 언어는 상태 변이, 부작용 등을 배제하거나 최소화하여 코드의 예측 가능성과 안정성을 높이는 데 초점을 맞추고 있습니다.
순수 함수형 언어와 대비되는 기존 언어들은 주로 명령형 언어(Imperative Languages)라고 불립니다. 때로는 객체지향 언어(Object-Oriented Languages)나 절차형 언어(Procedural Languages)와 같은 특정 프로그래밍 패러다임을 따르는 언어들을 포함하기도 합니다. 이러한 언어들은 순수 함수형 언어와 몇 가지 중요한 차이점을 가지고 있습니다.
명령형 언어란?
명령형 언어는 프로그램의 상태를 변경하는 명령(명시적 지시)을 통해 작업을 수행하는 프로그래밍 패러다임을 따르는 언어들을 말합니다. 이들은 주로 다음과 같은 특징을 가집니다:
- 상태 변경:
- 변수의 값을 변경하거나, 데이터 구조를 수정하는 등 프로그램의 상태를 직접적으로 변경합니다.
- 부작용:
- 함수나 명령이 외부 상태(예: 전역 변수, 파일 시스템 등)에 영향을 미치거나, 외부로부터 영향을 받습니다.
- 제어 구조:
- 반복문(for, while 등), 조건문(if, switch 등)과 같은 명령적 제어 구조를 사용하여 프로그램의 흐름을 제어합니다.
- 명령의 순서:
- 프로그램이 실행되는 순서가 중요하며, 명령들이 순차적으로 실행됩니다.
대표적인 명령형 언어들
- C:
- 절차형 언어의 대표주자로, 시스템 프로그래밍에 널리 사용됩니다.
- C++:
- C에 객체지향 프로그래밍(OOP) 개념을 추가한 언어입니다.
- Java:
- 객체지향 언어로, 플랫폼 독립적인 애플리케이션 개발에 널리 사용됩니다.
- Python:
- 다중 패러다임 언어로, 명령형, 객체지향, 함수형 프로그래밍을 모두 지원합니다.
- JavaScript:
- 주로 웹 개발에 사용되는 다중 패러다임 언어로, 명령형과 이벤트 기반 프로그래밍을 지원합니다.
순수 함수형 언어와의 비교
순수 함수형 언어와 명령형 언어는 다음과 같은 점에서 상이합니다:
- 상태 관리:
- 순수 함수형 언어: 상태 변경을 피하고, 모든 데이터는 불변(immutable)입니다.
- 명령형 언어: 상태 변경을 빈번하게 사용하며, 가변(mutable) 데이터를 다룹니다.
- 부작용:
- 순수 함수형 언어: 함수는 부작용이 없으며, 동일한 입력에 대해 항상 동일한 출력을 반환합니다.
- 명령형 언어: 함수나 명령이 부작용을 가질 수 있어, 외부 상태를 변경하거나 의존할 수 있습니다.
- 프로그램 구조:
- 순수 함수형 언어: 함수 조합과 재사용을 중시하며, 고차 함수와 같은 기능을 적극 활용합니다.
- 명령형 언어: 명령의 순서와 상태 변화를 중시하며, 제어 흐름을 기반으로 프로그램을 구성합니다.
요약
순수 함수형 언어와 대비되는 명령형 언어는 상태 변경과 부작용을 허용하며, 명령의 순서를 통해 프로그램을 구성하는 방식입니다. 이러한 명령형 언어들은 객체지향이나 절차형 프로그래밍 패러다임을 따르며, 일상적인 소프트웨어 개발에서 널리 사용되고 있습니다. 반면, 순수 함수형 언어는 이러한 특성을 배제하고 함수의 순수성과 불변성을 강조하여, 코드의 예측 가능성과 안정성을 높이는 데 중점을 둡니다.
순수 함수형 언어와 명령형 언어를 섞어서 사용하는 경우가 많습니다. 실제 개발에서는 두 가지 패러다임의 장점을 결합하여 사용하는 것이 일반적입니다. 또한, 각각의 방식만을 고수하기보다, 특정 문제나 프로젝트의 요구사항에 따라 적절한 패러다임을 선택하는 것이 중요합니다.
섞어서 사용하는 경우
- 다중 패러다임 언어:
- 많은 현대적인 프로그래밍 언어는 하나의 패러다임에만 국한되지 않고, 여러 패러다임을 지원합니다.
- 예를 들어, Python이나 JavaScript 같은 언어는 명령형, 객체지향, 함수형 프로그래밍을 모두 지원합니다. 이를 통해 개발자는 상황에 맞게 서로 다른 패러다임의 장점을 활용할 수 있습니다.
- 예시: Python에서는 함수형 프로그래밍의 특징인 map, filter 같은 함수를 사용하면서도, 명령형 제어 구조(for, if)를 사용할 수 있습니다.
- 순수 함수형 언어 내에서 명령형 스타일 사용:
- 순수 함수형 언어인 Haskell조차도 필요에 따라 명령형 스타일의 프로그래밍을 지원합니다. 예를 들어, do notation을 사용하여 IO 작업이나 상태 변경을 처리할 수 있습니다.
- 순수 함수형 언어에서 명령형 스타일을 사용할 때는 주로 부작용을 안전하게 격리하여 사용합니다.
- 명령형 언어에서 함수형 프로그래밍 기법 도입:
- 명령형 언어에서 불변성, 고차 함수, 순수 함수와 같은 함수형 프로그래밍 기법을 도입하여 코드의 예측 가능성, 재사용성, 테스트 용이성을 높입니다.
- 예시: JavaScript에서 Array.prototype.map()과 같은 함수형 스타일의 메서드를 사용하여 배열을 변환할 수 있습니다.
언제 어떤 패러다임을 사용하는가?
순수 함수형 프로그래밍을 사용하는 경우
- 복잡한 상태 관리:
- 상태 변경이 빈번하거나, 상태 관리가 복잡한 시스템에서 불변성(immutable)과 순수 함수(pure function)를 사용하여 버그를 줄이고, 예측 가능성을 높일 수 있습니다.
- 예시: 상태 관리가 중요한 대규모 애플리케이션에서 상태 관리를 단순화하고, 테스트 용이성을 높이기 위해 함수형 프로그래밍을 사용합니다.
- 병렬 처리:
- 순수 함수형 언어에서는 부작용이 없기 때문에 병렬 처리를 쉽게 구현할 수 있습니다. 이는 동시성(concurrency)이나 병렬성을 요구하는 시스템에서 유리합니다.
- 예시: Haskell의 병렬 처리 라이브러리들은 순수 함수의 특성을 이용해 병렬 처리를 효율적으로 수행합니다.
- 수학적 계산, 알고리즘 설계:
- 함수형 프로그래밍의 수학적 성격은 복잡한 알고리즘이나 계산 모델을 설계하는 데 적합합니다.
- 예시: 금융, 데이터 분석 등에서 수학적 모델을 구현할 때 함수형 프로그래밍이 유리할 수 있습니다.
명령형 프로그래밍을 사용하는 경우
- 직관적이고 명시적인 흐름 제어:
- 명령형 프로그래밍은 코드의 실행 흐름을 명시적으로 제어할 수 있기 때문에, 프로그램의 실행 순서를 직접 다루기 쉬운 경우에 유리합니다.
- 예시: 루프를 사용하여 반복 작업을 수행하거나, 조건문을 통해 분기 처리를 하는 상황에서 명령형 코드가 더 직관적일 수 있습니다.
- 상태가 중요한 시스템:
- 게임 개발이나 사용자 인터페이스(UI) 설계와 같이, 상태 변화를 직접적으로 관리해야 하는 시스템에서는 명령형 프로그래밍이 더 적합할 수 있습니다.
- 예시: 게임 루프에서 객체의 상태를 계속해서 업데이트해야 하는 경우, 명령형 접근 방식이 자연스러울 수 있습니다.
- 낮은 수준의 시스템 프로그래밍:
- 하드웨어와 직접 상호작용하는 시스템 프로그래밍(C/C++ 사용)이 필요한 경우, 명령형 프로그래밍이 필수적입니다. 이는 메모리 관리, 포인터 연산, 하드웨어 자원 관리 등을 명시적으로 처리할 수 있기 때문입니다.
- 예시: 운영체제 커널, 드라이버 개발 등에서 명령형 프로그래밍이 필수적입니다.
요약
- 섞어서 사용: 현대 프로그래밍에서는 특정 패러다임을 고수하기보다 상황에 맞게 섞어서 사용하는 것이 일반적입니다.
- 순수 함수형 프로그래밍: 상태 관리, 병렬 처리, 수학적 계산에 적합합니다.
- 명령형 프로그래밍: 직관적 제어 흐름, 상태 변화, 하드웨어와의 상호작용에 적합합니다.
상황에 맞게 두 패러다임을 적절히 활용하는 것이 중요합니다.