func :: a -> IO ()
= putStrLn "it is OK"
func _
> func 1
OK
it is
> func (Just 1)
OK
it is
> func (+)
OK
it is
> func id
OK it is
하스켈이 a -> IO ()
를 만났을 때, a
는 어떤 타입도 올 수 있습니다. *
카인드든, * -> *
카인드든 어떤 타입도 받아 들입니다.
그럼 f a
, m a
또는 a b
등 임의의 소문자 두 개를 연속으로 쓴 경우는 어떨까요?
func :: a b -> IO ()
= putStrLn "it is OK"
func x
> func 1
ghci<interactive>:75:6: error:
No instance for (Num (a0 b0)) arising from the literal ‘1’
• In the first argument of ‘func’, namely ‘1’
• In the expression: func 1
In an equation for ‘it’: it = func 1
위 오류를 해석해 보면, a b -> IO ()
를 만나면, a
는 *
카인드는 올 수 없습니다. 뒤에 b
라는 ’인자’를 받는 모양이기 때문에 a
는 적어도 * -> *
카인드는 되어야할 것처럼 보입니다. 하지만 b
가 * -> *
가 될지, 어떤 카인드가 될지 알 수 없으니 정확히 말하면, forall {k} k -> *
가 되어야 합니다. 그래야 b
가 뒤따라오는게 말이 됩니다. 게다가 b
를 받아야 하니, 첫번째 인자의 카인드는 b
와는 같은 카인드여야 합니다. (공백을 인자 적용이라는 함수로 생각하는 건 어떨까요)
> :t func
ghcifunc :: forall {k} (a :: k -> *) (b :: k). a b -> IO ()
런타임은 인자 1
을 보고, a b
를 1
과 맞추려고 할 것입니다. 오류 메시지는
1
은 Num
클래스의 인스턴스까지는 추론Num
의 인스턴스 중 (a0 b0)
는 없다는 메시지입니다.a
는 특정 타입으로 추론되지 않은 a0
, b
도 특정 타입으로 추론되지 않은 b0
입니다.)Num a => ...
(a
는 Num
클래스의 인스턴스이다.란 뜻)
Num
은 타입이 아닙니다. 클래스는 별도로 Constraint
카인드로 분류합니다.
=>
의 왼쪽은 Constraint
만 올 수 있습니다.
> :k Num
Num :: * -> Constraint
-- Num 은 * 카인드를 받아 Constraint 를 만든다.
:k
카인드는 타입 생성자(값 생성자 아니고)와 클래스만 대상으로 합니다.
리터럴 1
의 타입은 뭘까요?
> :t 1
1 :: Num p => p
리터럴 1
만 보고 타입까지 결정되진 않습니다. 클래스까지만 결정 되면, 클래스에 있는 메소드는 쓸 수 있지만, 최종 런타임 코드를 만들어내려면 메소드의 구현체가 결정되야 합니다. 이 구현체는 어떤 코드들과 어울리냐에 따라 나중에 결정됩니다.