언어를 호모 닉으로 만드는 방법


16

이 기사 에 따르면 다음 라인의 Lisp 코드는 "Hello world"를 표준 출력으로 인쇄합니다.

(format t "hello, world")

호모 닉 언어 인 Lisp는 다음 과 같이 코드를 데이터로 처리 할 수 ​​있습니다.

이제 다음 매크로를 작성했다고 상상해보십시오.

(defmacro backwards (expr) (reverse expr))

뒤로는 매크로의 이름으로, 표현식 (목록으로 표시)을 가져 와서 반대로합니다. 다음은 "Hello, world"입니다. 이번에는 매크로를 사용합니다.

(backwards ("hello, world" t format))

Lisp 컴파일러는 해당 코드 줄을 볼 때 목록의 첫 번째 아톰 ( backwards)을보고 매크로 이름을 알 수 있습니다. 평가되지 않은 목록 ("hello, world" t format)을 매크로로 전달하여 매크로를 목록으로 재정렬합니다 (format t "hello, world"). 결과 목록은 매크로 표현식을 대체하며 런타임시 평가됩니다. Lisp 환경은 첫 번째 원자 ( format)가 함수 임을 확인 하고 평가하여 나머지 인수를 전달합니다.

Lisp에서는 코드가 목록 ( s-expressions ?)으로 구현 되므로이 작업을 쉽게 수행 할 수 있습니다 (잘못된 경우 수정하십시오 ).

이제이 OCaml (동성 어가 아닌) 스 니펫을 살펴보십시오.

let print () =
    let message = "Hello world" in
    print_endline message
;;

Lisp에 비해 훨씬 더 복잡한 구문을 사용하는 OCaml에 동종 성을 추가한다고 가정합니다. 어떻게 하시겠습니까? 동 음성을 달성하기 위해 언어에 특히 쉬운 구문이 있어야합니까?

편집 : 이 주제 에서 Lisp와는 다른 호모 닉을 달성 하는 또 다른 방법을 찾았습니다 .io 언어로 구현 된 방법 . 이 질문에 부분적으로 대답 할 수 있습니다.

간단한 블록부터 시작해 봅시다 :

Io> plus := block(a, b, a + b)
==> method(a, b, 
        a + b
    )
Io> plus call(2, 3)
==> 5

좋아요, 블록이 작동합니다. 더하기 블록에는 두 개의 숫자가 추가되었습니다.

이제이 작은 친구에 대해 약간의 성찰을 해 봅시다.

Io> plus argumentNames
==> list("a", "b")
Io> plus code
==> block(a, b, a +(b))
Io> plus message name
==> a
Io> plus message next
==> +(b)
Io> plus message next name
==> +

뜨거운 거룩한 차가운 금형. 블록 매개 변수의 이름을 얻을 수있을뿐만 아니라 그리고 블록의 완전한 소스 코드의 문자열을 얻을 수있을뿐만 아니라 코드에 몰래 들어가서 메시지를 탐색 할 수 있습니다. 그리고 가장 놀라운 것은 : 그것은 매우 쉽고 자연 스럽습니다. 이오의 탐구에 충실하다. 루비의 거울은 그 어떤 것도 볼 수 없습니다.

그러나 우와 우와 지금은 그 다이얼을 만지지 마십시오.

Io> plus message next setName("-")
==> -(b)
Io> plus
==> method(a, b, 
        a - b
    )
Io> plus call(2, 3)
==> -1


1
: @Bergi 스칼라 매크로에 대한 새로운 접근 방식이 scala.meta을 .
Martin Berger

나는 항상 호모 닉성이 과대 평가되었지만. 충분히 강력한 언어에서는 항상 언어 자체의 구조를 반영하는 트리 구조를 정의 할 수 있으며, 필요에 따라 소스 언어 (및 / 또는 컴파일 된 형식)와의 변환을 위해 유틸리티 함수를 작성할 수 있습니다. 예, LISP에서는 약간 더 쉬우나 (a) 대부분의 프로그래밍 작업은 메타 프로그래밍이되어서는 안되며 (b) LISP는이를 가능하게하기 위해 언어 선명도를 희생했습니다.
Periata Breatta

