Skip to content

Instantly share code, notes, and snippets.

@lagenorhynque
Last active November 11, 2022 06:59
Show Gist options
  • Save lagenorhynque/1427b3a733ad0bd1bc7edd44790193d5 to your computer and use it in GitHub Desktop.
Save lagenorhynque/1427b3a733ad0bd1bc7edd44790193d5 to your computer and use it in GitHub Desktop.
Clojureで作る"simple"なDSL

Clojureで作る"simple"なDSL


(defprofile lagénorhynque
  :id           @lagenorhynque
  :reading      "/laʒenɔʁɛ̃k/"
  :aliases      ["カマイルカ🐬"]

  :languages    [Clojure Haskell English français]

  :interests    [programming language-learning law mathematics]

  :commits      ["github.com/lagenorhynque/duct.module.pedestal"
                 "github.com/lagenorhynque/duct.module.cambium"]
  :contributes  ["github.com/japan-clojurians/clojure-site-ja"])

twitter icon


Lisp系言語で(内部)DSLといえばマクロを使うもの?

  • マクロを定義すると
    • コンパイル前にコード(AST)を自在に変換し評価を制御することができる

    • マクロ自体はfirst-class objectではないのでcomposabilityが損なわれる


Clojureコミュニティではマクロ定義は控えめ

  • 「マクロ・クラブ」のルール

  • data > functions > macros

    • data-driven, data-orientedなものを好む

Clojureコミュニティでは"simple"であることを重視

  • Simple Made Easy (Rich Hickeyのプレゼン)
    • 一言でいえば、"simple"に設計しよう

      • "simple"とは単一の役割/責務のみを上手く果たすこと
      • cf. UNIX哲学
    • "complex"にするのを避けて"simple"なものを組み合わせよう


data-drivenなDSLの例


Reagentのテンプレート: HTML

ReactのJSXに相当するテンプレート

(defn some-component []
  [:div
   [:h3 "I am a component!"]
   [:p.someclass
    "I have " [:strong "bold"]
    [:span {:style {:color "red"}} " and red"]
    " text."]])

(def sqlmap {:select [:a :b :c]
             :from   [:foo]
             :where  [:= :f.a "baz"]})

bidi: ルーティングDSL

(def routes ["/" {"index.html" :index
                  "articles/" {"index.html" :article-index
                               [:id "/article.html"] :article}}])

cf. Compojure: マクロベースのルーティングDSL

(defroutes app
  (GET "/" [] "<h1>Hello World</h1>")
  (route/not-found "<h1>Page not found</h1>"))

malli: バリデーションスキーマ

(def Address
  [:map
   [:id string?]
   [:tags [:set keyword?]]
   [:address
    [:map
     [:street string?]
     [:city string?]
     [:zip int?]
     [:lonlat [:tuple double? double?]]]]])

data-drivenだと何がうれしい?

  • Clojureでプログラム的に自由に操作できる
    • 言語機能と標準ライブラリでそのまま扱える

    • read (parse)も print もそのままできる

    • Clojureデータはclojure.specで仕様を記述できる

    • Clojureデータとしてどのように表現し、どのように変換/解釈するかだけに集中できる

      • 問題も"simple"になる

参考: edn形式

  • edn (extensible data notation)

    • Clojureリテラル(のサブセット)によるシリアライゼーションフォーマット

    • ClojureにとってのS式表現

      • ベクター、マップ、セットなどもある
      • JavaScriptにとってのJSON相当
    • タグによる拡張も可能(extensible)

  • Clojureとはedn形式で表現されたデータを扱うことに特化した言語ともいえる(?)


Further Reading

#!/usr/bin/env bash
# npm install -g reveal-md
reveal-md simple-dsls-in-clojure.md --theme night --highlight-theme monokai-sublime -w $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment