2013-03-16 2 views
5

저는 스키마를 더 잘 이해하려고하기 위해 파이썬에서 작은 스키마와 비슷한 언어를 작성하려고합니다.scheme의 구문 객체의 용도는 무엇입니까?

문제는 구문 개체에 문제가 있다는 것입니다. 나는 그들이 무엇을 위해 있고 어떻게 작동 하는지를 정말로 이해하지 못하기 때문에 구현할 수 없다.

이들을 이해하기 위해 DrRacket에서 구문 개체로 약간 연주했습니다. #'(+ 2 3)을 평가 내가 찾을 수 있었던 것과

는 최상위 네임 스페이스의 하나를 그림자 어휘 + 변수가있는 경우를 제외하고, '(+ 2 3)을 평가 다르지 않습니다하는 경우 (eval '(+ 2 3)) 여전히 반환 5이지만 (eval #'(+ 2 3))은 오류가 발생합니다. 예를 들어

: (eval (shadow-stx)) 오류가 발생하면서

(define (top-sym) 
    '(+ 2 3)) 
(define (top-stx) 
    #'(+ 2 3)) 
(define (shadow-sym) 
    (define + *) 
    '(+ 2 3)) 
(define (shadow-stx) 
    (define + *) 
    #'(+ 2 3)) 

(eval (top-sym)), (eval (top-stx))(eval (shadow-sym)) 모두가 5 돌아갑니다. 해당 없음6입니다.

더 잘 알지 못한다면 구문 개체에 관한 특별한 것 (더 나은 오류보고를 위해 코드의 위치를 ​​저장한다는 사소한 사실을 제외하고)은 오류가 발생한다고 생각합니다. 심볼 대응자가 잠재적으로 원하지 않는 값을 반환했을 특정 상황.

스토리가 단순하다면 일반 목록 및 기호에 비해 구문 개체를 사용하는 것이 실제로 이점이 없습니다.

내 질문은 : 그래서 나는 그들을 특별하게 만드는 구문 개체에 대해 놓친 무엇입니까?

+0

보셨습니까? http://docs.racket-lang.org/reference/syntax-model.html#%28part._stxobj-model%29 yet? – dyoo

+0

@dyoo 나는 여러 번 읽어 봤는데, 내가 위의 테스트를 시도하기 전까지는 그 점을 이해했다고 생각했다. 그 페이지에서'(eval (shadow-stx))'가'6 '을 반환해야한다고 생각했을 것입니다. – Matt

+2

구문 개체에 대해 물어볼 때 분명히 알 수있는 것은 정말 독특한 라켓 (Racket)의 것입니다. 일반적으로 스키마와 관련이 없습니다. 구문 객체는 라켓의 언어 인프라의 중심에있는 데이터 유형입니다. – dyoo

답변

11

구문 개체는 기본 Racket 컴파일러의 어휘 컨텍스트 저장소입니다. 구체적으로 다음과 같은 프로그램을 입력 할 때 :

#lang racket/base 
(* 3 4) 

컴파일러는 해당 프로그램의 전체 내용을 나타내는 구문 객체를 수신합니다. 프로그램의 *thingy 내에서 구문 개체로 컴파일 시간 표현을 가지고

#lang racket/base 

(define example-program 
    (open-input-string 
    " 
    #lang racket/base 
    (* 3 4) 
    ")) 

(read-accept-reader #t) 
(define thingy (read-syntax 'the-test-program example-program)) 
(print thingy) (newline) 
(syntax? thingy) 

참고 : 여기에 우리가 그 구문 객체가 어떻게 생겼는지 볼 수 있도록 예입니다. 그리고 현재 thingy에있는 *은 그것이 어디에서 왔는지 알지 못합니다. 아직 바인딩 정보가 없습니다. 컴파일 중 확장 중에의 컴파일러가 **#lang racket/base에 대한 참조로 연결한다는 것입니다.

컴파일 할 때 상호 작용하면 더 쉽게 알 수 있습니다. (참고 : 나는 컴파일 타임과 런타임 중 일어나는 일에 대한 토론을 피하고 싶기 때문에 고의적으로 eval에 대한 이야기를 피하고있다.

#lang racket/base 
(require (for-syntax racket/base)) 

;; This macro is only meant to let us see what the compiler is dealing with 
;; at compile time. 

(define-syntax (at-compile-time stx) 
    (syntax-case stx() 
    [(_ expr) 
    (let() 
     (define the-expr #'expr) 
     (printf "I see the expression is: ~s\n" the-expr) 

     ;; Ultimately, as a macro, we must return back a rewrite of 
     ;; the input. Let's just return the expr: 
     the-expr)])) 


(at-compile-time (* 3 4)) 

우리는 우리가 컴파일하는 동안 사물의 상태를 검사 할 수 있도록, 매크로, 여기 at-compile-time 사용합니다 :) 여기

은 우리가 이러한 구문 객체가하는 일의 이상을 검사 할 수있는 예입니다. DrRacket에서이 프로그램을 실행하면 DrRacket이 먼저 프로그램을 컴파일 한 다음 실행한다는 것을 알 수 있습니다. 프로그램을 컴파일 할 때 at-compile-time의 사용을 볼 때 컴파일러가 매크로를 호출합니다. 의 프로그램을 조금 수정하자

I see the expression is: #<syntax:20:17 (* 3 4)> 

, 우리는 식별자의 identifier-binding 검사 할 수 있는지 :

그래서 컴파일시에, 우리는 같은 것을 볼 수 있습니다

#lang racket/base 
(require (for-syntax racket/base)) 

(define-syntax (at-compile-time stx) 
    (syntax-case stx() 
    [(_ expr) 
    (let() 
     (define the-expr #'expr) 
     (printf "I see the expression is: ~s\n" the-expr) 
     (when (identifier? the-expr) 
     (printf "The identifier binding is: ~s\n" (identifier-binding the-expr))) 

     the-expr)])) 


((at-compile-time *) 3 4) 

(let ([* +]) 
    ((at-compile-time *) 3 4)) 

을 DrRacket에서이 프로그램을 실행하면 다음 출력이 표시됩니다.

I see the expression is: #<syntax:21:18 *> 
The identifier binding is: (#<module-path-index> * #<module-path-index> * 0 0 0) 
I see the expression is: #<syntax:24:20 *> 
The identifier binding is: lexical 
12 
7 

(덧붙여서 : at-compile-time의 출력을 왜 앞에서 볼 수 있습니까? 컴파일은 런타임 전에 완전히 완료 되었기 때문에! 우리가 경우 프로그램을 미리 컴파일하고 raco make를 사용하여 바이트 코드를 저장, 우리가 컴파일러는 우리가 프로그램을 실행할 때 호출되는 볼 것입니다.) 컴파일러가 at-compile-time의 사용에 도달하는 시간으로

, 그것은에 알고 적절한 어휘 바인딩 정보를 식별자에 연결하십시오. 첫 번째 경우에 identifier-binding을 검사하면 컴파일러는 해당 모듈이 특정 모듈 (이 경우 #lang racket/base)과 연결되어 있다는 것을 알고 있습니다. 이는 module-path-index 사업에 관한 것입니다. 그러나 두 번째 경우에는 어휘가 바인딩임을 알 수 있습니다. 컴파일러는 이미 (let ([* +]) ...)을 통과 했으므로 *의 사용이 let으로 설정된 바인딩을 다시 참조합니다.

라켓 컴파일러는 구문 개체를 사용하여 이러한 종류의 바인딩 정보를 클라이언트와 같은 매크로에 전달합니다. 구문 객체의 바인딩 정보는 시간에 우리는 구문 객체를 평가하기 때문에, 자신의 바인딩이 일을 참조 수도 관련되지 않을 수 있습니다


물건의 종류의 문제에 고민입니다 검사 eval를 사용하려고 존재하지 않는! 그게 근본적으로 당신이 실험에서 실수를 본 이유입니다. 이 예 신중 문법 객체의 내용만을 위해 모듈 결합 된 것을 참조하도록 구성된다

#lang racket/base 

(module mod1 racket/base 
    (provide x) 
    (define x #'(* 3 4))) 

(module mod2 racket/base 
    (define * +) ;; Override! 
    (provide x) 
    (define x #'(* 3 4))) 

;;;;;;;;;;;;;;;;;;;;;;;;;;; 

(require (prefix-in m1: (submod "." mod1)) 
     (prefix-in m2: (submod "." mod2))) 

(displayln m1:x) 
(displayln (syntax->datum m1:x)) 
(eval m1:x) 

(displayln m2:x) 
(displayln (syntax->datum m2:x)) 
(eval m2:x) 

:

여전히 여기에 S-식과 구문 개체 사이의 차이를 도시 한 예이다 우리는 eval을 사용할 당시 존재할 것입니다. 우리는 약간의 예제를 변경한다면

(module broken-mod2 racket/base 
    (provide x) 
    (define x 
    (let ([* +]) 
     #'(* 3 4)))) 
우리가 eval broken-mod2 나오는 x하려고 할 때 구문 객체에 의해 존재하지 않는 어휘 바인딩을 참조하기 때문에

는 일들이 끔찍하게 휴식 우리는 eval 시간. eval은 어려운 짐승입니다.

+0

덧붙여 Matthew Flatt는 Clojure/West 2013에서이 주제에 관해 구체적으로 이야기 할 것입니다. 그의 이야기 http://clojurewest.org/sessions#flatt는 명확히하는 데 도움이되는 예제가 더 많이 있습니다. – dyoo

+0

감사! 대화가 온라인으로 볼 수 있습니까? – Matt

+0

@Matt : 나는 그렇게 생각하지만, 나는 절대적으로 확신하지는 않는다. Clojure/West 2012는 InfoQ http://clojurewest.org/news/2012/5/11/clojurewest-video-schedule.html에서 주최하는 비디오 아카이브를 보유하고 있으므로 올해도 동일한 작업을 수행 할 것으로 기대됩니다. – dyoo

관련 문제