初心者が「Clojure で Web 開発をはじめてみよう」をやってみて、ひっかかったところ。と、ライブラリのメモ。

2021-09-12

  • 「プログラミングClojure 第2版」を一通り読んで
  • 公式サイトのチュートリアルをやって
  • 4Clojure(現在は閉鎖中)を「easy」まで解答

ぐらいの初心者です。

理解を深める為に、ひっかかったろころ、チュートリアルで使用されているライブラリをまとめてみました。

ひっかかったところ、すぐに飲み込めなかったところ

Varオブジェクト

http://ayato-p.github.io/clojure-beginner/intro_web_development/column_rdd_and_more_ring.html#ring-repl

シンボルを直接渡してしまうと handler を束縛していたオブジェクト(関数)が直接 run-server に渡ってしまい、それを後から変更することは出来なくなるのですが、 Var オブジェクトを渡しておけば handler を束縛するオブジェクトが変わっても run-server 関数は Var に格納されたオブジェクトをその都度参照出来るようになるので、サーバーを再起動せずに handler 関数の再評価だけで出力結果を変更することができるようになります。

とりあえずは、「値渡しを参照渡しに変更」と読み替えて進めたけど、もう少し深いみたいなので、改めて調べます。

シンボル

Symbol は、それ自体が指し示す値を保持しているわけではない。

user=> (def x 1)
#'user/x

user=> 'x
x

user=> (type 'x)
clojure.lang.Symbol

user=> x
1

user=> (type x)
java.lang.Long


user=> (namespace 'user/x)
user

user=> (name 'user/x)
x

user=> (namespace 'x)
nil

user=> (name 'x)
x

メタ情報

http://ayato-p.github.io/clojure-beginner/intro_web_development/part3_what_is_compojure.html#id17

今まで使っていた html の定義はそのままに持ってきていますが、 ring.util.response/response もこのネームスペースに再定義します。再定義する際に ring.util.response/response の Var を todo-clj.util.response/response に渡しているわけですが、このときにメタ情報が失われます(つまり引数の数などといった情報)。なので、 alter-meta! でメタ情報を更新しています。

clojure.org のリファレンスによると

型に関する情報をコンパイラーに伝達するために使用されますが、データソース、ポリシーなどに注釈を付けるなど、さまざまな目的でアプリケーション開発者が使用することもできます。

メタデータは同等性(またはハッシュコード)に影響を与えません。メタデータのみが異なる2つのオブジェクトは同じです。 (Google 翻訳)

https://clojure.org/reference/metadata
(def ^{:version 1} document "This is text")
;;=> #'user/document

(meta #'document)
;;=> {:version 1}

(alter-meta! #'document #(update-in % [:version] inc))  ;increase version
;;=> {:version 2}

コンディションマップ

http://ayato-p.github.io/clojure-beginner/intro_web_development/part3_5_middleware_for_dev.html#id1

(defn wrap-dev [handler]
  {:pre [(or (fn? handler) (and (var? handler) (fn? (deref handler))))]}
  (let [wrap-exceptions (try-resolve 'prone.middleware/wrap-exceptions)

:pre / post とかあるのは、契約プログラミングにおける 事前条件 / 事後条件を示しています。これは関数を実行する際に、それぞれの条件を満たしているかを毎回チェックする、というものです。 特に安全にコードを実行したい際に利用することができます。

(defn constrained-sqr [x]
    {:pre  [(pos? x)]
     :post [(> % 16), (< % 225)]}
    (* x x))

いずれかの条件がfalseと評価され、* assert *がtrueの場合、java.lang.AssertionError例外がスローされます。

~@(スプライシングアンクオート)

http://ayato-p.github.io/clojure-beginner/intro_web_development/part3_what_is_compojure.html#id1

replで実行すると、いろいろ名前空間名がついてややこしくなるけど、それを省略すると以下のようになる、はず。

`(1 2 3) ;リストをクオート
=> (1 2 3)

`(1 2 3 (for [i (range 5)] i)) ;
=> (1 2 3 (for [i (range 5)] i))

`(1 2 3 ~(for [i (range 5)] i)) ;forをアンクォート
=> (1 2 3 (0 1 2 3 4))

`(1 2 3 ~@(for [i (range 5)] i)) ;forをスプライシングアンクオート
=> (1 2 3 0 1 2 3 4)

マクロ

http://ayato-p.github.io/clojure-beginner/intro_web_development/part6_build_up_our_app.html?highlight=%E3%83%9E%E3%82%AF%E3%83%AD#id10

ソースを見て、やっている内容はわかるけど、実際に自分で作るときにマクロの使い所が難しそうです。

いろいろ記事が書かれているようなので、要調査。

使われているライブラリの概要

ring

Webのリクエスト→処理→レスポンスを抽象化して扱う枠組み。

Compojure

ルーティング+α。DSLを使うタイプ。

compojure.core/GET

(GET "/" request home)

第一引数はパスで、第二引数はバインディング、第三引数以降ではバインディングを利用して返却するレスポンスを作る部分。

compojure.core/context

パスの共通部分をまとめるものです


(defroutes todo-routes
  (context "/todo" req
    (GET "/" req todo-index)
    (GET "/new" req todo-new)

compojure.core/routes

それぞれのルート定義(ハンドラー)をひとつの Ring ハンドラーへとする

compojure.core/defroutes

↑のマクロ定義

compojure.routes/not-found

prone

親切なエラー画面を出す。

environ

環境変数や Java のシステムプロパティを同じようにアクセス出来るようになる。

alenbic

replを再起動しなくてもライブラリを依存関係に追加。

Hiccup

タグベクターを HTML へ変換。

hiccup.core/html

bouncer

バリデーション

(require '[bouncer.core :as b]
         '[bouncer.validators :as v])
;; => nil
user> (b/validate {:title ""}
                  :title v/required)

ring-http-response

 ring.util.response ネームスペースを置き換える便利な HTTP レスポンスに関するライブラリ

slingshot

Clojure の try と throw のそれぞれと互換がある try+throw+ というマクロを提供するライブラリ

potemkin 

便利な関数いろいろ