@PeriataBreatta 당신이 옳지 만 MP의 주요 장점은 MP가 런타임 페널티없이 추상화 가능하게한다는 것 입니다. 따라서 MP는 언어 복잡성을 증가시키는 비용이 들지만 추상화와 성능 사이의 긴장을 해결합니다. 그만한 가치가 있습니까? 나는 사실 말하고 싶지만 모든 주요 치하는 MP 확장이 작업 프로그래머의 많은 MP 유용 제공하는 트레이드 오프를 찾을 수 있음을 나타냅니다.
Martin Berger

답변:


10

모든 언어를 호모 닉으로 만들 수 있습니다. 기본적으로 언어를 '미러링'하여이 작업을 수행합니다 (언어 생성자에 대해 해당 생성자의 해당 표현을 데이터로 추가한다는 의미, AST 생각). 또한 견적 및 견적 해제와 같은 몇 가지 추가 작업을 추가해야합니다. 그것은 어느 정도입니다.

Lisp는 쉬운 구문 덕분에 초기에 사용했지만 W. Taha의 MetaML 언어 제품군은 모든 언어에 대해 가능하다는 것을 보여주었습니다.

전체 프로세스는 동종 생식 메타 프로그래밍 모델링에 요약되어 있습니다. 같은 소재에 대한보다 가벼운 소개가 여기 있습니다 .


1
틀린 점 있으면 지적 해주세요. "미러링"은 질문의 두 번째 부분 (io lang의 동음)과 관련이 있습니다.
incud

@Ignus 귀하의 의견을 완전히 이해하지 못했습니다. 호모 닉의 목적은 코드를 데이터로 처리 할 수 ​​있도록하는 것입니다. 즉, 모든 코드 형식은 데이터로 표현되어야합니다. 이 작업을 수행하는 방법에는 여러 가지가 있습니다 (예 : 경량 모듈 식 스테이징 방식에 의해 수행 된 데이터와 코드를 구별하기 위해 유형을 사용하는 AST 준 따옴표). 모두 언어 구문의 배가 / 미러링이 필요한 형식입니다.
Martin Berger

@Ignus가 MetaOCaml을 살펴보면 도움이 될 것이라고 생각합니까? "동성애"라는 것은 단지 할당량을 의미하는 것입니까? MetaML 및 MetaOCaml과 같은 다단계 언어가 더 발전한다고 가정합니까?
Steven Shaw

1
@StevenShaw MetaOCaml은 특히 Oleg의 새로운 BER MetaOCaml 입니다. 그러나 런타임 메타 프로그래밍 만 수행한다는 점에서 다소 제한적이며 AST만큼 표현 적이 지 않은 준 인용 부호를 통해서만 코드를 나타냅니다.
Martin Berger

7

OCaml의 컴파일러는 그래서, OCaml의 자체 기록 확실히 OCaml의에서 OCaml로하는 AST를 조작하는 방법이있다.

ocaml_syntax언어 에 내장 유형 을 추가하고 내장 defmacro함수를 사용하여 유형의 입력을 취하는 것을 상상할 수 있습니다.

f : ocaml_syntax -> ocaml_syntax

이제 유형defmacro무엇입니까? 경우에도이 그뿐만 아니라 정말, 입력에 따라 f신원 함수, 코드의 결과로 조각의 종류는 전달 된 구문의 부분에 따라 달라집니다.

언어가 동적으로 형식화되고 컴파일시 매크로 자체에 형식을 지정할 필요가 없기 때문에이 문제는 lisp에서 발생하지 않습니다. 한 가지 해결책은

defmacro : (ocaml_syntax -> ocaml_syntax) -> 'a

어떤 상황 에서도 매크로를 사용할 수 있습니다 . 그러나 이것은 안전하지 않습니다. 물론, bool대신에 a를 사용하여 string런타임에 프로그램을 중단시킬 수 있습니다.

