확장 MultiParamTypeClasses, FunctionalDependencies

Posted on July 29, 2020

MultiParamTypeClasses

{-# LANGUAGE MultiParamTypeClasses #-}
class SomeC a b where
  method :: a -> b -> IO ()

instance SomeC Integer Double where
  method _ _ = putStrLn "Double ok"

instance SomeC Integer Char where
  method _ _ = putStrLn "Char ok"

> method 1 1.0
Double ok
> method 1 'a'
Char ok

타입 클래스를 정의할 때, 타입 매개 변수가 두 개 이상인 것도, 또는 하나도 없는 것도 가능하게 해주는 확장입니다.
확장을 끄고 위 클래스를 정의하면 에러가 납니다.

    • Too many parameters for class ‘SomeC’
      (Enable MultiParamTypeClasses to allow multi-parameter classes)
-- 클래스 매개 변수는 하나만 가능합니다. 

    • Illegal instance declaration for ‘SomeC Integer Double’
        (Only one type can be given in an instance head.
         Use MultiParamTypeClasses if you want to allow more, or zero.)
-- 인스턴스 헤드에는 하나의 타입만 있어야 합니다.

아마도 초창기 하스켈은, 인스턴스 헤드에 있는 타입의 가장 바깥을 싸고 있는 생성자하고만 매칭을 시도 했던 것 같습니다. 디폴트로 이 기능이 들어가 있지 않은 이유를 아는 분은 댓글 부탁드립니다.

FunctionalDependencies

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}

class SomeC a b c where
  method :: a -> b -> c

instance SomeC Int Int Int where 
  method a b = a + b

main :: IO ()
main = putStrLn $ show $ method 1 2  

위 코드는 method 의 결과 타입이 모호하다는 에러를 내뱉습니다. 왜 GHC가 추론하지 못할까요?

instance SomeC Int Int Double where
  method a b = (a + b) :: Double

이런 인스턴스가 추가로 있다고 생각하면, method 1 2 를 보고, SomeC Int Int Int를 골라야 할지, SomeC Int Int Double을 골라야 할지 알 수 없습니다. 인스턴스가 하나만 있다고 GHC가 무조건 있는 인스턴스를 고르는 건 아닙니다.

이럴 때, 매개 변수로 들어오는 타입들 중 하나가 다른 타입에 의존한다고 알려주면 GHC가 추론을 마칠 수 있는 단서가 됩니다.

class SomeC a b c | a b -> c where
  method :: a -> b -> c

instance SomeC Int Int Int where 
  method a b = a + b

| a b -> c 구문으로 c 타입은 a, b 타입에 의존한다고 해 놓으면, a :: Int, b :: Int를 만나면 c :: Int로 추론합니다.

물론 이 상태에서

instance SomeC Int Int Double where
  method a b = a - b

을 추가하면

Functional dependencies conflict between instance declarations:
  instance SomeC Int Int Int -- ... 
  instance SomeC Int Int Double -- ...

의존성 충돌이 난다고 에러가 납니다.

Github 계정이 없는 분은 메일로 보내주세요. lionhairdino at gmail.com