2011-12-02 4 views
2

최소한의 예제 코드 :모호한 유형

class IntegralAsType a where 
    value :: (Integral b) => a -> b 

class (Num a, Fractional a, IntegralAsType q) => Zq q a | a -> q where 
    changeBase:: forall p b . (Zq q a, Zq p b) => a -> b 

newtype (IntegralAsType q, Integral a) => ZqBasic q a = ZqBasic a deriving (Eq) 

zqBasic :: forall q a . (IntegralAsType q, Integral a) => a -> ZqBasic q a 
zqBasic x = ZqBasic (mod x (value (undefined::q))) 

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where 
    changeBase (ZqBasic x) = fromIntegral (value (undefined::p)) -- ZqBasic is an instance of Num 

저는 여기에 달성하기 위해 노력하고있어에 약간의 배경이있다 : IntegralAsType 다른 계수와 두 숫자의 추가 같은 것을 방지하여 컴파일시에 안전을 입력 보장 . ZqBasic은 Zq 타입의 내부 표현이며, 다른 것들이 있습니다. 이것이 Zq가 정의 된 방식입니다. 목표는 내부 표현에 투명한 시스템을 얻는 것입니다.

내 문제는 changeBase 함수에 있습니다. 나는 'p'타입에 대해 명시 적 forall을 사용하고 있습니다. 그러나 여전히 "모호한 타입 변수 a0"(IntegralAsType a0)은 값의 사용으로 인해 발생합니다. "

왜 나는 혼란 스럽습니다. 이 오류가 발생했습니다. 특히 이전 글에서는 changeBase 함수와 동일한 설정을 가진 것처럼 보이는 "zqBasic"함수와 같은 도움을 받았습니다. 명시 적 한정사 'forall q a'를 추가하여 zqBasic에서 모호한 변수 오류를 수정했습니다. 이 한정 기호가 없으면 모호한 유형 변수 오류가 발생합니다. 왜 한정자가 필요한지 이해하지만 changeBase에 도움이되지 않는 이유는 모르겠다.

감사

+0

추가 참고 사항 : ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses 및 FunctionalDependencies 확장을 사용하고 있습니다.위의 코드는 'undefined :: p'가 'changeBase'의 (undefined :: q)로 대체 될 때 작동합니다. – crockeea

답변

3

어쨌든 범위에없는 p 사용하고 있기 때문에 ScopedTypeVariables 여기에 도움이되지 사용 보인다. 다음 정의를 비교하십시오.

changeBase (ZqBasic x) = fromIntegral (value (undefined::foobar)) 

이것은 새로운 유형의 변수를 작성하기 때문에 동일한 오류가 발생합니다.

changeBase (ZqBasic x) = fromIntegral (value (5::p)) 

그러나 이것은 다른 오류를 제공합니다.관련 비트는 :

