수색…


비고

템플릿 하스켈이란 무엇입니까?

템플릿 하스켈은 GHC 하스켈에 내장 된 템플릿 메타 프로그래밍 기능을 가리킨다. 원래 구현을 설명하는 문서는 여기 에서 찾을 수 있습니다 .

무대는 무엇입니까? (또는 무대 제한이란 무엇입니까?)

단계는 코드가 실행될 참조됩니다. 보통 코드는 런타임에만 실행되지만 Template Haskell을 사용하면 컴파일 타임에 코드를 실행할 수 있습니다. "일반"코드는 0 단계이고 컴파일 타임 코드는 1 단계입니다.

스테이지 제한은 스테이지 0 프로그램이 스테이지 1에서 실행되지 않을 수 있다는 것을 의미합니다. 이것은 컴파일 타임에 일반 프로그램 (메타 프로그램뿐만 아니라)을 실행할 수있는 것과 같습니다.

규칙에 따라 (그리고 구현의 간결성을 위해) 현재 모듈의 코드는 항상 스테이지 0이며 다른 모든 모듈에서 가져온 코드는 스테이지 1입니다. 이러한 이유로 인해 다른 모듈의 표현식 만 연결될 수 있습니다.

1 단계 프로그램은 Q Exp , Q Type 등의 스테이지 0 표현입니다. 그러나 그 반대는 사실이 아닙니다. 유형 Q Exp 의 모든 값 (스테이지 0 프로그램)이 1 단계 프로그램이 아니라,

게다가 스플 라이스가 중첩 될 수 있기 때문에 식별자는 1보다 큰 스테이지를 가질 수 있습니다. 스테이지 제한은 일반화 될 수 있습니다. 스테이지 n 프로그램은 m> n 단계에서 실행되지 않을 수 있습니다. 예를 들어 특정 오류 메시지에서 1보다 큰 단계에 대한 참조를 볼 수 있습니다.

>:t [| \x -> $x |]

