2023/04/24:更新(シンタックスクォートのみを用いた方式を紹介。それ以外を削除。)
内容がほとんど同じな2つの関数
java.time
のLocalDateTime
とZonedDateTime
のparse
メソッドを呼び出す2つの関数があります。
この2つの関数をマクロを用いて共通部分を抜き出したいです。
(ns code01 (:import (java.time LocalDateTime ZonedDateTime) (java.time.format DateTimeFormatter))) (defn parse-local ^LocalDateTime [^CharSequence text ^DateTimeFormatter formatter] (LocalDateTime/parse text formatter)) (defn parse-zoned ^ZonedDateTime [^CharSequence text ^DateTimeFormatter formatter] (ZonedDateTime/parse text formatter))
マクロの呼び出し例は以下になります。 マクロを呼び出すことで関数を定義します。
(def-parse parse-local LocalDateTime) (def-parse parse-zoned ZonedDateTime)
シンタックスクォートによる定義
シンタックスクォートを用いたマクロ定義を下に示します。
(ns code02 (:require [clojure.pprint]) (:import (java.time LocalDateTime ZonedDateTime) (java.time.format DateTimeFormatter))) (defmacro def-parse [fn-name java-class] `(defn ~fn-name [^CharSequence text# ^DateTimeFormatter formatter#] (. ~java-class ~'parse text# formatter#))) (clojure.pprint/pprint (macroexpand-1 '(def-parse parse-local LocalDateTime))) (def-parse parse-local LocalDateTime) (def-parse parse-zoned ZonedDateTime)
実行結果は以下になります。
$ clojure -M src/code02.clj (clojure.core/defn parse-local [text__146__auto__ formatter__147__auto__] (. LocalDateTime parse text__146__auto__ formatter__147__auto__))
シンタックスクォート中ではシンボルは名前空間名/シンボル
に変換されます。
それを止めるには、クォートしたシンボルをアンクォートします。(例:`~'parse
)
また、マクロ中の関数の引数やlet内の変数などのローカル変数名は末尾に#
をつけてマクロ展開時に他と被らない名前にします。
上の例ではtext#
とformatter#
で#
を使用しています。
これで無事にdefn
するマクロを定義して処理を共通化できました。