Could not deduce (Num p1) arising from the literal `5' 
     (snip) 
    or from (Zq q (ZqBasic q a), Zq p b) 
     bound by the type signature for 
       changeBase :: (Zq q (ZqBasic q a), Zq p b) => ZqBasic q a -> b 

p가 새로운 입력 변수로 인스턴스화되고 있음을 나타낸다. 형식 시그니처의 forall이 클래스의 형식 매개 변수가 아닌 형식 변수를 실제 선언에서 가져 오지 않는다고 추측합니다. 그러나 이 변수를 기본 선언의 범위로 가져옵니다.

어쨌든, 여기 또는 여기에 없습니다.

형식 변수에 대한 액세스가 필요한 대부분의 문제를 해결하는 것만으로도 충분합니다. 유형을 적절히 조작 할 수있는 보조 기능을 만들면됩니다. 예를 들어, 이런 말도 안되는 기능은 팬텀 유형의 용어를 연상 척합니다

zq :: (Zq q a) => a -> q 
zq _ = undefined 

fundep가 q이 모호하게하기 때문에 이것은 기본적으로 그냥, 당신에게 함수 적 종속성에 직접 용어 수준의 액세스를주고있다 어떤 특정 a. 물론 실제 가치를 얻을 수는 없지만 그 점은 중요하지 않습니다. undefined에 불편을 끼쳐 드린 경우 [] :: [q] 대신 비슷한 효과를 사용하고 head 또는 필요한 경우에만 사용하십시오.

지금 당신은 강제로 올바른 유형을 추론하는 where 절 또는 이것 저것을 사용하여 약간의 주위에 물건을 비틀 수 있습니다

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where 
    changeBase (ZqBasic x) = b 
    where b = fromIntegral (value (zq b)) 

여기에서 일어나고 것은 b 우리가 원하는 실제 일이다, 그러나 우리는 value 필요 b 유형으로 결정되는 유형 p을 보려면 결과에 임시 이름을 지정하여 필요한 유형을 얻는 데 사용할 수 있습니다.

많은 경우에 추가 정의없이이 작업을 수행 할 수 있지만 형식 클래스를 사용하면 임시 다형성을 피하고 "재귀"가 다른 인스턴스를 포함 할 수 없도록해야합니다.

관련 메모에서 표준 라이브러리 함수 asTypeOf은 이러한 유형의 바이올린 작업과 정확히 일치합니다.

1 :

+0

위의 코드에 두 개의 'b'가 있습니다. 처음 두 개는 지역 변수이고, 세 번째 항목은 서명의 유형을 나타냅니다. 맞습니까? – crockeea

+0

@Eric : 마지막 코드 단편에서 'b'의 세 가지 용도는 모두 같은 것을 말합니다. 유형을 전혀 언급하지 않습니다. 실제로 유형을 참조 할 필요를 피하는 것이 목표입니다. –

1

호출 어떤 종류의 a0-p에서 value (undefined::p) 변환합니다. value 유형에서 우리가 알아낼 수있는 유일한 것은 a0이 필수 유형이라는 것입니다.

이 값은 fromIntegral으로 전달되며, 이는 a0에서 b으로 변환됩니다. fromIntegral 유형에서 우리가 알아낼 수있는 유일한 것은 a0이 필수 유형이라는 것입니다.

아무 것도 어떤 형식을 결정하지 않으므로, value (undefined::p) 표현식에 형식 주석이 필요하므로 모호성을 해결하십시오. 형식 서명을 살펴본 결과 value은 추가 변환을 수행하지 않고 올바른 반환 형식을 생성 할 수 있어야합니다. fromIntegral에 대한 전화를 삭제할 수 있습니까?

편집 당신은 ScopedTypeVariables 확장을 활성화해야합니다. ScopedTypeVariables이 없으면 유형 변수는 둘 이상의 유형 서명에 언급 될 수 없습니다. 이 경우 변수 이름 p은 함수의 유형 시그니처와 본문에서 동일한 변수를 참조하지 않습니다.

다음 코드는 나를 위해 작동 :

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where 
    changeBase = zqBasicChangeBase 

zqBasicChangeBase :: forall p b q a. 
    (IntegralAsType q, IntegralAsType p, Integral a, Zq p b) => ZqBasic q a -> b 
zqBasicChangeBase (ZqBasic x) = fromIntegral (value (undefined :: p) :: Integer) 
+0

명확한 설명 주셔서 감사하지만 작동하지 않는 것 같습니다. 에서 임의의 (임의의) 적분 값으로 작업을 통합하여 "올바른"유형으로 변환해서는 안됩니까? 반환 유형의 컨텍스트를 기반으로 fromIntegral에 대한 올바른 호출 유형을 유추해야한다고 생각합니다. 또한, 가치가 무엇에 대한 코드를 잘 작동합니다 undefined :: p 대신 "(값 (정의되지 않은 :: q))", 그래서 p 때문에 인수로 바인딩되지 않은 문제가 나타납니다? – crockeea

+0

나는 또한 value (즉 (value (undefined :: p)) :: Integer)에 대한 호출 유형을 명시 적으로 시도했지만 동일한 오류가 발생하므로 모호한 유형이 아닌 것으로 보입니다 가치에서. – crockeea

+0

타입 시그니처가 원하는 것을 의미하는'ScopedTypeVariables' 확장이 필요합니다. 확장 기능을 켜고 컴파일합니다. – Heatsink

0

두 (? 기본적으로 상당) 그 일에 접근 C.A. 사용 맥캔의 접근 방식 :

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where 
    changeBase (ZqBasic x) = b 
    where b = fromIntegral ((value (zq b))*(fromIntegral (value (zq q)))) -- note that 'q' IS in scope 

2 : 오히려 b>을 ​​A- 서명을하는 것보다, 내가 B-하는 changeBase의 서명을 변경>를 가 다음 다음 작동합니다

instance (IntegralAsType q, Integral a) => Zq q (ZqBasic q a) where 
    changeBase x = fromIntegral ((value (zq x))*(fromIntegral (value (zq q)))) -- note that 'q' IS in scope 

목표는 언제나 인수와 반환 유형에 대한 형식 매개 변수에 액세스 할 수 있도록하려면이 두 가지 방법을 모두 사용할 수 있습니다.

또한 'zqBasic'생성자와 'changeBase'의 차이점에 대한 대답은 C.A.McAnn이 지적했듯이 'p'가 명시 적 forall과 함께 선언의 범위에 포함되지 않는다는 것입니다. 누구든지 을 설명 할 수있는 이유가 있다면입니다. 감사하겠습니다.