<interactive>:1:10: error:
    * Stage error: `x' is bound at stage 2 but used at stage 1
    * In the untyped splice: $x
      In the Template Haskell quotation [| \ x -> $x |]

Template Haskell을 사용하면 비 관련 식별자의 스코프 범위 오류가 발생하지 않습니까?

일반적으로, 하나의 Haskell 모듈에있는 모든 선언은 모두가 서로 재귀 적으로 간주 될 수 있습니다. 즉, 모든 최상위 선언은 단일 모듈에서 다른 모든 범위에 있습니다. Template Haskell이 활성화되면 범위 지정 규칙이 변경됩니다. 대신 모듈이 TH 스플 라이스에 의해 분리 된 코드 그룹으로 분리되며 각 그룹은 상호 재귀 적이며 모든 그룹은 모든 추가 그룹의 범위에 있습니다.

Q 타입

Language.Haskell.TH.Syntax 로 정의되고있는 Q :: * -> * 형태의 constructor은, 계산을 실행하는 모듈의 컴파일시의 환경에 액세스 가능한 계산을 나타내는 abstract 형입니다. Q 타입은 또한 TH에 의한 이름 캡쳐 라고 불리는 변수 대체를 처리합니다 ( 여기에서 설명합니다). 모든 스플 라이스는 일부 X 대해 QX 유형을 갖습니다.

컴파일 타임 환경에는 다음이 포함됩니다.

  • 범위 내 식별자 및 상기 식별자에 관한 정보,
    • 함수 유형
    • 생성자의 유형 및 소스 데이터 유형
    • 타입 선언 (클래스, 타입 패밀리)의 완전한 명세
  • 스플 라이스가 발생하는 소스 코드의 위치 (선, 열, 모듈, 패키지)
  • 기능 고정 (GHC 7.10)
  • 지원되는 GHC 확장 (GHC 8.0)

Q 타입은 또한 newName :: String -> Q Name 함수로 새로운 이름을 생성 할 수 있습니다. 이름은 암시 적으로 아무 곳에도 바인딩되지 않으므로 사용자가 직접 바인딩해야하므로 결과적으로 이름의 사용 범위가 올바른지 확인하는 것은 사용자의 책임입니다.

QFunctor,Monad,Applicative 대한 인스턴스를 가지고 있으며 이것은 Language.Haskell.TH.Lib 에서 제공되는 결합 자와 함께 Q 값을 조작하기위한 기본 인터페이스입니다. 형식의 TH ast의 모든 생성자에 대한 도우미 함수를 정의합니다.

LitE :: Lit -> Exp
litE :: Lit -> ExpQ

AppE :: Exp -> Exp -> Exp 
appE :: ExpQ -> ExpQ -> ExpQ

ExpQ , TypeQ , DecsQPatQ 는 일반적으로 Q 유형 내에 저장된 AST 유형의 동의어입니다.

TH 라이브러리는 runQ :: Quasi m => Q a -> ma 함수를 제공하며 인스턴스 Quasi IO 가 있으므로 Q 유형은 단순한 IO 입니다. 그러나 runQ :: Q a -> IO a 를 사용하면 컴파일 타임 환경에 액세스 할 수 없는 IO 작업이 생성됩니다. 이는 실제 Q 유형에서만 사용할 수 있습니다. 이러한 IO 작업은 해당 환경에 액세스하려고 시도하면 런타임에 실패합니다.

알기스 카레

친숙한

curry :: ((a,b) -> c) -> a -> b -> c
curry = \f a b -> f (a,b)

함수는 임의의 속성의 튜플로 일반화 할 수 있습니다. 예를 들면 다음과 같습니다.

curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
curry4 :: ((a, b, c, d) -> e) -> a -> b -> c -> d -> e 

그러나 2에서 (예 :) 20 개의 튜플에 대한 이러한 함수를 손으로 작성하는 것은 지루할 수 있습니다 (프로그램에서 20 개의 튜플이 존재한다는 사실은 레코드로 수정해야하는 디자인 문제를 거의 확실하게 나타냄).

Template Haskell을 사용하여 임의의 n 대해 이러한 curryN 함수를 생성 할 수 있습니다.

{-# LANGUAGE TemplateHaskell #-}
import Control.Monad (replicateM) 
import Language.Haskell.TH (ExpQ, newName, Exp(..), Pat(..))
import Numeric.Natural (Natural) 

curryN :: Natural -> Q Exp

curryN 함수는 자연수를 취해 하스켈 대서양 표준시 (Haskell AST)처럼 그 curryN 의 카레 함수를 생성합니다.

curryN n = do
  f  <- newName "f"
  xs <- replicateM (fromIntegral n) (newName "x")

먼저 함수의 각 인수에 대해 새로운 유형 변수를 생성합니다. 하나는 입력 함수 용이고 다른 하나는 함수에 대한 인수 용입니다.

  let args = map VarP (f:xs)

표현식 args 는 패턴 f x1 x2 .. xn 나타냅니다. 패턴은 별도의 문법적 실체입니다. 우리는이 동일한 패턴을 가져 와서 람다, 함수 바인딩 또는 let 바인딩의 LHS (오류가 될 수 있음)에 배치 할 수 있습니다.

      ntup = TupE (map VarE xs)

함수는 인수의 시퀀스에서 인수 튜플을 작성해야합니다. 이는 우리가 여기에서 한 것입니다. 패턴 변수 ( VarP )와 표현식 변수 ( VarE )를 구분하십시오.

  return $ LamE args (AppE (VarE f) ntup)

마지막으로 우리가 생산하는 값은 AST \f x1 x2 .. xn -> f (x1, x2, .. , xn) 입니다.

인용 부호와 '해제 된'생성자를 사용하여이 함수를 작성할 수도 있습니다.

...
import Language.Haskell.TH.Lib  

curryN' :: Natural -> ExpQ
curryN' n = do
  f  <- newName "f"
  xs <- replicateM (fromIntegral n) (newName "x")
  lamE (map varP (f:xs)) 
        [| $(varE f) $(tupE (map varE xs)) |]

인용문은 구문 적으로 유효해야하므로 [| \ $(map varP (f:xs)) -> .. |] Haskell에서 패턴의 'list'를 선언 할 방법이 없기 때문에 [| \ $(map varP (f:xs)) -> .. |] 는 유효하지 않습니다. 위의 것은 \ var -> .. 로 해석됩니다 \ var -> .. 스플 라이스 된 표현식은 PatQ 유형, 즉 패턴 목록이 아닌 단일 패턴을 가질 것으로 예상됩니다.

마지막으로 GHCi에서 TH 함수를로드 할 수 있습니다.

>:set -XTemplateHaskell
>:t $(curryN 5)
$(curryN 5)
  :: ((t1, t2, t3, t4, t5) -> t) -> t1 -> t2 -> t3 -> t4 -> t5 -> t
>$(curryN 5) (\(a,b,c,d,e) -> a+b+c+d+e) 1 2 3 4 5
15

이 예제는 주로 여기 에서 적용됩니다.

Template Haskell과 Quasiquotes의 문법

템플릿 하스켈은 -XTemplateHaskell GHC 확장으로 가능합니다. 이 확장을 통해이 절에서 자세히 설명하는 모든 구문 기능을 사용할 수 있습니다. Template Haskell에 대한 자세한 내용은 사용자 가이드에서 제공 합니다.

스플 라이스

  • 스플 라이스는 $(...) 로 쓰여진 Template Haskell에 의해 활성화 된 새로운 구문 엔티티입니다. 여기서 (...) 는 몇 가지 표현식입니다.

  • $ 와 표현식의 첫 번째 문자 사이에는 공백이 없어야합니다. Template Haskell은 $ 연산자의 파싱을 오버라이드합니다. 예를 들어, f$g($) fg 로 파싱됩니다. 반면 Template Haskell을 사용하면 스플 라이스로 파싱됩니다.

  • 스플 라이스가 최상위 레벨에 나타나면 $ 생략 할 수 있습니다. 이 경우 스플 라이스 된 표현식이 전체 행입니다.

  • splice는 컴파일 타임에 Haskell AST를 생성하기 위해 실행되는 코드를 나타내며 AST는 Haskell 코드로 컴파일되어 프로그램에 삽입됩니다.

  • 표현식, 패턴, 유형 및 최상위 레벨 선언 대신 스플 라이스가 나타날 수 있습니다. 접합 된 ​​표현식의 유형은 각각 각각 Q Exp , Q Pat , Q Type , Q [Decl] 입니다. 선언 스플 라이스는 최상위 레벨 에만 표시 될 수 있지만 다른 것들은 다른 표현식, 패턴 또는 유형 안에있을 수 있습니다.

표현 견적 (참고 :이 아닌 QuasiQuotation)

  • 표현식 인용은 다음 중 하나로 작성된 새로운 구문 엔티티입니다.

    • [e|..|] 또는 [|..|] - .. 은 표현식이고 인용문 유형은 Q Exp .
    • [p|..|] - .. 는 패턴이고 따옴표에는 유형 Q Pat .
    • [t|..|] - .. 는 유형이고 인용문은 Q Type .
    • [d|..|] - .. 는 선언 목록이며 견적 유형은 Q [Dec] 입니다.
  • 표현식 인용 부호는 컴파일 타임 프로그램을 사용하고 해당 프로그램이 나타내는 AST를 생성합니다.

  • 스플 라이스없이 따옴표 (예 : \x -> [| x |] )에 값을 사용하면 \x -> [| $(lift x) |] , 여기서 lift :: Lift t => t -> Q Exp 는 클래스에서옵니다.

    class Lift t where
      lift :: t -> Q Exp
      default lift :: Data t => t -> Q Exp

입력 된 스플 라이스 및 인용문

  • 유형이 지정된 스플 라이스는 이전에 언급 된 (유형이 지정되지 않은) 스플 라이스와 유사하며 $$(..) 로 작성됩니다 $$(..) 여기서 (..) 은 표현식입니다.

  • e 가 유형 Q (TExp a) 이면 $$e 는 유형 a Q (TExp a) .

  • 형식화 된 인용문은 [||..||] 형식을 취합니다 [||..||] 여기서 .. 는 유형 a 의 표현식입니다. 결과 견적에는 유형 Q (TExp a) 있습니다.

  • 타입이 지정된 표현식은 타입이 지정되지 않은 표현식으로 변환 될 수 있습니다 : unType :: TExp a -> Exp .

준 인용문

  • QuasiQuotes는 표현 인용을 일반화합니다 - 이전에는 인용 인용에 사용 된 구문 분석기는 고정 세트 ( e,p,t,d ) 중 하나이지만 QuasiQuotes는 사용자 정의 구문 분석기를 정의하여 컴파일시 코드를 생성 할 수있게합니다. 유사 인용문은 정규 인용문과 동일한 모든 문맥에 나타날 수 있습니다.

  • 준 인용문은 [iden|...|] 로 작성되며, 여기서 idenLanguage.Haskell.TH.Quote.QuasiQuoter 유형의 식별자입니다.

  • QuasiQuoter 는 인용문이 나타날 수있는 서로 다른 컨텍스트마다 하나씩 네 개의 파서로 구성됩니다.

    data QuasiQuoter = QuasiQuoter { quoteExp  :: String -> Q Exp,
                                     quotePat  :: String -> Q Pat,
                                     quoteType :: String -> Q Type,
                                     quoteDec  :: String -> Q [Dec] }

이름

  • 하스켈 식별자는 Language.Haskell.TH.Syntax.Name 유형으로 표현됩니다. 이름은 Template Haskell에서 Haskell 프로그램을 나타내는 추상 구문 트리의 잎을 형성합니다.

  • 현재 범위에있는 식별자는 'e 또는 'T 와 함께 이름으로 바뀔 수 있습니다. 첫 번째 경우에서 e 는 표현 범위에서 해석되고 두 번째 경우에는 T 가 유형 범위에 있습니다 (유형 및 값 생성자가 하스켈에서 모호하지 않고 이름을 공유 할 수 있음을 상기 함).



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow