2013-08-13 2 views
1

나는 좋은 운동을 내놓았지만 효과를 내지 못한다.유형 선언 된 인스턴스에 대한 추론

아이디어는 형식 검사기가 숫자가 유효한지 여부를 알려주는 방식으로 로마 숫자를 시도하고 표현하는 것입니다.

{-# LANGUAGE RankNTypes 
       , MultiParamTypeClasses #-} 

    data One a b c = One a deriving (Show, Eq) 
    data Two a b c = Two (One a b c) (One a b c) deriving (Show, Eq) 
    data Three a b c = Three (One a b c) (Two a b c) deriving (Show, Eq) 
    data Four a b c = Four (One a b c) (Five a b c) deriving (Show, Eq) 
    data Five a b c = Five b deriving (Show, Eq) 
    data Six a b c = Six (Five a b c) (One a b c) deriving (Show, Eq) 
    data Seven a b c = Seven (Five a b c) (Two a b c) deriving (Show, Eq) 
    data Eight a b c = Eight (Five a b c) (Three a b c) deriving (Show, Eq) 
    data Nine a b c d e = Nine (One a b c) (One c d e) deriving (Show, Eq) 

    data Z = Z deriving (Show, Eq) -- dummy for the last level 
    data I = I deriving (Show, Eq) 
    data V = V deriving (Show, Eq) 
    data X = X deriving (Show, Eq) 
    data L = L deriving (Show, Eq) 
    data C = C deriving (Show, Eq) 
    data D = D deriving (Show, Eq) 
    data M = M deriving (Show, Eq) 

    i :: One I V X 
    i = One I 

    v :: Five I V X 
    v = Five V 

    x :: One X L C 
    x = One X 

    l :: Five X L C 
    l = Five L 

    c :: One C D M 
    c = One C 

    d :: Five C D M 
    d = Five D 

    m :: One M Z Z 
    m = One M 

    infixr 4 # 

    class RomanJoiner a b c where 
     (#) :: a -> b -> c 

    instance RomanJoiner (One a b c) (One a b c) (Two a b c) where 
     (#) = Two 

    instance RomanJoiner (One a b c) (Two a b c) (Three a b c) where 
     (#) = Three 

    instance RomanJoiner (One a b c) (Five a b c) (Four a b c) where 
     (#) = Four 

    instance RomanJoiner (Five a b c) (One a b c) (Six a b c) where 
     (#) = Six 

    instance RomanJoiner (Five a b c) (Two a b c) (Seven a b c) where 
     (#) = Seven 

    instance RomanJoiner (Five a b c) (Three a b c) (Eight a b c) where 
     (#) = Eight 

    instance RomanJoiner (One a b c) (One c d e) (Nine a b c d e) where 
     (#) = Nine 

    main = print $ v # i # i 

이 가능 다르게 할 수 있으며,이 솔루션은 불완전하지만, 내가 생각하는 반면, 지금 나는 그것이 RomanJoiner (한 IVX) (하나 IVX) B0에 대한 인스턴스가 없다고 불평 이유를 이해할 필요가 나는 그러한 소목 장이를 선언했다.

답변

4

문제는 인스턴스가 에 따라 선택되지 않는다는 것입니다. 하나의 확장자 FunctionalDependencies은 유형 추론에 도움이됩니다. 이를 활성화하고 | a b -> c과 같이 a # b의 유형을 ab 유형에서 유추 할 수 있습니다. 불행히도, 그게 당신이 오류 Functional dependencies conflict between instance declarations을 얻을 것이기 때문에 당신이해야 할 유일한 일은 아닙니다. HList에 정의 된 일부 클래스 (다른 곳에서는 정의 될 수 있음)를 사용하면 상충되는 두 인스턴스를 단일 인스턴스로 결합 할 수 있습니다. 두 인스턴스 (오류를 계산하면 3 개)는 일부 유형이 동일한 지 여부에 따라 가능한 결과가 선택됩니다 .

이 솔루션에 대한 몇 가지 의견

추한 것 :

  1. 당신이 lazier 표시 인스턴스가 있다면 (hCondHCond 대) 다시 값 수준에서 타입 수준에서 무슨 일이 일어나고 있는지 복제 할 필요가 없습니다 (예 : instance Show I where show _ = "I").
  2. 더 많은 현대식 확장자 TypeFamilies 변수 ba, bb, bc, babc ... 중 다수를 제거 할 수 있습니다.

    {-# LANGUAGE RankNTypes, MultiParamTypeClasses, FunctionalDependencies, ScopedTypeVariables, UndecidableInstances, FlexibleContexts, FlexibleInstances #-} 
    import Data.HList hiding ((#)) 
    import Data.HList.TypeEqGeneric1 
    import Data.HList.TypeCastGeneric1 
    import Unsafe.Coerce 
    
    data One a b c = One a deriving (Show, Eq) 
    data Two a b c = Two (One a b c) (One a b c) deriving (Show, Eq) 
    data Three a b c = Three (One a b c) (Two a b c) deriving (Show, Eq) 
    data Four a b c = Four (One a b c) (Five a b c) deriving (Show, Eq) 
    data Five a b c = Five b deriving (Show, Eq) 
    data Six a b c = Six (Five a b c) (One a b c) deriving (Show, Eq) 
    data Seven a b c = Seven (Five a b c) (Two a b c) deriving (Show, Eq) 
    data Eight a b c = Eight (Five a b c) (Three a b c) deriving (Show, Eq) 
    data Nine a b c d e = Nine (One a b c) (One c d e) deriving (Show, Eq) 
    
    data Z = Z deriving (Show, Eq) -- dummy for the last level 
    data I = I deriving (Show, Eq) 
    data V = V deriving (Show, Eq) 
    data X = X deriving (Show, Eq) 
    data L = L deriving (Show, Eq) 
    data C = C deriving (Show, Eq) 
    data D = D deriving (Show, Eq) 
    data M = M deriving (Show, Eq) 
    
    i :: One I V X 
    i = One I 
    
    v :: Five I V X 
    v = Five V 
    
    x :: One X L C 
    x = One X 
    
    l :: Five X L C 
    l = Five L 
    
    c :: One C D M 
    c = One C 
    
    d :: Five C D M 
    d = Five D 
    
    m :: One M Z Z 
    m = One M 
    
    infixr 4 # 
    
    class RomanJoiner a b c | a b -> c where 
        (#) :: a -> b -> c 
    
    
    instance RomanJoiner (One a b c) (Two a b c) (Three a b c) where 
        (#) = Three 
    
    instance RomanJoiner (One a b c) (Five a b c) (Four a b c) where 
        (#) = Four 
    
    instance RomanJoiner (Five a b c) (One a b c) (Six a b c) where 
        (#) = Six 
    
    instance RomanJoiner (Five a b c) (Two a b c) (Seven a b c) where 
        (#) = Seven 
    
    instance RomanJoiner (Five a b c) (Three a b c) (Eight a b c) where 
        (#) = Eight 
    
    data Error = Error 
    instance forall a b c a' b' c' ba bb bc bab babc z bn nine. 
        (TypeEq a a' ba, 
        TypeEq b b' bb, 
        TypeEq c c' bc, 
        HAnd ba bb bab, 
        HAnd bab bc babc, 
    
        TypeEq c a' bn, 
        HCond bn (Nine a b c b' c') Error nine, 
    
        HCond babc (Two a b c) nine z) => 
         RomanJoiner (One a b c) (One a' b' c') z where 
        (#) x y = hCond (undefined :: babc) 
           (Two (uc x :: One a b c) (uc y :: One a b c)) $ 
           hCond (undefined :: bn) 
           (Nine (uc x :: One a b c) (uc y :: One c b' c')) 
           Error 
         where uc = unsafeCoerce 
    
    main = print $ v # i # i 
    {- 
    Prints with ghc 762, HList-0.2.3 
    
    *Main> main 
    Seven (Five V) (Two (One I) (One I) 
    
    -} 
    
+0

감사합니다. 두 사람을 특별히 치료해야하는 이유는 명확하지 않지만 나머지는 다루지 않았습니다. 이것은 다른 숫자가 다른 사람들을 특별 대우해야한다는 뜻입니까? 나는 해결책을 소화해야 할 것이다. –