정적 타입이란, 컴파일 타임에 지정된 타입끼리 만나지 않는 상황이 생기면 마리 알려줘서 실행 타임에 오류를 줄이자는 것입니다. 그런데, 이 장점을 이용하려다 보니, 코딩시 일일이 타입을 “고정” 해야 하는 단점이 있습니다. 타입이 고정되면, 비슷한 동작을 하는, 아니 거의 같은 동작을 하는 함수들을 타입에 따라 모두 준비해야 합니다. (+) 함수를 정의하는데, Int 값을 받아들이게 만들었다면, Float 를 위한 (+) 를 또 만들어야 합니다.
타입별로 함수를 준비해야 하는 건 달라지지 않지만, 타입별 함수를 컴파일러가 자동으로 만들어 주면, 프로그래머 입장에선 (+) 함수 하나만 만들면 “여러 타입에 동작한다” 와 다를게 없는 상태로 만들 수 있습니다.
조금 더 확장해서, 라이브러리에선 일일이 타입별로 정의하지만, 컴파일러가 적절한 타입을 고르게 해서, 프로그래머가 타입에 대한 신경을 끌 수 있다면, 그 또한 “여러 타입에 동작한다”와 다를게 없습니다.
정적 타입 언어인 하스켈에서 타입 클래스 + 타입 추론을 써서 위와 같은 개념을 구현합니다.
코딩할 때, 타입을 고정하지 않고 열어 두면, 컴파일러가 타입 추론을 이용해 타입을 고정합니다. 열어 둘 때 활짝 열어 아무 타입이나 다 가능하게 해 놓을수도 있고, 살짝 열어, 특정 성격의 타입만 받아들이게 할 수 있습니다.
폴리모픽 매개 변수 forall a. a를 두면, GHC가 코드 조합을 할 때, 타입 결정을 뒤로 미루는 효과가 납니다. 하스켈의 타입 추론은 마치 하스켈의 Lazy한 함수 실행처럼 Lazy하게 타입을 결정합니다. 어떤 분류의 타입만 받게끔 해서 코드를 조합하다가 구체 타입이 필요한 시점이 되서야 타입이 뭔지 결정합니다. 심지어 끝끝내 구체 타입을 알 필요가 없는 경우도 있긴 있습니다.(ex. length함수, 컨테이너 안에 들어 있는 엘리먼트 수만 세기때문에 엘리먼트 뭔지 알필요가 없습니다.)
어찌됐든 GHC가 알아서 단서들을 모아서 타입 추론을 하는데, 이 때 GHC가 써먹도록 만드는게 타입 레벨 함수입니다.
이렇게 할 수 있는 이유는, 하스켈은 강력한 타입 추론 기능을 가지고 있기 때문입니다. 이렇게 강력한 기능을 좀 더 적극적으로 써보자는 개념이 Dependent Haskell 입니다. (하스켈에서 가지를 친 이드리스Idris 언어는 이 개념에 특화되어 있는 언어입니다.)
Dependent Function
https://serokell.io/blog/why-dependent-haskell
Int를 받고 Int를 내뱉는 함수라면, 2같은 값을 받아 연산해서 3같은 값을 결과로 내 뱉습니다.
* -> *
카인드를 받고 *
카인드를 내 뱉는 타입 레벨 함수는 결과로 Int
같은 타입을 내 뱉습니다.
1,2,3… 등을 뭉뚱그려서(추상화 해서) Int
타입이라 분류했습니다.
Int
, Double
, Char
등을 뭉뚱그려서 *
카인드로 분류하고
Maybe a
, List a
, Some a
등을 뭉뚱그려서 * -> *
카인드로 분류합니다.
값은 타입으로 분류하고(추상화하고), 타입은 카인드로 분류(추상화)합니다.
타입은 컴파일 타임에 코드를 “조립”할 때만 필요합니다. 예를 들어, 반드시 Int 타입만 받게 지정해 놓은 타입에 Double을 내뱉는 코드 조각이 맞춰지는 상황이 오면 문제를 잡아냅니다. 이렇게 타입 체크를 일일이 하면서 문제 없이 모든 코드 조각을 맞췄다면(타입 추론을 통과했다면), 타입이 달라 문제가 생기는 경우는 없게됩니다. 그러면 런타임에는 굳이 타입 정보를 끌고 다녀야 할 이유가 없어집니다. 그렇기 때문에 타입 레벨 프로그래밍도 컴파일 타임까지만 노리고 개념을 활용하는데, 너무 적극적으로 쓰다 보면 런타임에도 타입 레벨에 있는 정보가 필요할 때가 있습니다. 이럴 때 싱글턴 라이브러리의 도움을 받습니다.
참고 - stackoverflow - type level programming meaning at run time
런타임에는 타입 정보가 모두 사라집니다. 컴파일 타임을 넘어 런타임에도 타입 레벨에 있는 정보가 필요할 경우, 값 레벨로 정보를 복사해 놓을 때 쓰는 라이브러리입니다.
https://ryanglscott.github.io/2019/08/29/how-ghc-8-8-nearly-killed-singletons/
https://blog.jle.im/entry/introduction-to-singletons-1.html
필요한 확장
TypeFamilies
DataKinds
PolyKinds
GADTs
RankNTypes
ScopedTypeVariables
TypeApplications
TypeOperators
MultiParamTypeClasses
FlexibleContexts
FlexibleInstances
… 계속
미완성 포스트