ClojureのソースコードをClojarsにpushする

今日もClojureの進捗生産。

普段から

  • コードを書く
  • わからんことが発生する
  • 色々ググって調べる
  • 問題解決
  • 問題の発生から解決に至るまでのプロセスをブログで記録に残す

というサイクルを徹底していて、なのでブログの更新は自分が何かコードを書いていてつまづいた形跡となっている。

で、このサイクルを実行するには多少まとまった時間が必要なわけなのだけれど、なぜか自分でもよくわからないのだが色々書類作業が発生していて、あまりここ最近はコードを書くことができていない。

ここでいう書類作業とは、内定先の入社までにやるべきhogehogeだったり各所へのメール出しだったり色々。

ホントにこの手の書類作業はツラいものなのでできるだけのんびり一人でコードを書いている時間の方がハッピーなのだけれど、まあ世の中そう簡単にはうまくいかないわけで。

...とまああまり無駄な前置きをせずにさっさと今日やったことをまとめておこうと思う。

Clojarsへのpush

前回のエントリーでLeiningenの:dependenciesに記述されるものはローカルのディレクトリではなくClojarsであることを述べた。

komi.hatenadiary.com

今回はその続きとして、作ったリポジトリをClojarsへpushする。

前準備

まず前提として、今回の実行環境はmacOS Catalinaの最新バージョンである。

なぜここにきていきなり環境の話をするかというと、今回のClojarsへのpushに際してgnupgというものを用いて暗号化する必要があり、これが環境ごとによって微妙に挙動が異なるため。

さて、まず最初に行うこととして、Clojarsのアカウントを作成する。

clojars.org

これを行ったあと、~/.lein/credentials.cljに以下を記述する。

{#"https://clojars.org/repo"
  {:username  "ユーザー名" :password  "パスワード"}}

これを書いて保存すればオッケー。

GPGを準備

次に、先述のgnupgをインストールする。

今回はmacOSなのでHomebrewで行う。

$ brew install gnupg

インストールできたら、

$ gpg --version

などで確認。

このgnupgはいわゆる公開鍵と秘密鍵でhogehogeするものなので、まず鍵を生成しなければいけない。

なので以下のコマンドで鍵を生成。

$ gpg --gen-key

名前とメールアドレスを要求されるのでテキトーに入力しておく。

ここでメールアドレスは別に入れなくてもなんとかなるらしい(多分)(自分は問題なかった)(ホントにこれでいいかはわからない)

それらの入力が終わったらoの大文字のOで終了。

さて、そうしたらターミナルの画面がパッと切り替わって、なんだか文字化けしながら何か選択する感じの画面が出てくる。

f:id:komi1230:20191117003722p:plain
こちらのQiitaより拝借。GPGの画面。

文字化けしていてパッと見だと何を言っているのか理解不能だが、実はここはパスワードを入力させて最後にOKかキャンセルするかを選択する画面らしい。

GUIならまだしもCUIで無理をした結果、環境の違いでこうなってしまったらしい。悲しい。

一応なんかしらの言語設定を施すことでこの文字化けを回避することができるらしいが、わざわざそれをやるのはめんどくさいのでこのまま入力する。

この画面のままパスワードを入力していくと、だんだん * * * *と溜まっていくのがわかると思う。

入力が終わったら矢印キーで下のOKを選択し、Enterで抜ける。

そうすると鍵の生成が終わり。

Clojarsにpushするぞ

鍵の準備が終わったので、早速ClojarsへのCredentialsを暗号化する。

$ gpg --default-recipient-self -e ~/.lein/credentials.clj > ~/.lein/credentials.clj.gpg

これで暗号化できるはず。

さて、ようやく待ちに待ったClojarsへのpushを行う。

自分のClojureのプロジェクトへのリポジトリに移動し、以下のコマンド

$ lein deploy clojars

これでできる。

バージョン情報やプロジェクト名などはproject.cljから勝手に読み取ってくれる。便利。

pushする際に名前とパスワードが求められるので、入力。

これにて終了。

雑記

このデプロイコマンドを打つと勝手にtarget/が生成されるが、これが自分のGitHubリポジトリへのpushに邪魔だという人は.gitignoreにて

**/target/

と書いておけば全階層のtarget/ディレクトリが無視される。

まとめ

今回はgpgを用意してClojarsへのpushまでを行った。

これにて自分のプロジェクトがClojarsに乗っかった状況になるので、Leiningenで:dependenciesに書けば読んでくれるということになる。

実は今回のエントリーのモチベは、前のエントリーにて発表したClojure機械学習ライブラリのMoeにある。

Moeの進捗について、まだプロジェクト開始から数日しか経っておらず、これに加えてなかなか開発する時間が取れていないのでまだ全然なのだが、今日先ほど入力データのバリデーションを行ってくれるような簡単なユーティリティ関数を用意したので、それをまずClojarsにあげてみて確認したというのが今回の背景にある。

進捗はダメダメだが、その過程でClojureのお作法について色々学んでいっているので数ヶ月後にはかなり成長しているだろうことを期待。

ではでは、本日もお疲れ様でした。

Leiningenでプロジェクトの配下にサブプロジェクトがあるような構成を作る

実は今日でClojureに入門してそろそろ1ヶ月が経とうとしている。

Clojureを始めるきっかけとなったのは『7つの言語 7つの世界』だった。

ちょうど1ヶ月前、フォロワーの方と大阪でお茶することになったのだが、少し早めに着いてしまったので書店で本を物色して時間を潰していた。

その際この本を見かけ、なんとなくClojureの章の数ページだけ見てみるとClojureはおもしろいぞ〜的な感じの雰囲気で、それでちょっと興味を持ったのがきっかけ。

もともと趣味Lisperだった(Common Lisp書いてた)のだが、Clojureについては存在を認知していたもののなぜか手を出してこなかった(恐らくCommon Lispでそれなりに楽しかったから?)

まあそんなこんなでClojureを始めたというわけである。

で、今のところの感想として、Common LispよりClojureの方が馴染むかなぁといったところ。

ただ、Common Lispとの違いに戸惑って変なところでつまづいたりしてるので大変といえば大変。

ということでちまちまブログを更新してはClojureの勉強メモをつけているのがここ最近の話である。

とまあ回顧録的な話はこの辺にしておいて、そろそろ本題へ入ろうと思う。

自作ライブラリ書いてみたい

自分はもともとLispの人というより機械学習の人というような感じでツイッターでは振る舞っていたのだが、最近はLisp好きの人というような感じになってる。

ただ、一応話題のAI人材ということで、卒業研究も画像処理関係のタスクなので機械学習をやらなきゃいけないわけで。

そんなこんなで最近はLisp機械学習をやろうとウダウダしているわけである。

で、先日Clojure機械学習関係のリポジトリあるかなと探してみたところ、どれも最後のコミットが数年前という感じだった。

これはClojure機械学習系のライブラリ書いてみるのにいい機会かなと思い、Clojure機械学習ライブラリを書こうという思考に至るわけである。

まあ実際のところJavaでは機械学習ライブラリは存在していて、ClojureJavaの資産を利用できるので今からClojureで書くのは無駄なのだけれども...(おまけにJavaの方が速い)

とにかく、完全に自己満なのだがClojure機械学習自作ライブラリを作りたいというわけである。

Clojureディレクトリ構成がわからない

で、リポジトリを作ってプロジェクトを立ち上げようと思ったわけだけれど、今後の拡張可能性を考えてscikit-learnのようなプロジェクト構成にしようと考えた。

具体的に、

project_root
├── project.clj
├── README.md
├── LICENSE
├── src
    ├── linear_model
    │   ├── hoge.clj
    │   ├── foo.clj
    └── svm
        ├── bar.clj
        ├── piyo.clj

というような感じを想定していた。

なぜこうしたかというと、機械学習アルゴリズムは膨大で、線形回帰をはじめとしてニューラルネットや決定木があり、それらをいちいちリポジトリで切っていくより1つのリポジトリでまとまっていた方が扱いやすいと考えたため。

要するに、自分が所望のものはプロジェクトの配下にサブプロジェクトを配置するような感じにしたかったのである。

さて、そんなことを考えていた一方でLeiningenでlein new default hogeで作れるプロジェクトの雛形は

hoge
├── CHANGELOG.md
├── LICENSE
├── README.md
├── doc
│   └── intro.md
├── project.clj
├── resources
├── src
│   └── hoge
│       └── core.clj
└── test
    └── hoge
        └── core_test.clj

というような具合で、project.cljの直下にsrc/というディレクトリがあって、そこからソースコードが存在する。

そうしてプロジェクトのディレクトリ構成とLeiningenの依存関係について少し悩み始める。

Leiningenの:dependenciesが見ているもの

結論から言ってしまうとLeiningenでの:dependenciesはClojarsを参照しているのでローカルのディレクトリ構成は一切無関係で、プロジェクト配下のサブプロジェクトそれぞれをClojarsに登録さえすればプロジェクトの統一的なパッケージ化が実現できるらしい。

これは手元でlein new default hogeとして、本来やりたい構成を再現しながらlein runをしてみたところエラーを観測したのでこのような結論を出した次第である(恐らくどこかのドキュメントを参照すれば書いてありそうだが)

実際、例えばClojureのWebアプリフレームワークRingはまさにこの形をしていて、ring-corering-jetty-adapterなどのそれぞれをClojarsに登録してあり、本体のringproject.clj:dependenciesでそれらを包括するだけの形となっている。

github.com

ということで複数のサブプロジェクトから構成されるプロジェクトについては自由な配置にして良いので、大まかなディレクトリ構成については決まったわけである。

namespaceとディレクトリ名

先日ツイッターdefproject名前空間の違いがわからんという旨のツイートをしたところ、以下のようなことを教えてもらった。

なるほど、名前空間の定義もある程度の規則があるらしい。

そこでもう一度色々なClojureライブラリのディレクトリ構成を見てみると、名前空間hoge.foo.barというような場合は

src
├── hoge
    ├── foo
        ├── bar
            ├── main.clj
            ├── sub.clj

のようにディレクトリ名の重なり方と名前空間の名称はリンクしているのが良いらしい。

最終的に

以上の色々な試行錯誤を経て、最終的に今回自分が作りたいプロジェクトの構成は以下のようになった。

moe
├── CHANGELOG.md
├── LICENSE
├── README.md
├── doc
│   └── intro.md
├── project.clj
└── src
    ├── linear-model
    │   ├── CHANGELOG.md
    │   ├── README.md
    │   ├── doc
    │   │   └── intro.md
    │   ├── project.clj
    │   ├── src
    │   │   └── moe
    │   │       └── linear-model
    │   │           ├── base.clj
    │   │           └── core.clj
    │   └── test
    │       └── moe
    │           └── linear-model
    │               └── core_test.clj
    └── svm
        ├── CHANGELOG.md
        ├── README.md
        ├── doc
        │   └── intro.md
        ├── project.clj
        ├── src
        │   └── moe
        │       └── svm
        │           └── core.clj
        └── test
            └── moe
                └── svm
                    └── core_test.clj

今回のプロジェクトの名前はMoeで、ハワイ語で夢という意味があり、それにちなんで命名した。

github.com

ちなみにハワイ語命名に良く、

  • Lea : 幸福
  • Mahina : 月
  • Maoli : 本物の
  • Leo : 声
  • Koa : 勇敢な

など良さげな単語がたくさんあるので、ぜひ命名に困ったらハワイ語を探してみるといいと思う。

latte.la

まとめ

今回はLeiningenの:dependenciesが参照しているものについて簡単に調べ、その上で今回やりたいことを実現するためのディレクトリ構成を作った。

とりあえず当初の目的だった機械学習ライブラリの自作の準備ができたので、これからようやくアルゴリズムの実装に落ち着いて取り組むことができる。

別に今回のプロジェクトについては納期も無ければ要件をガチャガチャ変更する上司もいないし社内政治でプロジェクトがポシャる可能性も存在しないので、のんびりやっていこうと思う。

そういえば来月のアドベントカレンダーは2つ登録していて両方ともLisp系で書く予定なので、ある程度このプロジェクトが形になればEat your own dog foodを実践して何か記事にできるかなとか考えている。

とにかくこれからがんばっていくぞい〜

Clojureでルーティング機能を実装する

前々からClojureの勉強を続けていて、今回はルーティングの実装について。

komi.hatenadiary.com

Clojureでサーバーを立てて、そのあと順調に色々実装して最終的にRESTful APIの実装までやりたいな〜なんて思っていたのだけれど、その合間で色々Clojureについて分からないことが多々発生して、それらを対処していて気がついたらだいぶ時間が空いてしまった。

そんなこんなでようやくClojureでサーバー開発の進捗を生むことに。

今回はルーティングについての実装をやっていく。

ルーティングについて

例えばツイッターのURLは

https://twitter.com

となっている。

そして、自分のアカウント(今回は@komi_edtr_1230とする)へのURLは

https://twitter.com/komi_edtr_1230

という感じ。

ここで、/komi_edtr_1230というような追加部分によって自分のアカウントへとコントロールされるようになっている。

このように、今まではローカルでサーバーを立ててポート番号を3000としていた場合

http://localhost:3000

にブラウザからアクセスすることによって表示していた何らかの文章を表示していたが、それを

http://localhost:3000/hoge
http://localhost:3000/hoge/piyo

などと階層化したりする。

厳密にはもう少しちゃんとした説明があると思うが、とりあえずこんな感じのものをルーティングと呼ぶ。

とりあえずサーバー立てようぜ

概念は理解したし、とりあえずサーバーを立てようと思う。

例の如く、最初にLeiningenでプロジェクトの雛形を作る。

lein new app mini_server

そして、でき上がった雛形に対し、project.cljをいじって今回使うライブラリを書き込む。

(defproject mini_server "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.0"]
                 [ring "1.7.1"]]
  :main ^:skip-aot mini-server.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

今回使うのはRingのみなので、:dependenciesにringを追記する。

さて、src/mini_server/core.cljにコードをゴリゴリ書いていく。

(ns mini-server.core
  (:gen-class)
  (:require [ring.adapter.jetty :as jetty]))

(defonce server (atom nil))

(defn handler [req]
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body "Hi ! This is test server !"})

(defn start-server []
  (when-not @server
    (reset! server
            (jetty/run-jetty handler
                              {:port  3000
                               :join? false}))))

(defn stop-server []
  (when @server
    (.stop @server)
    (reset! server nil)))

(defn reset-server []
  (when @server
    (stop-server)
    (start-server)))

(defn -main
  "start this server"
  [& args]
  (start-server))

前回までの作業はこんな具合で、

lein run

として、ブラウザでhttp://localhost:3000を開いたらHi ! This is test server !と表示されていると思う。

ところでリクエストって?

今のところ、handlerが決め打ちでメッセージを出すような感じになっている。

ルーティングの実装ではこのhandlerを触っていくわけだが、最初にサーバーを動かした際のリクエストについて簡単な解説を。

上記のコードでは(defn handler [req] ...)というようにリクエストを引数にとって実装をしていたわけだけど、このリクエストは一体何者なんだろう。

それの中身を見るために、一度途中にprintlnを挟んで見てみる。

(defn handler [req]
  (println req)
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body "Hi ! This is test server !"})

そうすると、このような感じ。

{:ssl-client-cert nil, 
 :protocol HTTP/1.1,
 :remote-addr 0:0:0:0:0:0:0:1, 
 :headers {sec-fetch-site none, host localhost:3000, user-agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36, sec-fetch-user ?1, connection keep-alive, upgrade-insecure-requests 1, accept text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3, accept-language en-US,en;q=0.9,ja;q=0.8, accept-encoding gzip, deflate, br, sec-fetch-mode navigate, cache-control max-age=0}, 
 :server-port 3000, 
 :content-length nil, 
 :content-type nil, 
 :character-encoding nil, 
 :uri /, 
 :server-name localhost, 
 :query-string nil, 
 :body #object[org.eclipse.jetty.server.HttpInputOverHTTP 0x7600f8af HttpInputOverHTTP@7600f8af[c=0,q=0,[0]=null,s=STREAM]], 
 :scheme :http, 
 :request-method :get}

わかりやすさのために改行した。

リクエストはマップの形をしていてのだが、アクセス元の端末の情報やどのブラウザの情報かなどの情報が来ている。

ちなみにデータ構造としてはマップになっていて、これはライブラリのRingがいい感じに処理してくれているのでリクエストの情報がClojure的に扱いやすいようになっている。

さて、ここで注目したいのが:uriで、これは/となっている。

これはどういうことかというと、URLとしてはhttps://localhost:3000/にアクセスしていることを意味している。

なので、ルーティング機能を実装する際はこのURIが実装のヒントとなっていて、リクエストから(:uri req)ような感じでURIの情報を取ってきてテキトーに条件分岐させるのが良さげな気がする。

ルーティングを実装する

まず手始めに、受付可能なURIをこちら側で決めておこうと思う。

今回は//hogeがアクセス可能なURIとし、それ以外は何もしないような設定としたい。

つまり、ルーティングのマップとしては

(def routes
  {"/" home
   "/hoge" hoge-page})

としようと思う。

まず、ホーム画面へのリクエストを受けた際のページを書く。

(defn home-view [req]
  "<h1>Home</h1><a href=\"/hoge\">やっほー!ルーティングのテストのためのページだよ!ここはホーム画面!</a>")

次に、このページからのリクエストにステータス情報(200とか404とか)を付加し、さらにHTMLにおけるheadersを追加するようにする。

(defn ok [body]
  {:status 200
   :body   body})

(defn html [res]
  (assoc res
         :headers
         {"Content-Type" "text/html; charset=utf-8"}))

(defn home [req]
  (-> (home-view req)
      ok
      html))

さて、今度はhogeページの場合についても書く。

(defn hoge-view [req]
  "<h1>Hogeのページ</h1>コード書いてるときってなんかhogeって打ちたくなる...コード書いてるときってなんかhogeって打ちたくならない?")

(defn hoge-page [req]
  (-> (hoge-view req)
      ok
      html))

これらとは別で、指定されてないURIが呼ばれた際に404 page not foundが表示されるようにしたい。

