2012-09-04 2 views
12

실제 세계 하스켈이 예제가 있습니다GHC는 클래스 인스턴스가 루프인지 경고 할 수 있습니까?

class BasicEq3 a where 
    isEqual3 :: a -> a -> Bool 
    isEqual3 x y = not (isNotEqual3 x y) 

    isNotEqual3 :: a -> a -> Bool 
    isNotEqual3 x y = not (isEqual3 x y) 

instance BasicEq3 Bool 

을 내가 GHCI에서 실행할 때

#> isEqual3 False False 
out of memory 

을 그래서, 당신은 두 가지 방법 중 하나 이상을 구현해야하거나 루프 것이다. 그리고 당신은 깔끔한 것을 선택할 수있는 융통성을 얻습니다.

내가 갖고있는 질문은 기본 설정을 충분히 오버라이드하지 않았고 기본값이 루프를 형성하면 경고 또는 무언가를 얻을 수있는 방법이 있습니까? 나에게는 이상하게 보입니다.이 예제에서는 매우 똑똑한 컴파일러가 괜찮습니다.

답변

12

나는 GHC가 "끊어짐이없는"순환 종속성의 경우에 경고를내는 것은 완벽하다고 생각합니다. 해당 줄을 따라 표가 있습니다 : http://hackage.haskell.org/trac/ghc/ticket/6028

"결정 불가능한"것이 단지 문제의 인스턴스를 효과적으로 해결할 수 없다는 것을 의미하지는 않습니다. GHC (또는 다른 Haskell 컴파일러)는 이미 필요한 정보를 가지고 있으며 사용자가 순환 종속성을 "위반하지 않고 클래스를 인스턴스화하는 경우에는 경고를 발행 할 수 있습니다. 그리고 이전 게시물에 나온 것처럼 드문 경우 컴파일러에서 오류가 발생하는 경우 사용자는 -nowarnundefinedcyclicmethods 또는 이와 유사한 메커니즘을 사용하여 GHC에 조용하게 알릴 수 있습니다. 거의 모든 경우에 경고가 가장 환영받을 것이며 프로그래머의 생산성을 높일 것입니다. 거의 항상 어리석은 버그를 피해야합니다.

+0

예! 중지 문제의 경우, Agda와 같은 전체 언어는 일반적으로 해결할 수없는 문제가 크고 흥미로운 해결할 수있는 부분 집합을 가질 수 있음을 증명합니다. –

+0

"간단히 말해서 결정할 수없는 것"은 컴파일러 기술을 부인할 수있는 기준이 아닙니다. 일반적인 경우 결정 불가능한 대부분의 문제는 특정 경우에 결정할 수 있습니다. " http://c2.com/cgi/wiki?SufficientlySmartCompiler –

+0

trac 티켓이 말도 안되는 이유로 닫혔습니다 : 누군가가'-' 또는'negate'없이'Num' 인스턴스를 정의 할 수 있지만'+'는 평가하지 않습니다 그것의 명백하게 원형 정의가 잘 끝날 것입니다. 이 always-discarding-second-argument는'-' 또는'negate'를 정의하지 않고 비 종료에서 벗어날 수있는 유일한 방법입니다. 제 생각에는 이런 것들이 유용하다고 생각합니다. 그리고'-noarnundefinedcyclicmethods'를 지정해야 괜찮습니다.'_'을 사용하여 순환 정의를 벗어나고 싶다면. 나는 _no idea_ 어떻게 trac에 인상을 가지고있다. – AndrewC

12

아니요, 저는 GHC가 그러한 일을하지 않을까 걱정됩니다. 또한 가능하지 않습니다 일반적으로.

유형 클래스의 메소드는 유용한 방식으로 상호 재귀적일 수 있습니다. 이러한 유형 클래스의 고안된 예가 있습니다. 기본 구현이 서로의 용어 임에도 불구하고 sumOdds 또는 sumEvens을 정의하지 않는 것이 좋습니다.

class Weird a where 
    measure :: a -> Int 

    sumOdds :: [a] -> Int 
    sumOdds [] = 0 
    sumOdds (_:xs) = sumEvens xs 

    sumEvens :: [a] -> Int 
    sumEvens [] = 0 
    sumEvens (x:xs) = measure x + sumOdds xs 
+0

흥미 롭 군, 나는 결코 생각하지 않았다. –

+0

사실, 많은 표준 클래스가이를 수행합니다 (예 : 'Ord'). 그것이 "최소한의 완전한 정의"가 나오는 곳입니다. –

+2

상호 재귀 적이 있습니까? Corecursive가 다릅니다 – nponeccop

2

