2016-06-10 3 views
1

parsec을 사용하여 일부 소스 코드를 AST로 구문 분석합니다. 나는 최근에 "의심스러운"코드를 잡으려고 -Wall-W 옵션을 사용했고 명시적인 확장자가없는 the parsec-related top-level functions in this file에 대해 불평하고 있습니다.parsec 유형 주석 이해

예 1

vimL = choice [ block 
       , statement 
       ] 

유추 유형은 여기에 있습니다 :

vimL :: ParsecT String() Data.Functor.Identity.Identity Node 

그래서, 그 주석을 추가 할 경우, 컴파일러는 내가 import에 의미있는 Data.Functor.Identity.Identity 약 있지 않는 액세스를 불평 그것 :

import Data.Functor.Identity 

그리고 만약 그렇게한다면, 나는 할 수있다. 형식 주석을 다음과 같이 수정하십시오.

vimL :: ParsecT String() Identity Node 

및 컴파일러는 계속 허용합니다. 그러나 그것은 여전히 ​​내가 아주 깊이 이해하고있는 것이 아닙니다.

link :: forall u. 
     ParsecT String u Data.Functor.Identity.Identity Node 

그러나 나는 또한 사용하지 않는 것을 사용할 수 없습니다 :

예 2

link = Link <$> (bar *> linkText <* bar) 
    where 
    bar  = char '|' 
    linkText = many1 $ noneOf " \t\n|" 

유추 유형은 여기

{-# LANGUAGE RankNTypes #-} 

주 나는 분배 할 수 그걸로 내가 forall을 떨어 뜨리면. 이러한 작업을 모두 :

link :: ParsecT String u Data.Functor.Identity.Identity Node 
link :: ParsecT String u Identity Node 

예 3

string' s = mapM_ char' s >> pure s <?> s 

이 하나의 추론 유형은 다음과 같습니다

{-# LANGUAGE RankNTypes #-} 
{-# LANGUAGE KindSignatures #-} 
:

는 하나를 사용하기 위해
string' :: forall s u (m :: * -> *). 
      Stream s m Char => 
      [Char] -> ParsecT s u m [Char] 

, 나는 모두 필요

다시 말해서, 내가,583,210 나는 다음과 컴파일러가 아직도 그것을 수용에 유형을 단순화 할 수 있습니다 :

string' :: Stream s m Char => [Char] -> ParsecT s u m [Char] 

매우 간단하지 않음을하지만. 더 나아가와 constaint을 삭제 :

{-# LANGUAGE NoMonomorphismRestriction #-} 

그에서 저를 얻을 수 있지만 그렇지 않습니다 :

No instance for (Stream s m Char) arising from a use of ‘char'’ 

내가 생각 :

string' :: [Char] -> ParsecT s u m [Char] 

내가 얻을.

질문

대부분이 내 머리 위로 갈이, 그래서 맹목적으로 첫번째 좀 더 통찰력을 확보하지 않고있는 추론 유형 서명을 복사하여 붙여 넣을하지 않습니다. 아무도 이러한 의미가 무엇인지 밝혀 낼 수 있습니까? parsec 무거운 코드에 주석을 달 수있는 모범 사례는 무엇입니까? forall은 컴파일러 오류가 발생하지 않고 생략 될 수있는 경우 무엇을합니까? 그리고 가능한 모든 별칭 트릭이 있는지 여부 이들을 더 읽기 쉽도록 만드시겠습니까?

답변

2

나는 parsec에 전문가는 아니지만, 그래서 나는 그들이 유형을 설명하려면 다른 사람이 무거운하자 하겠지만, 여기에 몇 가지 생각입니다

일반적으로 더 친숙한 유형의 동의어를 내보내려고 패키지 . 이 경우, 당신은

type Parsec s u = ParsecT s u Identity -- in Text.Parsec.Prim 
type Parser = Parsec String()   -- in Text.Parsec.String 

을 사용할 수 있습니다 그래서 더 의미가해야 할, 당신 vimL :: Parser Node 도착 - 그것은 Node을 생산하는 String에서 실행할 수있는 파서입니다.

forall이 문맥에서는 매우 적습니다. 사용 가능한 친숙한 동의어가있는 이유입니다. 그러나 자신의 용기로 parsec이 더 높은 순위 유형을 많이 사용한다는 것을 내기하고자합니다. forall 없이는 표현할 수 없으므로 GHC 서명은 명시 적으로 forall입니다.

은 (간단히, forall x. <something-with-x><some-thing-with-x>과 동일하지만 서명의 중간에 forall이있는 경우, 상황이 많이 지저분 얻을.) parsec (from the documentation)에

편집

어떤 물건을. 유형 ParsecT s u m a은 가능한 가장 일반적인 구문 분석기를 나타냅니다. the source의 의견을 읽으면 도움이됩니다.

  • 은 스트림 유형을 설명합니다. 추상적 인 의미의 파서는 일련의 기호를 가져 와서 구조화 된 출력 형식으로 변환합니다.
  • a은 출력 양식의 유형입니다.
  • u은 사용자 상태 유형입니다. parsec은 이미 상태 정보 (예 : 의미있는 구문 분석 오류 메시지를 제공 할 수 있도록 텍스트의 위치와 같은)를 추적하므로 사용자 패키지를 들고 다니기를 원하는 상태로 사용자 패키지를 보냅니다. 이것의 안에서 2.12 Advanced: User State)
  • m는 물건이 달리고있는 기초 모나드이다. 나는

그런 다음, 몇 가지 특별한 경우가 발생 ... 당신이 모나드를 grok 수 IFF에이 부분이 분명있을 것입니다 생각 :

  • m = Identity 우리가 실행의 모나드 문맥을 필요로하지 않는다는 것을 의미 촬영. Parsec s u a 동의어는이 경우에 해당합니다.
  • u =()은 상태 정보를 보유 할 필요가 없음을 의미합니다.
  • s = String은 입력 (스트림)이 문자열임을 의미합니다.

마지막 string' :: forall s u (m :: * -> *). Stream s m Char => [Char] -> ParsecT s u m [Char] 출력이 String = [Char] 것을 의미한다 (전술 한 다른 두 옵션과 결합, 즉. Parser a 형 동의어가되는 것이다), 상기 사용자 상태는 모나드 컨텍스트 및 입력 무엇이든 될 수있다 - 그들이 어떤 조건을 만족한다면, 따라서 Stream s m Char 제약.

해당 제약 Stream s m t은 스트림 입력 유형 sm (Maybe (t,s))으로 "펼칠"수 있어야한다는 것을 의미합니다. m 부분은이 펼쳐짐이 모나드 컨텍스트에서 발생할 수 있음을 의미합니다. Maybe 부분은 입력이있는 동안에 만 펼칠 수 있다는 사실을 처리합니다. t은 스트림 앞쪽에서 벗어나는 토큰이며 s은 스트림의 나머지 마지막으로 스트림 s의 유형은 토큰 t의 유형을 고유하게 식별해야하므로 기능 종속성 s -> t이 있습니다.

+0

매우 도움이됩니다. 'string ':: forall s u (m :: * -> *). 문자열 Smars Char => [Char] -> ParsecT sum [Char]'는'string ':: String -> Parser String'이되었고, 나머지 대부분은'Parser Node','Parser String' 또는'Parser()'가되었습니다. . – wincent