(defn not-found []
  {:status 404
   :body "<h1>404 page not found</h1>"})

では各ページの準備が整ったので、最後に各ページに遷移するためのhandlerを書く。

(defn match-route [uri]
  (get routes uri))

(defn handler [req]
  (let [uri (:uri req)
        maybe-fn (match-route uri)]
    (println req)
    (if maybe-fn
      (maybe-fn req)
      (not-found))))

これらを実装して、実行してみる。

そうすると以下のように見えるはず。

f:id:komi1230:20191106234551p:plain
ホーム画面

f:id:komi1230:20191106234617p:plain
Hogeのページ

うんうん、ちゃんとURLが変わって各ページが見えてる。

まとめ

今回はリクエストについて簡単に調べたあとClojureでルーティング機能を実装し、各ページごとに表示を変えるようなものを作った。

ちなみに今回は割愛したが、ルーティングを簡単に実装するようなライブラリもあり、実際にプロダクトを作るようなケースにおいてはそういうものも試していきたい。

次回以降ではRESTful APIを実装してJSONでやり取りする何かを作っていく予定。

これからもがんばっていくぞい!

Clojureのマルチメソッドについてのメモ

ついさっきPaul Grahamの『ハッカーと画家』を読み終えた。

もともとLisperだということもあってPaul Grahamのサイトでエッサイを読んでいたのだけれど、ついこの前ブックオフに入ったらこの本が中古で売っていたので買ってみた次第。

感想として、Lispが最高と言う結論ではなく(もちろんLispは最高なんだけれど)、それよりもハッカーとして社会で良い仕事をするにはどうすればいいのかということについては今後の人生で長く付き合っていく問題なんだなぁというところ。

将来のことを考えているとプロマネみたいなこともしなければいけないわけで、でも個人的にはハッキング大好きの技術野郎でありたいという気持ちもあって、そこらへんをどう両立していくか、それと同時に良い仕事環境と文化をどう構築するか、などは技術に生きる人間として頭の片隅で意識しておく事柄なのかな、と。

さてさて、気難しい話はここらへんにしておいて今回もClojureの勉強メモを書いていく。

自分は今までCommon Lispの中心にやってきたわけだけど、やっぱりCommon LispClojureは同じLisp言語族でありながらそれなりに違う部分もあるわけで。

そういうことで前にはClojureのリーダマクロについてまとめた。

komi.hatenadiary.com

このエントリーを公開してからClojureハッカーの方々から色々教えてもらい、シンボルと値と名前空間の関係性についての話やメタデータコンパイル速度の話など、とても勉強になった。

ここらへんについてもそのうちまとめておく予定。

で、このエントリーではClojureにおけるマルチメソッドについてまとめようと思う。

マルチメソッドis何

マルチメソッドとは何かというと、簡単にいえば一つの関数名に対して複数の関数を仕込むこと。

多分言葉で説明するより例を出した方が早いと思うので、例えば

(defmulti greeting
  (fn [x] (get x "language")))

;params is not used, so we could have used [_]
(defmethod greeting "English" [params]
 "Hello!")

(defmethod greeting "French" [params]
 "Bonjour!")

;then can use this like this:
(def english-map {"id" "1", "language" "English"})
(def french-map {"id" "2", "language" "French"})

=>(greeting english-map)
"Hello!"
=>(greeting french-map)
"Bonjour!"

こんな具合にメソッドgreetingに対して様々な結果を出力させることができる。

Common LispClojureの違い

Clojureにおけるマルチメソッドdefmultiは、Common Lispだと総称関数defgenericに相当すると思われる(多分)。

Common Lispの場合、このようなマルチメソッドの定義は、

  1. まず最初に総称関数を宣言する
  2. その後にdefmethodでメソッドを生やす
  3. メソッドの分岐はdefmethodの引数の中でそれぞれの引数の型ごとに行う

実はCommon Lispは別に最初にdefgenericなんかやらなくてもdefmethodでメソッドを用意できるのだけれど、今回はClojuredefmultiとの対比を行うためにこんな感じで。

さて、具体的にCommon Lispではどのようにマルチメソッドの定義を行うかというと

(defgeneric greeting (a))

(defmethod greeting ((a string))
  (format t "Hello ~a!~&" name))

(defmethod greeting ((a integer))
  (format t "Your number is ~a." a))

というような感じで、引数の型ごとにマルチメソッドを定義する。

この場合、型は整数型とか意外にも自作の型が使えて、別途でクラスを定義することによって可能となる。

(defclass person ()
  ((name
    :initarg :name
    :accessor name)))

