<style>
/* basic design */
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6,
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {
font-family: 'Meiryo UI', 'Source Sans Pro', Helvetica, sans-serif, 'Helvetica Neue', 'Helvetica', 'Arial', 'Hiragino Sans', 'ヒラギノ角ゴシック', YuGothic, 'Yu Gothic';
text-align: left;
line-height: 1.6;
letter-spacing: normal;
text-shadow: none;
word-wrap: break-word;
color: #444;
}
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {font-weight: bold;}
.reveal h1, .reveal h2, .reveal h3 {color: #00a474;}
.reveal th {background: #DDD;}
.reveal section img {background:none; border:none; box-shadow:none; max-width: 95%; max-height: 95%;}
.reveal blockquote {width: 90%; padding: 0.5vw 3.0vw;}
.reveal table {margin: 1.0vw auto;}
.reveal code {line-height: 1.2;}
.reveal p, .reveal li {padding: 0vw; margin: 0vw;}
.reveal .box {margin: -0.5vw 1.5vw 2.0vw -1.5vw; padding: 0.5vw 1.5vw 0.5vw 1.5vw; background: #e4ffe5; border-radius: 1.5vw;}
/* table design */
.reveal table {background: #f5f5f5;}
.reveal th {background: #444; color: #fff;}
.reveal td {position: relative; transition: all 300ms;}
.reveal tbody:hover td { color: transparent; text-shadow: 0 0 3px #aaa;}
.reveal tbody:hover tr:hover td {color: #444; text-shadow: 0 1px 0 #fff;}
/* blockquote design */
.reveal blockquote {
width: 90%;
padding: 0.5vw 0 0.5vw 6.0vw;
font-style: italic;
background: #ddffff;
}
.reveal blockquote:before{
position: absolute;
top: 0.1vw;
left: 1vw;
content: "\f10d";
font-family: FontAwesome;
color: #00a474;
font-size: 3.0vw;
}
/* font size */
.reveal h1 {font-size: 5.0vw;}
.reveal h2 {font-size: 4.0vw;}
.reveal h3 {font-size: 2.8vw;}
.reveal h4 {font-size: 2.6vw;}
.reveal h5 {font-size: 2.4vw;}
.reveal h6 {font-size: 2.2vw;}
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {font-size: 2.2vw;}
.reveal code {font-size: 1.6vw;}
/* new color */
.red {color: #EE6557;}
.blue {color: #16A6B6;}
/* split slide */
#right {left: -18.33%; text-align: left; float: left; width: 50%; z-index: -10;}
#left {left: 31.25%; text-align: left; float: left; width: 50%; z-index: -10;}
</style>
<style>
/* 背景デザイン */
.reveal {
background-color:/*背景色*/
#f8f8ff;
}
.reveal h1 {padding: 3.0vw 0vw;}
@media screen and (max-width: 1024px) {
.reveal h2 {margin: -2.0vw 0 0 0; padding: 0.0vw 0vw 3.0vw 2.0vw; }
}
@media screen and (min-width: 1025px) and (max-width: 1920px) {
.reveal h2 {margin: -1.5vw 0 0 0; padding: 0.0vw 0vw 3.0vw 2.0vw; }
}
@media screen and (min-width: 1921px) and (max-width: 100000px) {
.reveal h2 {margin: -1.0vw 0 0 0; padding: 0.0vw 0vw 3.0vw 2.0vw; }
}
</style>
<style>
/* specific design */
.reveal h2 {
padding: 0 1.5vw;
margin: 0.0vw 0 2.0vw -2.0vw;
border-left: solid 1.2vw #00a474;
border-bottom: solid 0.8vw #9e9e9e;
}
</style>
<!-- --------------------------------------------------------------------------------------- -->
#### 卒業研究スライド
# The Racket Guide<br>16 Macros Ⅱ
<br>
<br>
#### 2021/10/22
### @ariken
---
## 16.2 General Macro Transformers
---
## Sintax Objects
マクロトランスフォーマの入力と出力(ソース形式と置換形式)は、 *sintax object* として表される。
sintax object には、式の quoted 形式に本質的に対応するシンボル、リスト、定数値(数字など)が含まれる。例えば、式 (+ 1 2) の表現には、synbol '+ と数字の 1 と 2 がリストに含まれている。この quoted された内容に加えて、sintax object はソースロケーションと語彙結合情報をフォームの各部分に関連付ける。ソースロケーション情報は、sintax error を報告するときなどに使用され、語彙結合情報は、マクロシステムが語彙の範囲を維持するために使用される。この追加情報に対応するため、式 (+ 1 2) の表現は単なる '(+ 1 2) ではなく、'(+ 1 2) をsintax objectにパッケージ化したものになる。
文字としてのsintax objectを作成するには、シンタックスフォームを使用する。
```ocaml
> (syntax (+ 1 2))
#<syntax:eval:1:0 (+ 1 2)>
```
----
' が quote を省略するのと同じように、#' は (syntax) を省略する。
```ocaml
> #'(+ 1 2)
#<syntax:eval:1:0 (+ 1 2)>
```
----
シンボルだけを含む sintax object は、*identifier sintax object* である。Racketは、識別子を検出するidentifier? 特に、free-identifier=? は、2つの識別子が同じ割り当てを参照しているかどうかを判断する。
```ocaml
> (identifier? #'car)
#t
> (identifier? #'(+ 1 2))
#f
> (free-identifier=? #'car #'cdr)
#f
> (free-identifier=? #'car #'car)
#t
> (require (only-in racket/base [car also-car]))
> (free-identifier=? #'car #'also-car)
#t
```
----
シンタックスオブジェクト内のリスト、シンボル、数字などを見るには、 syntax->datum を使用する。
```ocaml
> (syntax->datum #'(+ 1 2))
'(+ 1 2)
```
----
syntax-e関数はsyntax->datumと似ているが、ソースの位置と字句の文脈の情報を1層だけアンラップして、独自の情報を持つサブフォームをシンタックスオブジェクトとしてラップしたままにする。
```ocaml
> (syntax-e #'(+ 1 2))
'(#<syntax:eval:1:0 +> #<syntax:eval:1:0 1> #<syntax:eval:1:0 2>)
```
----
### datum->syntax
syntax->datumの反対は、もちろんdatum->syntaxである。'(+ 1 2)のようなデータに加えて、datum->syntaxは語彙的な文脈を提供するために既存のシンタックスオブジェクトを必要とし、またオプションとしてソースロケーションを提供するために別のシンタックスオブジェクトを必要とします。
```ocaml
> (datum->syntax #'lex
'(+ 1 2)
#'srcloc)
#<syntax:eval:1:0 (+ 1 2)>
```
---
## Macro Transfomer Procedures
1つの引数を持つ任意の手続きは、マクロトランスフォーマーになり得る。結果的に、 syntax-rules 形式はプロシージャ形式に展開されるマクロである。たとえば、 syntax-rules 形式を( define-syntax 形式の右手に置くのではなく)直接評価すると、その結果はプロシージャ(手続き)になる。
```ocaml
> (syntax-rules () [(nothing) something])
#<procedure>
```
----
syntax-rules を使用する代わりに、lambda を使用して独自の Macro Transfomer Procedures を直接記述することができる。プロシージャ(手続き)の引数はソース形式を表す sintax object であり、プロシージャ(手続き)の結果は置換形式を表す sintax object でなければならない。
```ocaml
> (define-syntax self-as-string
(lambda (stx)
(datum->syntax stx
(format "~s" (syntax->datum stx)))))
> (self-as-string (+ 1 2))
"(self-as-string (+ 1 2))"
```
----
マクロトランスフォーマに渡されるソース形式は、識別子が応用位置(式を始める括弧の後)で使用されている式を表し、識別子が応用位置ではなく式の位置として使用されている場合は、それ自体を表す。
syntax-rules が生成する手続きは、その引数が識別子を単独で使用している場合にはシンタックスエラーを発生させる。
```ocaml
> (self-as-string (+ 1 2))
"(self-as-string (+ 1 2))"
> self-as-string
"self-as-string"
```
define-syntax 形式は define と同じ関数のショートカット構文をサポートしているので、以下の self-as-string の定義は、lambdaを明示的に使用したものと同等である。
```ocaml
> (define-syntax (self-as-string stx)
(datum->syntax stx
(format "~s" (syntax->datum stx))))
> (self-as-string (+ 1 2))
"(self-as-string (+ 1 2))"
```
---
## Mixing Patterns and Expressions: sintax-case
syntax-rules が生成する手続きは、内部的に syntax-e を使用して与えられた sintax object を分解し、 datum->syntax を使用して結果を構築する。 syntax-rules 形式では、パターンマッチングやテンプレートsintaxモードから任意のRacket式に移行する方法がない。
syntax-case 形式では、パターンマッチ、テンプレートsintax、任意の式を混在させることができる。
```ocaml
(syntax-case stx-expr (literal-id ...)
[pattern expr]
...)
```
syntax-case 形式は、 syntax-rule と異なり、手続きを生成しない。代わりに、パターンと照合する sintax object を決定する stx-expr 式で始まる。また、各 syntax-case 節は、パターンとテンプレートの代わりに、パターンと expr を持つ。
----
expr の中では、通常#'で省略されるsintax形式がテンプレートsintaxモードに移行する。節の expr が #' で始まる場合sintax rule形式のようなものになる。
```ocaml
> (syntax->datum
(syntax-case #'(+ 1 2) ()
[(op n1 n2) #'(- n1 n2)]))
'(- 1 2)
```
----
swap マクロは define-syntax-rule や syntax-rules の代わりに syntax-case を使って書くことができる。
```ocaml
(define-syntax-rule (swap x y)
(let ([tmp x])
(set! x y)
(set! y tmp)))
```
```ocaml
(define-syntax (swap stx)
(syntax-case stx ()
[(swap x y) #'(let ([tmp x])
(set! x y)
(set! y tmp))]))
```
----
syntax-case を使用する利点の1つは、 swap のエラーレポートをより適切に提供できることである。例えば、 swap の define-syntax-rule の定義では、 `(swap x 2)` は、 2 が識別子ではないため、 set! の観点からシンタックスエラーが発生する。 swap の syntax-case の実装を改良して、サブフォームを明示的にチェックすることができる。
```ocaml
(define-syntax (swap stx)
(syntax-case stx ()
[(swap x y)
(if (and (identifier? #'x)
(identifier? #'y))
#'(let ([tmp x])
(set! x y)
(set! y tmp))
(raise-syntax-error #f
"not an identifier"
stx
(if (identifier? #'x)
#'y
#'x)))]))
```
----
この定義では、 `(swap x 2)` は、 set! ではなく swap に起因するシンタックスエラーを提供している。
swap の上記の定義では、 #'x と #'y は、マクロ変換の結果として使用されていないにもかかわらず、テンプレートである。この例では、テンプレートが入力構文の一部にアクセスするためにどのように使用されるかを示している(この場合、一部の形式をチェックするため)。また、 #'x または #'y のマッチは raise-syntax-error の呼び出しで使用され、syntax-error メッセージが非識別子のソースの場所を直接示すことができる。
{"metaMigratedAt":"2023-06-16T12:39:41.720Z","metaMigratedFrom":"YAML","title":"MacroとはⅡ","breaks":true,"slideOptions":"{\"theme\":\"white\",\"controls\":true,\"progress\":true,\"slideNumber\":\"c/t\",\"center\":false,\"fragments\":true,\"help\":true,\"transition\":\"convex\",\"transitionSpeed\":\"default\",\"keyboard\":true,\"width\":\"93%\",\"height\":\"100%\",\"spotlight\":{\"enabled\":false}}","contributors":"[{\"id\":\"a0de3d6a-cf71-4961-a3bb-e324e7c21a77\",\"add\":9103,\"del\":577}]"}