검증 필요
타입 서명을 써주지 않으면, 가끔은 GHC가 type defaulting 규칙으로 타입을 결정합니다. 추론 단서가 없으면 클래스에 정의되어 있는 default 타입으로 추론합니다.
컴파일된 모듈은 디폴트로 monomorphism restriction이 켜져 있고, GHCi 프롬프트에서는 디폴트로 꺼져 있습니다.
> :set -XMonomorphismRestriction
> let plus = (+)
> plus 1.1 1.1
<interactive>:30:6: error:
No instance for (Fractional Integer)
• 1.1’
arising from the literal ‘In the first argument of ‘plus’, namely ‘1.1’
• In the expression: plus 1.1 1.1
In an equation for ‘it’: it = plus 1.1 1.1
> :set -XNoMonomorphismRestriction
> let plus = (+) -- Restriction을 바꾸면, 재정의 후 테스트해야 합니다.
> plus 1.1 1.1
2.2
확장이 켜져 있는 상태에서 plus
타입이 추론되는 걸 보면, 1.1
리터럴을 보고 Fractional
클래스라고 판단하는데, 타입 서명 없이 plus
를 정의하면 GHC가 알아서 type defaulting 규칙에1 따라 Num
클래스의 디폴트 타입 Integer
를 적용해 Integer -> Integer -> Integer
타입으로 추론합니다. Fractional
클래스의 Integer
인스턴스가 없기 때문에 오류가 납니다.
monomorphism restriction을 켜면 아래 함수 정의들의 해석이 달라집니다.
Monomorphism restriction - Haskell Wiki
-- GHC는 f1 :: (Show x) => x -> String 로 추론한다. (1)
= show x
f1 x
-- 아래는 위와 다르게 GHC는 f2 :: () -> String 로 추론한다. (2)
= \x -> show x
f2
-- 두 번째에 명시적으로 타입 서명을 주면 결과가 같아진다. (3)
f3 :: (Show a) => a -> String
= \x -> show x
f3
-- 포인트 프리로 적은 건 () -> String 로 추론된다. (4)
= show
f4
-- 역시 타입 서명을 추가하면 포인트 프리로 쓸 수 있다. (5)
f5 :: (Show a) => a -> String
= show f5
검증 필요
왜 (1)과 (2)가 다르게 추론될까요?
monomorphism restriction 때문에 다르게 추론됩니다.
(1)번은 f1
을 show
와 함수 바인딩2해서 x
를 추론합니다. f1
은 show x
함수에 이름을 붙여 놓은겁니다. show
가 그렇듯이 f1
은 Show
인스턴스를 인자로 받습니다. 하지만 (2)번은 show
가 아닌 \x -> ...
람다 함수에 이름을 붙여 놓은 겁니다. 여기서도 역시 하스켈은 lazy합니다. show
가 실행이 필요해지기 전까지는 x
가 뭔지 모르는 상태입니다. monomorphism 제한이 켜져 있는 상태에서 추론 단서가 전혀 없으면 ()로 추론합니다.
그럼 (4)번은 어떻게 해석하면 될까요? (1)번에서 x
는 함수 바인딩 하면서 show
에 있는 매개 변수의 정보를 가져오지만, (4)번은 매개 변수는 상관없이 show
함수와 바인딩합니다.
함수가 폴리모픽 매개 변수를 가지고 있을 때, 폴리모픽 변수가 한가지로 추론되지 않으면 이 함수는 한가지mono 모양morphic이 아닙니다. 여러 코드 덩어리가 준비되어 있고, 나중에 구체 타입이 결정 되면 한가지를 고르게 됩니다.
monomorphic을 켜면, GHC가 함수 정의를 보고 타입을 추론할 단서(타입 서명)를 찾아 보고, 구체 타입까지 도달 못하고 클래스까지만 추론 가능하면 디폴트 규칙에 따라 함수의 타입을 결정합니다.
monomorphic을 끄면, GHC는 클래스까지만 추론되면 그대로 두고, 구체 타입은 나중에 코드 조합에 따라 결정합니다.
Type defaulting in Haskell - Kwang’s Haskell Blog
Prelude numeric 클래스들만 디폴트값을 갖고 있고, 사용자 정의 클래스들에 디폴트를 지정할 수 없습니다.
Num Integer
default Real Integer
default Enum Integer
default Integral Integer
default Fractional Double
default RealFrac Double
default Floating Double
default RealFloat Double default
Part I - The Haskell 2010 Language - Chapter4 Declarations and Bindings
코드에 임시로 이름을 붙여 놓는 걸 바인딩이라 합니다. 모듈(함수)의 최상위 레벨이나, let
절, where
절등에서 바인딩할 수 있습니다.
Just x) = ...작업 코드
someFunc (Nothing = ...작업 코드 someFunc
function 바인딩은 someFunc
란 이름을 작업 코드의 결과값에 붙이는 작업입니다. 매개 변수 자리에 들어오는 값과 비교할 패턴이 있을 경우 패턴 바인딩이 일어납니다.
매개 변수를 통해 들어온 값에 이름을 붙이는 작업입니다. 예를 들어 매개 변수를 통해 Just 5
가 들어오면 Just x
란 패턴과 매칭해서 5
를 x
와 바인딩합니다.
※ 위 링크의 문서를 보면
-- (##) 이란 연산을 정의하고 아래같이 써주면?
## b : xs = ... a
이렇게 선언할 수 없다고 나옵니다. 패턴 매칭은 값 생성자와 하지 일반 함수와 하지 않습니다. 문서는 정교하게 정의를 해야하니 좀 복잡하게 쓰여 있습니다.↩︎