제약 조건 추적 기법


322

시나리오는 다음과 같습니다. 유형 서명으로 일부 코드를 작성했으며 GHC는 일부 x및에 대해 x ~ y를 추론 할 수 없다고 불평 y합니다. 일반적으로 GHC에 뼈를 던지고 단순히 함수 제약 조건에 동형을 추가 할 수는 있지만 몇 가지 이유로 나쁜 생각입니다.

  1. 코드 이해를 강조하지 않습니다.
  2. 하나가 충분할 경우 5 개의 제약 조건으로 끝날 수 있습니다 (예 : 5가 하나 이상의 특정 제약 조건에 의해 암시되는 경우)
  3. 뭔가 잘못했거나 GHC가 도움이되지 않으면 가짜 제약 조건이 생길 수 있습니다

방금 사례 3과 싸우는 데 몇 시간을 보냈습니다.에 대해 놀고 있으며 syntactic-2.0에 정의 된 버전과 share유사한 도메인 독립적 버전을 정의하려고 했습니다 NanoFeldspar.hs.

나는 이것을 가지고 있었다 :

{-# LANGUAGE GADTs, FlexibleContexts, TypeOperators #-}
import Data.Syntactic

-- Based on NanoFeldspar.hs
data Let a where
    Let :: Let (a :-> (a -> b) :-> Full b)

share :: (Let :<: sup,
          Domain a ~ sup,
          Domain b ~ sup,
          SyntacticN (a -> (a -> b) -> b) fi) 
      => a -> (a -> b) -> a
share = sugarSym Let

그리고 GHC could not deduce (Internal a) ~ (Internal b), 확실히 내가 가고있는 것이 아닙니다. 그래서 내가 의도하지 않은 코드를 작성했거나 (제약이 필요함) GHC는 내가 작성한 다른 제약으로 인해 그 제약을 원했습니다.

(Syntactic a, Syntactic b, Syntactic (a->b))제약 목록 에 추가해야한다는 것이 밝혀졌습니다 (Internal a) ~ (Internal b). 나는 기본적으로 올바른 제약 조건을 우연히 발견했습니다. 나는 여전히 체계적인 방법으로 찾을 수 없습니다.

내 질문은 :

  1. GHC가 그 제약을 제안한 이유는 무엇입니까? 구문상의 어느 것도 제약 Internal a ~ Internal b이 없으므로 GHC는 어디서 그것을 끌어 냈습니까?
  2. 일반적으로 GHC가 필요하다고 생각하는 제약의 기원을 추적하기 위해 어떤 기술을 사용할 수 있습니까? 내가 발견 할 수있는 제약 조건조차도 내 접근 방식은 본질적으로 재귀 제약 조건을 실제로 작성하여 문제의 경로를 강요하는 것입니다. 이 접근법은 기본적으로 무한한 토끼 구멍을 뚫고 나가는 가장 효율적인 방법에 관한 것입니다.

21
타입 레벨 디버거에 대한 논의가 있었지만 일반적인 컨센서스는 타입 체커의 내부 로직이 도움이되지 않는 것으로 보입니다 : / 현재 Haskell의 제약 솔버는 엉터리 불투명 로직 언어입니다 :)
Daniel Gratzer

12
@jozefg 그 토론에 대한 링크가 있습니까?
crockeea

36
종종 타입 시그니처를 완전히 제거하고 ghci가 시그니처의 생각을 알려주는 데 도움이됩니다.
Tobias Brandt

12
어떻게 든 ab바인딩 - 사용자의 컨텍스트의 유형 서명 외부에서 봐 - a -> (a -> b) -> a하지 a -> (a -> b) -> b. 어쩌면 그게 다야? 구속 조건 솔버를 사용하면 어디에서나 전이 평등에 영향을 줄 수 있지만 오류는 일반적으로 구속 조건이 유발 된 위치와 "가까운"위치를 나타냅니다. @jozefg를 통해 멋질 것입니다. 태그 또는 무언가로 제약 조건에 주석을 달아 어디에서 왔는지 보여줄 수 있습니까? : s
Athan Clark

답변:


6

우선, 함수의 유형이 잘못되었습니다. 나는 그것이 (컨텍스트없이) 있어야한다고 확신합니다 a -> (a -> b) -> b. GHC 7.10은 원래 코드를 사용하면 제약 조건 누락에 대해 불평하기 때문에 그 점을 지적하는 데 다소 도움이됩니다 Internal (a -> b) ~ (Internal a -> Internal a). share의 유형을 수정 한 후 GHC 7.10은 다음과 같이 안내합니다.

  1. Could not deduce (Internal (a -> b) ~ (Internal a -> Internal b))

  2. 위의 내용을 추가하면 Could not deduce (sup ~ Domain (a -> b))

  3. 그를 추가 한 후, 우리는 수 Could not deduce (Syntactic a), Could not deduce (Syntactic b)Could not deduce (Syntactic (a -> b))

  4. 이 세 가지를 추가 한 후에는 마지막으로 형식 검사를 수행합니다. 그래서 우리는 결국

    share :: (Let :<: sup,
              Domain a ~ sup,
              Domain b ~ sup,
              Domain (a -> b) ~ sup,
              Internal (a -> b) ~ (Internal a -> Internal b),
              Syntactic a, Syntactic b, Syntactic (a -> b),
              SyntacticN (a -> (a -> b) -> b) fi)
          => a -> (a -> b) -> b
    share = sugarSym Let
    

그래서 저는 GHC가 우리를 이끄는 데 쓸모가 없다고 말하고 싶습니다.

GHC로부터의 제약 조건을 가져 추적에 대한 질문에 관해서는, 당신은 시도 할 수 GHC의 디버깅 플래그를 특히, -ddump-tc-trace다음 위치를 확인하기 위해 결과 로그를 읽어 Internal (a -> b) ~ t(Internal a -> Internal a) ~ t받는 추가 Wanted세트,하지만 꽤 오랜 읽을 수 있습니다 .


0

GHC 8.8 이상에서 시도 했습니까?

share :: (Let :<: sup,
          Domain a ~ sup,
          Domain b ~ sup,
          SyntacticN (a -> (a -> b) -> b) fi,
          _) 
      => a -> (a -> b) -> a
share = sugarSym Let

핵심은 제약 조건 중 유형 구멍을 사용하는 것입니다. _ => your difficult type

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.