Obiekty składniowe mają zwykle być tylko serializable data. Składnia 3D osłabia ten stan: pozwala nam przemycić dowolne wartości, a nie tylko zwykłe dane. To czyni je "3d": są wartościami, które wznoszą się ponad zwykłe płaskie obiekty, których można się spodziewać po obiektach składniowych.
Na przykład możemy zakraść wartości lambda
!
#lang racket
(define ns (make-base-namespace))
(define (set-next! n)
(parameterize ([current-namespace ns])
(eval #`(define next #,n)))) ;; <-- 3d-syntax here
(define (compute s)
(parameterize ([current-namespace ns])
(eval s)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define counter 0)
(set-next! (lambda()
(set! counter (add1 counter))
counter))
(compute '(+ (next)
(next)
(next)
(next)))
Rozwiązanie to jest zwykle złe, ponieważ obecność takich wartości prawdopodobnie oznacza nieuzasadniona próba wycieku informacji po drugiej fazy kompilacji. Rezultat jest taki, że prawdopodobnie nie można go osobno skompilować. Jeśli widzisz błąd, który brzmi mniej więcej tak:
write: cannot marshal value that is embedded in compiled code value
to jest to najprawdopodobniej spowodowane makro wyprodukował kawałek 3D składni, które nie mogą być serializowane do kodu bajtowego.
Czasami, w rzadkich sytuacjach, naprawdę potrzebujemy składni 3d, często w kontekście dynamicznej oceny. Jako konkretny przykład, debugger w DrRacket może chcieć zanotować składnię programu, aby aplikacje funkcjonalne bezpośrednio wywoływały funkcje debuggera, dzięki czemu możemy robić takie rzeczy, jak kolorowanie pokrycia kodu w edytorze programów. W tym sensie składnia 3d może działać jako kanał komunikacji między dynamicznie wycenianym kodem a jego otoczeniem.
W przyszłości może wszystko będzie dobrze udokumentowane! +1 dla trudności. Wygląda mi na dobre pytanie o nagrodę. Jedyne, co mogłem trafnie zlokalizować, to ta dyskusja: http://lists.racket-lang.org/dev/archive/2013-January/011637.html – jdero