저는 그렇게 생각하지 않습니다. 나는 당신이 컴파일러가 멈추는 문제를 해결하기를 기대하고 있다고 걱정한다! 두 함수가 서로 정의되어 있기 때문에 잘못된 기본 클래스라는 의미는 아닙니다. 또한 유용한 기능을 추가하기 위해 instance MyClass MyType을 작성해야했던 과거 수업을 사용했습니다. 그래서 컴파일러에게 클래스에 대해 경고하도록 요청하는 것은 다른 유효한 코드에 대해 불평을 요구하는 것입니다.

[물론 개발 중에 ghci를 사용하고 작성한 후에 모든 기능을 테스트하십시오! 사용 HUnit 및/또는 QuickCheck, 그냥이 물건의 확인 어느 것도 최종 코드의 끝 없는지 확인합니다.]

6

아니,이없는, 컴파일러는이 결정을 할 수 있다면, 그게 해결에 상응하는 것이기 때문에 정지 문제. 일반적으로 두 함수가 "루프"패턴으로 서로를 호출한다는 사실은 실제로 함수 중 하나를 호출하면 루프가 발생한다고 결론을 내리기에는 충분하지 않습니다.

A (인위적인) 예제를 사용하려면,

이제 collatzEvencollatzOdd

collatzOdd :: Int -> Int 
collatzOdd 1 = 1 
collatzOdd n = let n' = 3*n+1 in if n' `mod` 2 == 0 then collatzEven n' 
            else collatzOdd n' 

collatzEven :: Int -> Int 
collatzEven n = let n' = n `div` 2 in if n' `mod` 2 == 0 then collatzEven n' 
             else collatzOdd n' 

collatz :: Int -> Int 
collatz n = if n `mod` 2 == 0 then collatzEven n else collatzOdd n 

(이것은 물론 Collatz conjecture을 구현하지 않는 가장 자연적인 방법이지만, 상호 재귀 함수를 보여줍니다.) 서로에 의존하지만 Collatz 추측에 따르면 collatz은 모두 긍정 n으로 종료됩니다. GHC가 기본 정의로 collatzOddcollatzEven을 가진 클래스가 완전한 정의를 가졌는지 여부를 결정할 수 있다면 GHC는 Collatz 추측을 해결할 수 있습니다! (물론 Halting Problem의 undecideability에 대한 증거는 아니지만, 상호 반복적 인 함수 집합이 잘 정의되어 있는지 여부를 결정하는 것이 왜 그렇게 단순하지는 않은지 설명해야합니다.)

In GHC는 이것을 자동으로 결정할 수 없으므로 Haskell 클래스에 대한 문서는 클래스의 인스턴스를 생성하는 데 필요한 "최소의 완전한 정의"를 제공합니다.

+8

최소한의 완전한 정의를 컴파일러가 검사 할 수있는 방법으로 지정하는 방법이 있다면 좋을 것입니다. – hammar

+0

좋은 지적입니다. 아마도 클래스 정의가있는 컴파일러 pragma 일 것입니다. – AndrewC

2

제 개인적인 견해로는 불이행하는 메커니즘이 불필요하고 현명하지 않습니다.

notEq3FromEq3 :: (a -> a -> Bool) -> (a -> a -> Bool) 
notEq3FromEq3 eq3 = (\x y -> not (eq3 x y)) 

eq3FromNotEq3 :: (a -> a -> Bool) -> (a -> a -> Bool) 
eq3FromNotEq3 ne3 = (\x y -> not (ne3 x y)) 

(사실,이 두 정의는 동일하지만은 일반적으로 진실되지 않을 것) : 클래스 저자 그냥 일반 함수로 기본값을 제공하는 것은 쉬운 것입니다. 그런 다음 인스턴스는 다음과 같이 표시됩니다.

instance BasicEq3 Bool where 
    isEqual3 True True = True 
    isEqual3 False False = True 
    isEqual3 _ _ = False 

    isNotEqual3 = notEq3FromEq3 isEqual3 

기본값은 필요하지 않습니다. 그런 다음 GHC는 정의를 제공하지 않으면 경고 할 수 있으며 불쾌한 루프는 코드에서 명시 적으로 작성해야합니다.

이렇게하면 기존 인스턴스에 영향을주지 않으면 서 기본 정의가있는 클래스에 새 메소드를 추가 할 수있는 깔끔한 기능이 제거되지만 내보기에는 그리 큰 이점이 아닙니다. 위의 방법은 원칙적으로 더욱 유연합니다. Ord 인스턴스에서 을 선택하여 구현할 비교 연산자 인을 허용하는 함수를 제공하십시오.

관련 문제