정적으로 유형이 지정된 언어의 유일한 원칙 솔루션 은 결과 유형 이 입력에 종속 되는 종속 유형 을 갖는 defmacro것입니다. 이 시점에서는 상황이 상당히 복잡해 지며 David Raymond Christiansen 의 멋진 논문을 통해 시작하겠습니다 .

결론 : 복잡한 구문을 갖는 것은 문제가되지 않습니다. 언어 내부에서 구문을 표현하는 방법은 많으며 quote"단순한"구문을 internal에 포함 시키는 작업 과 같은 메타 프로그래밍을 사용할 수도 있습니다 ocaml_syntax.

문제는 특히 유형 오류를 허용하지 않는 런타임 매크로 메커니즘을 사용 하여이 유형을 올바르게 지정하는 것입니다.

물론 Ocaml과 같은 언어의 매크로에 대한 컴파일 타임 메커니즘을 갖는 것도 가능합니다 (예 : MetaOcaml 참조) .

또한 유용한 : Ocaml의 메타 프로그래밍에 관한 Jane street


2
MetaOCaml에는 컴파일 타임 메타 프로그래밍이 아닌 런타임 메타 프로그래밍이 있습니다. 또한 MetaOCaml의 타이핑 시스템에는 종속 유형이 없습니다. (MetaOCaml도 유형이 소리가 나지 않는 것으로 밝혀졌습니다!) Template Haskell은 흥미로운 중간 접근 방식을 가지고 있습니다. 모든 단계는 유형 안전하지만 새로운 단계에 들어갈 때는 유형 확인을 다시 수행해야합니다. 이것은 실제로 내 경험에서 실제로 잘 작동하며 최종 (런타임) 단계에서 유형 안전의 이점을 잃지 않습니다.
Martin Berger

@cody 확장 포인트를 사용 하여 OCaml에서 메타 프로그래밍을 할 수 있습니까?
incud

@Ignus 나는 Jane Street 블로그 링크에서 Extension Points에 대해 잘 모르지만 Extension Points에 대해 많이 알지 못합니다.
코디

1
내 C 컴파일러는 C로 작성되었지만 C에서 AST를 조작 할 수 있다는 의미는 아닙니다.
BlueRaja-Danny Pflughoeft

2
@immibis : 분명히, 그러나 그가 의미하는 바라면, 그 진술은 공허하고 질문과 무관합니다 ...
BlueRaja-Danny Pflughoeft

1

예를 들어 F # (OCaml 기반)을 고려하십시오. F #은 완전 호모 닉은 아니지만 특정 상황에서 함수 코드를 AST로 가져 오는 것을 지원합니다.

F #에서는 다음 print과 같이 Expr인쇄 된 것으로 표시 됩니다.

Let (message, Value ("Hello world"), Call (None, print_endline, [message]))

구조를 더 잘 강조하기 위해 동일한 것을 만드는 다른 방법이 있습니다 Expr.

let messageVar = Var("message", typeof<string>)
let expr = Expr.Let(messageVar,
                    Expr.Value("Hello world"),
                    Expr.Call(print_endline_method, [Expr.Var(messageVar)]))

나는 그것을 이해하지 못했다. F #을 사용하면 표현식의 AST를 "빌드"한 다음 실행할 수 있습니까? 그렇다면 eval(<string>)기능 을 사용할 수있는 언어의 차이점은 무엇 입니까? ( 많은 자료에 따르면, 평가 기능을 갖는 것이 호모 닉성 (homoiconicity)을 갖는 것과는 다릅니다. 이것이 F #이 완전히 호모
닉성

@Ignus AST를 직접 만들거나 컴파일러에서 AST를 만들 수 있습니다. Homoiconicity는 "언어의 모든 코드를 데이터로 액세스하고 변환 할 수있게합니다" . F #에서는 일부 코드를 데이터로 액세스 할 수 있습니다 . (예를 들어, 표시해야 print[<ReflectedDefinition>]속성입니다.)
svick
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.