(setf p1 (make-instance 'person :name "Komi"))

(defmethod greet ((obj person))
  (format t "My name is ~a!~&" (name obj)))

こんな具合である。

setfでクラスのインスタンスを作った際にコンパイラ側にメタデータが渡され、それをもとにdefmethodで生やしたメソッドを使う際にメタデータの参照を行っているらしい。

簡単にまとめると

(defgeneric {総称関数の名前} ({引数})

(defmethod {総称関数の名前} (({引数} {引数の型}))
  {関数の中身})

とまあCommon Lispはこのように引数の型でマルチメソッドを定義して利用することができる。

さて、Clojureの場合どうかというと、最初の例をもう一度出すと

(defmulti greeting
  (fn [x] (get x "language")))

(defmethod greeting "English" [params]
 "Hello!")

(defmethod greeting "French" [params]
 "Bonjour!")

(def english-map {"id" "1", "language" "English"})
(def french-map {"id" "2", "language" "French"})

=>(greeting english-map)
"Hello!"
=>(greeting french-map)
"Bonjour!"

という感じ。

Clojureの場合、このマルチメソッドの動きとして

(defmulti {マルチメソッド名} 
  {何でメソッドを分岐させるか})

(defmethod {マルチメソッド名} {分岐のパターン} {引数}
  {関数の中身})

という風になっている。

今回の例では、(fn [x] (get x "language"))によってマップの中からlanguageに対応するものを引っ張ってきて、そして各languageごとに出力を変えている。

今回の例では引数は使っていないが、この定義方法なら各メソッドごとに引数の数や型を自由に指定できるわけである。

Common Lispとの違いとして、Common Lispでの総称関数では最初のdefgenericで各メソッドでの引数を指定していたのに対し、Clojureではdefmultiではディスパッチ(メソッドの分岐)だけを最初に定義しているということらしい。

うーん、結構違う感じだ。

ただ、冷静になって考えてみれば、たしかにClojureでのマルチメソッドの定義方法の方が柔軟な書き方ができるし、こっちの方が良いのではないかという気がしてくる。

実際、何かライブラリを用いる際にテキトーにオリジナルのメソッドを生やすとなるとClojureのやり方の方が使い勝手が良さそう。

Common Lispの場合、何かメソッドを生やす際は雑に何かラップした上でやるか、もしくは別の戦略を取る必要があり、こう考えるとClojureの方が自由度が高いと見ることができる。

プロトコル

マルチメソッドの書き方についてClojure良いなぁなんて思っていたら、どうやらClojureにはプロトコルdefprotocolというのが存在するらしい。

Clojureプロトコルとは、最初に型だけを定義して、メソッドの中身については後から実装していく感じらしい。

(defprotocol Hoge
  (foo [this])
  (bar [this a b]))

(defrecord Hoge1 []
  Hoge
  (foo [this] "this is Test Protocol")
  (bar [this a b] (str a b)))

(foo (Hoge1.))
=> "this is Test Protocol"

(bar (Hoge1.) "a" "b")
=> "ab"

なるほど、こっちの方がオブジェクト指向な書き方だ。

マルチメソッドに比べたらプロトコルの方がカッチリしている。

ところで、なぜかこいつには既視感を感じる。

class Hoge():
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def foo(self):
        return "this is Test Protocol"
    def bar(self, a, b):
        return str(a) + str(b)

あ、完全にPythonじゃん。

なるほどなるほど、まさかこんなところでClojurePythonが似ているなんてちょっと予想外だった。

面白い。

まとめ

今回はClojureのマルチメソッドの定義方法をCommon Lispと比較しながら雑にまとめた。

また、同時にClojureプロトコルについても簡単に説明した。

個人的にはあまりオブジェクト指向な書き方は好きじゃないのだけれど、場合によってはOOPで書いた方が楽な場合もあったりして、そこらへんはある程度妥協しながらということになるのかな。

とりあえず、Clojureおもろいなぁという感想。

これからもがんばるぞい!

シェルをzshにした

今までxonshというシェルを使っていたのだけれど、中身がPythonで書かれているせいか、新しい言語などに挑戦するときに環境構築でxonshが原因でコケることがちょこちょこあった。

今まではその度にxonshの場合になんとかできるように解決策を見出しいたのだけれど、流石に耐えきれなくて今回シェルをzshにすることにした。

もともと自分がxonshを使っていたモチベとして、シェル上でPythonがかけるということには対して魅力は感じてなくて、そうではなく大した設定をせずともデフォルトでリッチな補完機能や履歴検索、ハイライトなどを行ってくれていたからで、要するにシェルの設定を書くのがめんどくさかったため。

ここで今回ようやく重い腰を上げて環境構築するぞ、ということでxonshを卒業してzshにすることに。

結果として

結論としてこんな感じになった。

f:id:komi1230:20191030232012p:plain
zshの環境構築終了後

機能として

  • 有効なコマンドは緑色
  • 無効なコマンドは白色
  • コマンドの予測はグレーで表示
  • gitのブランチ管理とstatusの表示
  • コマンド予測
  • 補完のsuggestion

あたり。

他にもmangrepなどのコマンドでの色付けなども。

ちなみにプロンプトは自分で作った。オシャレ。

以下では環境構築の際にやったことについてまとめていく。

環境構築はやっぱりめんどくさい

まあ基本的にやりたくないのが環境構築。

あらかじめ環境構築を良しなにやってくれる物があれば人生ハッピー。

ということでPreztoを利用する。

リポジトリの指示の通りインストール。

github.com

まずzshを入れる。

自分が使っている端末はMacbookなので

$ brew install zsh

そしてzshを立ち上げる。

$ zsh

このときもしかしたら勝手に~/.zshrcが作られるかもしれないが、あとでPreztoが自動で作ってくれるので一旦ここでは削除しておく。

さて、リポジトリをcloneしてくる。

$ git clone --recursive https://github.com/sorin-ionescu/prezto.git "${ZDOTDIR:-$HOME}/.zprezto"

そして以下のコマンドを打ち込む。

setopt EXTENDED_GLOB
for rcfile in "${ZDOTDIR:-$HOME}"/.zprezto/runcoms/^README.md(.N); do
  ln -s "$rcfile" "${ZDOTDIR:-$HOME}/.${rcfile:t}"
done

前準備はこれでオッケーだと思われ。

デフォルトのシェルをzsh

としてzshを入れ、/etc/shells/usr/local/bin/zshを追加。

$ sudo vi /etc/shells  # => 最後に/usr/local/bin/zshを追加する

そしてデフォルトシェルを入れ替える

$ chsh -s /usr/local/bin/zsh

これにてデフォルトのシェルはzshになった。

zshの中身の具体的な設定

Preztoが色々やってくれたので、あとは自分のお好みの設定をやっていく。

まず、有効なコマンドの色付けとコマンドのsuggestionが欲しい。

ということで.zpreztorcにて以下を追記。

zstyle ':prezto:load' pmodule \
  'environment' \
  'terminal' \
  'editor' \
  'history' \
  'directory' \
  'spectrum' \
  'utility' \
  'completion' \
  'prompt' \
  'syntax-highlighting' \  <- こいつ追記
  'autosuggestions'  <- こいつも

これでオッケー。

さてさて、あとはプロンプトを整える。

自分の.zshrcはこうなっている。

# Source Prezto.
if [[ -s "${ZDOTDIR:-$HOME}/.zprezto/init.zsh" ]]; then
  source "${ZDOTDIR:-$HOME}/.zprezto/init.zsh"
fi

# Customize to your needs...
fpath=(/usr/local/share/zsh-completions $fpath)

# Environment variables
export LANG=ja_JP.UTF-8

# Be able to use colors
autoload -Uz colors
colors

# history
HISTFILE=~/.zsh_history
HISTSIZE=1000000
SAVEHIST=1000000

# Prompt
autoload -Uz vcs_info
zstyle ':vcs_info:git:*' check-for-changes true #formats 設定項目で %c,%u が使用可
zstyle ':vcs_info:git:*' stagedstr "%F{green}!" #commit されていないファイルがある
zstyle ':vcs_info:git:*' unstagedstr "%F{magenta}+" #add されていないファイルがある
zstyle ':vcs_info:*' formats "%F{cyan}%c%u(%b)%f" #通常
zstyle ':vcs_info:*' actionformats '[%b|%a]' #rebase 途中,merge コンフリクト等 formats 外の表示
precmd () { vcs_info }
PROMPT=' %{$fg[red]%}[%{$fg[blue]%}%n%{$fg[red]%}]-[%{$fg[green]%}%~%{$fg[red]%}]%{$reset_color%}'
PROMPT=$PROMPT' ${vcs_info_msg_0_} %{${reset_color}%}
'
PROMPT=$PROMPT'%{$fg[yellow]%} >>> '

多分これで大丈夫なはず。

最後にターミナルのカラーテーマ

普段から自分が使っているカラーテーマはIcebergなのだけれども、Icebergのままだと色が薄すぎて見にくい。

cocopon.github.io

確かに目に優しそうだけど....

てことでカラーテーマをIcebergからwombatに変える。

github.com

ここからダウンロードしてきて、Macbookでターミナルの環境設定のところからテーマを読み込ませる。

f:id:komi1230:20191030233732p:plain
ここの下部のimportとあるところ。日本語だと「読み込む」

これでオッケー。xonsh卒業。

まとめ

Zshに移行するためにPreztoを入れて、それに加えていくつか設定を書き加えた。

もうこれで環境構築で変なところで悲しむことはなさそう。

今回zshにしようと思った要因として、Common LispでのSLIME的なやつでClojureのCIDERを使おうと思ったのだけれど、それがREPLを動かしている側のサーバーとの通信でxonshが原因で通信が繋がらなかったことにある。

んー、xonshイケてるシェルなんだけどなぁ....

まあこれからはzsh使いのおじさんとして生きていく。

Clojureのリーダマクロをざざっと理解する

引き続きClojureのお勉強。

komi.hatenadiary.com

前回はClojureで簡単なメッセージを通信するHTTPサーバーを立てるところまでやった。

今回はそこから発展してミドルウェアの作成などをやろう.....と思っていたのだけれど、色々なWebサイトを参考にしてちょこちょこコードを読んでいたところリーダマクロがわからずなんだこれ...?となることが多々あった。

そこで今回はClojureでサーバーを構築する云々から一度離れてClojureのリーダマクロをささっとまとめておこうと思う。

リーダマクロって?

リーダマクロは接頭辞や接尾辞に何か文字を付け加えることで別の式に展開することを可能にするマクロ文字のこと。

例えばラムダ式の定義においてClojureだと

user=>  ((fn [x y] (+ x x)) 10 20)
;; 30

だったりするが、これはリーダマクロ#()を使うことで

user=> (#(+ %1 %2) 10 20)
;; 30

という感じになる。

ちなみにこの例での%は引数の順番を表し、%1は第一引数のこと。第一引数は%としてもオッケー。

こんな感じのマクロ文字がたくさんあるのだけれど、Clojure言語の実装を見てるとだいたいお気持ちが掴めてくるので以下ではそこらへんについてまとめていく。

Clojure言語のソースコードを見る

ClojureJavaで実装されていて、リーダマクロあたりについては以下のあたりのコードに実装されている。

github.com

中身はこうなっている。

    macros['"'] = new StringReader();
    macros[';'] = new CommentReader();
    macros['\''] = new WrappingReader(QUOTE);
    macros['@'] = new WrappingReader(DEREF);//new DerefReader();
    macros['^'] = new MetaReader();
    macros['`'] = new SyntaxQuoteReader();
    macros['~'] = new UnquoteReader();
    macros['('] = new ListReader();
    macros[')'] = new UnmatchedDelimiterReader();
    macros['['] = new VectorReader();
    macros[']'] = new UnmatchedDelimiterReader();
    macros['{'] = new MapReader();
    macros['}'] = new UnmatchedDelimiterReader();
// macros['|'] = new ArgVectorReader();
    macros['\\'] = new CharacterReader();
    macros['%'] = new ArgReader();
    macros['#'] = new DispatchReader();

これを見てみると#にディスパッチマクロが割り当てられており、ディスパッチマクロについては以下の通りに定義されている。

    dispatchMacros['^'] = new MetaReader();
    dispatchMacros['#'] = new SymbolicValueReader();
    dispatchMacros['\''] = new VarReader();
    dispatchMacros['"'] = new RegexReader();
    dispatchMacros['('] = new FnReader();
    dispatchMacros['{'] = new SetReader();
    dispatchMacros['='] = new EvalReader();
    dispatchMacros['!'] = new CommentReader();
    dispatchMacros['<'] = new UnreadableReader();
    dispatchMacros['_'] = new DiscardReader();
    dispatchMacros['?'] = new ConditionalReader();
    dispatchMacros[':'] = new NamespaceMapReader();

ディスパッチマクロとは、この場合マクロ文字#以降では別の式に変換するよーというような機能を持ったマクロで、リーダマクロの二段重ねくらいの認識で大丈夫なはず。

さて、早速中身を見ていこうと思う。

ダブルクオーテーション"セミコロン;

    macros['"'] = new StringReader();
    macros[';'] = new CommentReader();

これについては変数名の通りで、"は文字列を認識し;はコメントにするものである。

実際にClojureのコードでは

(println "Hi Clojure !")
;; (println "This is a just comment")

という感じになっている。

シングルクオーテーション\'

次の\'はシングルクオーテーションで、これはLisperにはお馴染みのシンボルを表す。

ClojureのシンボルはCommon Lispとは挙動が異なり、Common Lispではシンボルは値を内包していてシンボルと値が同一であるのに対し、Clojureはシンボルと値がそれぞれ独立で、それぞれを名前空間マッピングしているという構造を持っているらしい。

変数がunboundになることを想定したときシンボルと値は分けた方が都合がよいのでこのような構造をとっているらしく、ここらへんの挙動についてはRich HickeyのSimple made easyという哲学に基づいているもの、なんだとか。

少し難しいが、ここらへんも勉強していかないとなぁという感じ。

ご指摘いただいた@lagenorhynqueさんありがとうございました!!!

(def x 10)
(println x) ; => 10
(println 'x) ; => x
(println (quote x)) ; => x

アットマーク@

さて、次の@Common Lispの畑からやってきた人間にとってはあまり見慣れないもの。

Clojureには並行処理をサポートしていて、その際に変数を参照して展開することをこの@は行ってくれる。

refが参照ならderefが参照はずし(展開するといった方が正確?)

(def x (ref 10))
(println x) ; => #object[clojure.lang.Ref 0x17aabeae {:status :ready, :val 10}]
(println @x) ; => 10
(println (deref x)) ; => 10

ちなみにこの@は遅延評価でも有効らしく、

(def x (delay 10))
(println x) ; => #object[clojure.lang.Ref 0x17aabeae {:status :pending, :val 10}]
(println @x) ; => 10
(println x) ; => #object[clojure.lang.Ref 0x17aabeae {:status :ready, :val 10}]

この場合、遅延評価によって実行の前後でステータスがpendingからreadyに変化しているに注意。

キャレット^

これもまたCommon Lisperには見慣れないもので、Clojureでは実際の変数に対してコンパイル時の型の情報や名前空間などのメタ情報を付加することができる。

実際、変数をprivateかpublicかのアクセス制限をかけるとき

(def ^:private x 10)
(def ^{:private true} x 10 ;; 上と同じ

としたり、もしくは型の情報(型ヒント)を明記するとき

(defn- foo [^long i ^long j] (+ i j))

とすればオッケー。

実際にメタ情報を見ようとするなら

(def ^:private x 10)
(meta (var x)) 
;; {:private true, :line 1, :column 1, :file "/private/var/..."
(meta #'x)

として見れる。

ここで変数xについて'xがただのシンボルだが#'xはまた特殊で、Clojure では変数や関数の実体への参照として var というものがある。

よってREPLにて

(def x 10)
(println x)
(println 'x)
(println (var x))

として見てみると(var x)名前空間から参照しているのがわかると思う。

ここらへんは解説するとクソ長くなるのと自分がまだ完全理解してないのでここらへんで切り上げることにする。

バッククオーテーション`チルダ~

バッククオーテーションは式全体をシンボル化し、~はクオートを外す。

Common Lispだとアンクオートはカンマ,だったけどClojureではチルダ~になっているらしい。

さて、ここらへんは難しくないのでささっと。

(def x 10)
(println `(1 x)) ; => (1 x)
(println `(1 ~x)) ; => (1 10)

ちなみにここでクオートされたリストに対してアットマーク@を組み合わせると括弧を外すことができる。

(def y '(2 3))
(println `(1 ~@y) ; => (1 2 3)

なんでこんなことができるのかというと、~@がUnquoted Splicingというリーダマクロだから。

Lispすごい。

括弧とか( ) { } [ ]

ここらへんはリストとか集合とかベクトルとかのデータ型。

めんどくさいので省略。

シャープ+チルダ#^

メタデータを付加することができるが、先述のキャレット^単体と何が違うとかというと、#^では独自のkey/valueメタデータとして付与できる。

...というのがClojure1.1とか1.2とかあたりの話らしい。

なんでも、Clojure1.1ではキャレット^は非推奨で#^を推奨していたらしい。

しかし手元で試してみたら普通にキャレット^単体でも独自のkey/valueメタデータとして付与できて、なんだかよくわからない。

どういう使い分けなんだろう?

(def #^{:fuck-kyoto-univ 114514} x 10)
(meta #'x)

うーん、難しい。

追記. Clojure1.10.1では#^はdeprecatedとなっているらしく、挙動自体は#^^は変わらないらしい。

シャープ+シャープ##

これはInfなどを表すものらしい。

(/ 1.0 0.0) ; => ##Inf

シャープ+シングルクオーテーション#'

これは(var x)と同じこと。

さっきやった。

シャープ+ダブルクオーテーション#"..."

はい!出ました!みんな大好き正規表現!!!

Pythonだと最初にimport reとかやって正規表現ライブラリを引っ張ってきて...みたいな感じでやるけどClojureではデフォルトで正規表現を使えるのでなんと幸せなことだろうか....!!

(re-find #"\d+" "abc12345def") ; => "12345"
(re-matches #"hello, (.*)" "hello, world") ; => ["hello, world" "world"]

中の正規表現エンジンが先読みとか再帰とかのイケてるやつまでサポートしてるのかは知らないけど、Java正規表現のやつをラップしてる感じらしいので多分サポートしてるはず。

シャープ+括弧#(...)

これはこのエントリーの最初の例にやったので省略。

シャープ+波括弧#{...}

集合ね。省略。

シャープ+イコール#=

これは#=に続くフォームを評価するもので

#=(+ 1 2 3) ; => 6
(read-string "#=(+ 1 2 3)") ; => 6

というような感じ。

一つ注意点として*read-eval*がfalseになっている場合このリードマクロは機能しない。

(binding [*read-eval* false] (read-string "#=(+ 1 2 3)")) 
; => 計算できませーん

シャープ+エクスクラメーションマーク#!

コメントになるらしい。

シェバンとかで使えそう。

#!(+ 1 2 3) ; => 反応なし

シャープ+小なり大なり#<...>

これは以下のformを例外をThrowしてくれるらしい。

例えば(atom 42)をREPLで評価すると#<Atom: 42>とprintされ、これはUnreadableで評価されないものなんだよー的なことが他のサイトに書いてあったけどなんだかよくわからない。

参照 : CLJSのUnreadbleのページにて

シャープ+アンダーバー#_

これはコメントの扱いになるらしい。

つまり読まれず評価されない。

#_(+ 1 2 3) ; => 反応なし

シャープ+クエスチョンマーク#?

環境非依存なコードを書くために、環境ごとに条件分岐をしてくれるものらしい。

どういうことかというと、例えば以下のコードで

#?(:clj "Hi !" :cljs "Hello !")

実行がClojureならHi !とprintされ、ClojureScriptならHello !とprintされるらしい。

そういえばClojureScriptなんてものあったな....(Clojureに入門してそこまで時間たってない顔)

とりあえずこのディスパッチマクロはそういうものらしい。

シャープ+コロン#:

まず、コロン:はキーワード指定子であり、コロン+コロン::は現在の名前空間にキーワードを紐付けるものである。

その前提で、シャープ+コロン#:名前空間にキーワードをまとめて紐づけることができる。

具体的に、

#:person{:first "Han"
         :last "Solo"
         :ship #:ship{:name "Millennium Falcon"
                      :model "YT-1300f light freighter"}}

は以下のように読み替えられる。

{:person/first "Han"
 :person/last "Solo"
 :person/ship {:ship/name "Millennium Falcon"
               :ship/model "YT-1300f light freighter"}}

さて、コロン+コロン::は現在の名前空間にキーワードを紐付けるので#::も同様に

(ns rebel.core
  (:require
    [rebel.person :as p]
    [rebel.ship   :as s] ))

#::p{:first "Han"
     :last "Solo"
     :ship #::s{:name "Millennium Falcon"
                :model "YT-1300f light freighter"}}

は以下の通りとなる。

{:rebel.person/first "Han"
 :rebel.person/last "Solo"
 :rebel.person/ship {:rebel.ship/name "Millennium Falcon"
                     :rebel.ship/model "YT-1300f light freighter"}}

まとめ

意外とリーダマクロはそこまで多くないらしい。

ただ、いくつか挙動が謎なものもあって、自分でコードを書くときは不安だけどコードを読む分には最低限は大丈夫そう。

ところでメタデータで型情報を渡したらコンパイルとか速くなるものなんだろうか?

あくまで型ヒントみたいな感じでPythonのType Hintingみたいにコードに虚無を植え付けるだけとかだと感情も虚無になってしまうので....

とりあえずだんだんとClojureが楽しくなってきた。

さあこれからもがんばるぞい!

Clojureで超絶極小サーバーを立てる

先日の奨学金返金エントリーに対して辛辣なコメントが飛び交っており心を痛めている今日この頃。

komi.hatenadiary.com

このエントリーは返納までの期日が超絶短期間に設定されたことを問題視したものだったのだけれど、返金することになったことへの批判と擁護が予想以上に飛んできて色々想定外でいやそうじゃないんだよなぁという感情しかなかったのでいくつか追記しておいた。

多分もう大丈夫だと思われる。

さて、前置きという名のボヤキはここらへんにしておいて、最近もちまちまとClojureの勉強を進めている。

今日はClojureのサーバーの立て方とその周りの云々やっていて、その際に色々つまづいたものをここでテキトーにまとめておこうと思う。

概要

Pythonでサーバーを立てる場合、ターミナル上で

$ python -m http.server 8080

としたり、もしくはソースコードとして

import http.server
import socketserver

PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()

と書いてこれを実行することでサーバーを立てることができる。

PythonにはWSGI(Web Server Gateway Interface, ウィズギーと呼ぶらしい)というWebアプリケーションとWebサーバーの間をなすインターフェースがあり、WebアプリケーションとWebサーバーの実装を切り離すことで柔軟なアーキテクチャ設計ができるようになっている。

f:id:komi1230:20191028225318p:plain
サーバーのアーキテクチャ

図はこのページより拝借。

そんなアプリケーションとサーバーの間のクッションだが、JavaではそのインターフェースとしてJava Servlet APIがあり、そしてClojureの場合はRingというものがその役割を果たしている。

プロジェクトを立ち上げる

さて、概要はなんとなく掴んだのでいざ実装。

ClojureのプロジェクトはLeiningenを使ってビルドしていくので、まずプロジェクトを立ち上げる。

$ lein new app mini-server
$ cd mini-server

これでappをベースとしたプロジェクトディレクトリができる(デフォルトではlibraryをベースとしている)

ClojurePythonと違ってREPLに入ったあとに自由にモジュールをimportできるわけではなく、最初にプロジェクトファイルに何のモジュールを使うか書いておく必要があるのでこれを記述する(別にプロジェクトファイルに書かずとも何とかなる方法はあるが割愛)

project.cljにてdependenciesにRingを追記する。

(defproject mini-server "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.0"]
                 [ring "1.7.1"]]
  :main ^:skip-aot mini-server.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

ちゃんとバージョンを書いておく必要あり。

これでターミナルにてLeiningenのREPLを起動すると必要なモジュールを用意してくれるのでターミナルにて早速使ってみる。

まずREPLを起動。

$ lein repl

そして動かしてみる。

mini-server.core=> (require '[ring.adapter.jetty :as s])
;; => nil
mini-server.core=> (def server (atom nil))
;; => #'mini-server.core/server
mini-server.core=> (reset! server 
              #_=>         (s/run-jetty (fn [req] 
              #_=>                        {:body "Hi ! This is test server !"}) 
              #_=>                      {:port 3000 :join? false}))

途中の引数にて:join?はtrueの場合スレッドをサーバーが止まるまでブロックするのでREPLに帰ってこれなくなるためfalseにしておいた。

これを実行してブラウザにてlocalhost:3000を見てみると

Hi ! This is test server !

と表示されていると思う。

さて、動いていることが確認できたので今度はサーバーを止める。

mini-server.core=> (.stop @server)
;; => nil
mini-server.core=> (reset! server nil)
;; => nil

まとめ

今回はClojureでRingを使って簡単なHTTPリクエストを送るサーバーを実装した。

これからはもっとサーバーのライフサイクルをもっと簡潔に扱えるようなものを作り、ミドルウェアなども作っていこうと思う。

とりあえず今日はここまで。

余談

エントリーを書いてるときブラウザでのフォントが等幅フォントではないのでコードの部分のインデントを合わせるのが若干めんどくさい。

かといって今のフォントは読みやすいので記事を書いているときだけ別のフォントに切り替えることができたら色々捗りそう。

ここらへんもそのうち模索してみる予定。

出典