ハツカネズミの恋

Lisp のもろもろ,おぼえがき

How to deal with Ring

What's Ring ?

Ring is a handy set of interfaces to interact with the web server. Ring uses maps instead of functions to express the requests from client and the responses from server, like Rack in Ruby or WSGI in Python. Ring has been used commonly and playing an essential role in Clojure web development .

Components

Ring is composed of :

  • handler
  • middleware
  • request-map
  • response-map

Handler

Handler is a sort of function that takes request-map and return response-map. Handlers in Ring can be implemented as simple function, so they are easily tested.

(defn handler [req]
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body (:remote-addr req)})

:status is HTTP status like 404, :headers stands for HTTP header for client, and :body represents the response body.

Middleware

Middleware is defined as a higher-order-function for handlers. Middleware should take a handler as the first argument and return a new handler.

You can write middleware like:

(defn wrap-question-mark [handler]
  (fn [request]
    (let [response (handler request)]
      (update response :body #(str % "??")))))

I know this sample is a completely-nonsense, but sometimes you want to add double-question mark to every single response body, do you ??

Libraries

Ring has 4 libraries:

  • ring-core
  • ring-devel
  • ring-jetty-adapter
  • ring-servlet

Jetty is a Java HTTP server and Java Servlet container.

Eval in REPL

Now, let's eval next lines in your REPL ($ lein repl or something) Firstly, you need to require ring-jetty-adapter to connect Ring app to Jetty.

user => (require '[ring.adapter.jetty :as s])
;=> nil

This will return nil and it's a sign that you've successfully connected your app to Jetty.

Then, you can do next step: define the status of your server to (atom nil). Atoms provide a shared, synchronous state, and enables you to make synchronous changes of value. Since, In this case, the state of server would be toggled, you should use atom.

user => (def server (atom nil))
;=> #'user/server

You will get #'user/server as a result. To the finish, reset the server to start your server in port 3000.

user => (reset! server (s/run-jetty (fn [req] {:body "Close the world, Open the nExt"}) {:port 3000 :join? false}))

Let's explain these lines.

  1. reset! sets the instance returned by s/run-jetty to the server.
    This means the value of (atom nil) has been changed.
  2. s/run-jetty takes 2 arguments:
    first one ((fn [req] {:body "Close the world, Open the nExt"})) is a Ring handler function taking the request-map and returning the response-map, and the second ({:port 3000 :join? false}) is a server option.

Now go to your browser and type http://localhost:3000/. If there is no mistake, the text "Hello, World" should be displayed.

If you get tired of playing with your "Close the world", then you should stop the server properly. Eval next lines on your REPL.

user=> (.stop @server)
; => nil

user=> (reset! server nil)
; => nil

This will stop the server.

  1. In (.stop @server) , .stop is an instance method and it works literally. @server is a reference to server which has been bound to an atom.
  2. reset changes the state of server to nil again.

Finishing Touches

It's time to summarise the progress so far and write it to a file.

(ns example-clj.core
  (:require [ring.adapter.jetty :as server]))

(defonce server (atom nil))

(defn handler [req]
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body "Close the world, Open the nExt"})

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

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

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

Well, it seems like too long to see at once but meaning of each is quite simple. I'll explain these definitions in order.

  1. (defonce server (atom nil)) sets the state of server using atom. Using defonce prevent from redefining server when you reload the file.
  2. handler is as described earlier. What was previously defined as (fn [req] {:body "Hello, world"}) {:port 3000 :join? false}) is redefined as .
  3. start-server takes no args. when-not is an unless-like macro. If the @server has no value (= nil), the state of server is reset to the result of (server/run-jetty handler {:port 3000 :join? false}).
  4. stop-server takes no args. when is a macro means if .. do... When the @server has some value(= be evaluated "logical-true"), then (.stop @server) is executed. Otherwise Clojure execute (reset! server nil) and the server will stop.
  5. restart-server is just a composite of start-server and stop-server switched by the current value of @server.

Thus you have finished definition. You now can load the file on REPL and evaluate form like this :

user=> (require '[example-clj.core :as e])
;; => nil
user=> (e/start-server)
;; => #object[org.eclipse.jetty.server.Server 0x55b1143a "org.eclipse.jetty.server.Server@55b1143a"]

See http://localhost:3000/ and then your browser is supposed to display the lovely "Hello, World" text.

Summary

Currently, you've learned how to handle the requests and responses of HTTP with Ring. To the next step, you should get used to routing with Compojure, which is the tool to define lucid routing for Ring.

Collection in Clojure

Collection

Clojure における Collection とは,coll? 関数を適用した時に logical truth を返すオブジェクトのことだ.

coll?

Clojure では慣習的に,評価結果が Boolean になる関数は語末に "?" を付ける,という規則がある.すなわち coll? の評価値も Boolean だ.では coll? は何をテストして true/false を決定するのか.端的に言えば,coll? は適用先のオブジェクトが IPersistentCollection を実装しているか否かをテストし,していれば true ,していなければ false と評価する.

IPersistentCollection

Clojure では慣習的に,Java interface オブジェクトに対して,名前の頭に"I"を付ける,という規則がある.すなわち,IPersistentCollection も Java Interface だ.ちなみに Java abstract classes オブジェクトに対しては,名前の頭に "A" を付ける.IPersistentCollection には以下の5つの関数が定義されている.

method process
count element の個数を返す
cons element を追加する
empty empty set 相当の定数を返す
equiv collection 同士の同値性をテストする
seq Sequence を返す

Clojure における Collection は以上の5つをサポートしているオブジェクトとして定義される. Collection はサポートしている性質によって,さらに3つに分類される.

class meaning
Associative 連想配列的な key - value のペア
Counted elements の数が O(1) 時間内に求まる
Sequential elements の再帰処理が可能

数学と自然科学

数学が自然科学かどうか,という問いはしばしば議論される.しばしば議論される,という事実が示しているように,その,しばしば行われる議論は,往々にして一意な結論(らしきもの)を提供しない.一意な結論を提供しない,ということばをここでは,命題化されていない,くらいの意味で使っているが,個人的な直感に照らしても,こうした議論に結論が出る日は来ない気がしている.

しかし,結論が出ない議論を不毛だと切り捨てるのも,それこそ不毛に思う.結論が出ないという文は,結論が出ない,という事実のみを表現していて,議論の過程や,留保されている議題の価値や意味とは直接の関係がない.雑に”結論が出ない”と言ってしまったけれど,これは要するに,結論が出そうにない,あるいは,出ないだろうな,という単なる個人的な予想,予感に過ぎない.同様に,以下に書く内容も,一個人の雑感の域を出ない.

数学は自然科学である,という主張には,率直にいって,僕はどうしても違和感がある.その違和感の正体は,おそらく,これもまったく個人的な感覚としての,自然観,および科学観に由来している.

自然とは何か.ここでいう自然とは,ふつう連想されるような自然環境,生態系,もっと卑近には,相対的に植物の密度が高い場所,という意味では,もちろんない.科学の対象としての自然,自然科学という語の部分としての自然だ.この文脈における自然という単語を適切に言い換えるのはとても難しいのだけど,強いて言えば,人間に考察可能な世界の部分,くらいになるかもしれない.

すごく雑な定義だが,ここでは,人間に考察可能な世界の部分を取り扱うのが自然科学だとしよう.僕が,数学は自然科学である,という主張に対して違和感を覚えるとき,脳みそに引っかかるのは,この辺りだ.数学をしていて,僕は,人間に考察可能な世界の部分,を取り扱っている気がしない.

厳密さに欠ける話を続ける.そしてその厳密さを少しでも補強するため,上の主張にことばを加える.つまり,数学をしていて,僕は,人間に考察可能な世界の部分,を”直接”取り扱っている気がしない.というのも,僕は数学をしているとき,ひたすらことばの問題について考えている.数学的表現の構成,その形式と意味の交わりだけを考えている.

これは,抽象的な思考をしている,というのとは違う.僕は決して賢い人間ではないので,抽象的な思考はかなり苦手だ.なんでも適切な例示ができないと腑に落ちた気になれないし,調べても具合のいい例が出てこなかったら,無理にでもひねりだす.ただ,そうこうしているときに脳みその中にある色々なものを”世界(の部分)”と呼ぶことに,僕は抵抗がある.

それらはナマの世界(の部分)ではなくて,あくまでも世界の表現なのではないか,という気がしてならない.ナマの世界と,世界の表現は異なる.そして,数学の対象は世界の表現である.という直観が,僕にはある.これは直観である以上,間違っているかもしれないし,そもそも何も言えていないかもしれない.もちろん,僕個人が表現としての世界だけを対象に数学をしている,という,とてもありえそうでつまらない事実が見え透いただけかもしれない.

数学の対象は考察可能な世界ではなく,考察可能な世界を考察するための表現なのではないか.数学の発展の歴史というのは,論理という一本槍で,人類がより豊かな表現力を獲得してきた軌跡なのではないか,と僕は思う.いかなる世界の部分をも説明しない,純粋に,単なる表現の塊でしかない,そんな部分が,数学にはたしかに存在する気がする.

そしてそんな数学は,果たして自然科学なのだろうか.ちょっと言い過ぎだと思う.もっと控えめに,形而上学とでも分類しておくのが,個人的には腑に落ちる.

Yコンビネータを読み解く

この記事のお気持ち

Y = (λf . (λx . f (x x)) (λx . f (x x)))
という,この呪文がなんなのか,理解したい.
なぜこの呪文を理解したいのか.それは僕たちが生きているから.
生きてるって素晴らしいから.
そして,Yコンビネータの概念を理解すると再帰についての知見が得られるから.さらには関数型プログラミング言語の論理的背景の主軸であるラムダ計算について一定の知見が得られるから.知見はあの世に持っていけないから.生きているからこそ知見に意味があるから.

Yコンビネータ

結論から言うと,上記の呪文には"Yコンビネータ"という名前がある.
Yってなんだよ.僕は知らない.Yの意味がわからなくても,Yコンビネータが何かはわかる.すごいコンビネータってなんだよ.これは知らないといけない.Yコンビネータコンビネータの一種なので,コンビネータの意味がわからないとYコンビネータの意味もわからない.かなしい

不動点コンビネータ

Yコンビネータにおける”コンビネータ”とは不動点コンビネータのことだ.略称だ.かっこいい.また知らない言葉が出てきた.不動点不動点ってなに.英語でいうと fixed-point.かっこいい.英語はかっこいい.

不動点

不動点というのは数学のことばだ.数学のことばなのでちゃんとした定義がある.ちゃんとしてる定義は形式的な定義だ.

定義1
写像 f と集合Xの要素 x について, f(x) = x が成立し,かつその時に限り,x写像 f不動点である.

これで不動点がちゃんと定義できた.本当はこういう定義を書くとき,一階述語論理の論理式*1で表記するのが定番なんだけど,読者を限定するのがさみしいので日本語で書いた.僕はさみしがりやだから.あと面倒で.
じゃあ,上の定義を具体的に考えてみよう.
あ,でもその前に,またひとつ分かんないことばが出てきた.写像写像(map)ってなに.写像っていうのは,ここではいわゆる”関数”と同一視していい.関数ってことばが分からない人は,ごめんだけど,教科書で調べてきて.以下,写像と関数は素朴に同じものだとして話を進める.
話を戻して,不動点ってものの具体例を見てみよう.定義1より,つまり f(x) = x を満たすような x を考えればいい.
     f(x) = x^2 + 3x - 3
という関数  (f: \mathbb{R}\to\mathbb{R})が与えられたとき,
     f(1) = 1
となって,f(x) = x という条件を満足するから,1 はこの関数 f(x) = x^2 + 3x - 3不動点だ.より直感的に理解するために,グラフを考えてみよう.グラフはいいぞ.かっこいいし.簡単のために,いったん上にあげた具体的な関数のことは忘れよう.別に覚えててもいいけど,混乱しそうだから忘れてもらえると助かる.
定義1.より,不動点というのは f(x) = x を満たす x のことだった.早速これをグラフにしてみよう.慣習に従って,平行軸に x,垂直軸に y をとると,f(x) = x y = x の直線と同一視できる.ここにてきとうな関数 y = g(x) を導入して,重ねあわせてみよう.

それぞれ青とオレンジで描かれた2つのグラフの交点が,関数  g(x) = y不動点だ.

f:id:ksysk:20190211192141j:plain:w300
fixed-point
お,なんとなく不動点っていう概念の片鱗が理解できた気がする.じゃあ,なんで不動点の話をしていたんだっけ.そうだ,不動点コンビネータっていうことばの意味を理解するためだった.不動点の方がうっすら分かってきたから,今度はコンビネータについて考えてみよう.

コンビネータ(1)

英語で書くとcombinator.コンビネータとは,関数だ.とはいえ,関数にもいろいろある.コンビネータとはそんな,いろいろある関数のうちで,引数に関数を取れる関数のことだ.引数に関数を取れる.かっこいい.こういうかっこいい関数のことを高階関数(high-order function)という.たとえば,関数  f(x) があって,f(x)高階関数なら,f(x) は別の関数 g(y) を引数として呼び出していい.この例だと,xg(y) が代入されるわけだから,関数としては f(g(y)) みたいな形になる.もちろん自分自身を呼び出してもいい.すると  f(f(x)) となる.コンビネータとは,要するにこうした高階関数の一種だ.でも,コンビネータ高階関数とまったく同じものだったら,わざわざ名前をつけたりしない.名前がついてるということは,区別したいからだ.区別に意味があるから別の名前が必要になる.じゃあ,コンビネータはどんなふうに高階関数一般と区別されているんだろう.ここで定義をみてみよう.

定義2.1
関数  c高階関数であり,かつ, c に自由変数が含まれないとき,関数  cコンビネータである.
定義2.2
高階関数  cコンビネータ,または,引数のみからなる関数適用によってその結果が定義されるとき, cコンビネータである.
定義2.3
定義2.1および定義2.2によって定義される任意の関数はコンビネータである.

なるほど.こうやってコンビネータの定義をじっくり読んでいると,いくつか気になることがある.まず,定義2.1より,コンビネータ高階関数の部分集合だってことは分かった.だけど,自由変数ってなんだ.変数に自由とか不自由とかがあるのか.現代人じゃあるまいし.それと,もっと不思議なのが定義2.2だ."コンビネータ"の定義をしているのに,定義の記述の中にまた"コンビネータ"っていうことばが出てきている.詭弁の匂いがする.なんだか誤魔化されている気がする.そして気になる定義2.3の存在.なんの意味があるんだ.実はこれは立派な定義になっていて,定義2.1と定義2.2を併せてちゃんと読むと,”落としどころ"が見えてくる.すると,定義2.3が味わい深い,小粋な仕事をしていることも分かる.普通,こういう定義の方法を再帰的定義(recursive definition)と言ったりする.たぶん高校生の頃,数学的帰納法*2によってある命題が任意の自然数について成り立つ様子を見たと思う.それに似ている気がしないかな.見覚えがない人は,たびたびごめんだけど,教科書で調べてきて.

ともかく,コンビネータの定義を理解するためには,定義2.1で出てきた”自由変数”の意味が分からないといけない,ことは分かった.どうやら,変数にも種類がある.名前がついているんだから,そこには区別がある.そしてその区別はきっと有意味なはずだ.無意味な名前はいずれ消えていく*3

自由変数 - 束縛変数

形式言語の世界では,変数を自由変数束縛変数という2種類に分類して考える.形式言語というのは,自然言語ではなく,かつ,その文法が形式的(≒論理学的)に決まっている言語のことをいう.数学のことばやプログラミング言語形式言語だ.変数,ということばの中で”数”という字を使っているけれど,別に変数は数でなくても構わない.数でなくても構わないことを強調したいとき,日本語では変項ということばを使う.英語ではいずれの訳語も”variable”だ.以下では簡単のために変数という表記に統一する.
先に書いたように,自由変数(free variable)と束縛変数(bound variable)は形式言語の構成要素だ.形式言語はその文法が形式的,論理学的に決まっているのだから,これらの変数についてもその用法は形式的,論理学的に決まっている.ということで,定義のお時間です.

定義3.1
X の要素 x が自由変数であるとき,x を含む文 P(x) は述語である.
定義3.2
X の要素 x が束縛変数であるとき,x を含む文 P(x) は命題である.
定義3.3
x が自由変数であるとき,x は束縛変数ではない.
定義3.4
x が束縛変数であるとき,x は自由変数ではない.

また知らんことばが出てきた.述語とか命題とか.ここで,つらつら述語と命題の違いを説明してもいいんだけど,定義を見る限り"述語 - 自由変数" ,”命題 - 束縛変数" という対応関係はかなり綺麗だ.自由変数の意味が分かれば述語の意味も分かりそうだし,束縛変数の意味がわかれば命題の意味もわかりそう.じゃあ,この対応を利用して,具体例の方からそれぞれのことばの意味に迫っていこう.

前提
x は実数とする.
例1
x < 5
例2
すべてのxについてx < 5

例1 は自由変数 x についての述語だ.x には好きな数を代入できる.たとえば x = 4649 とすると,これは 4649 < 5 という偽の命題になる.例1 の段階での P(x) = x < 5 は真/偽(=真理値)が決まっていない”述語”だった. 4649 を代入したことでできた  P(4649) は,4649 < 5 となり,偽の真理値をもつ”命題”になった.もちろん,P(5) だったら真理値は真だ.自由変数の”自由”というのは,値を代入する自由のことだったらしい.そして,”自由に代入していい部分(=自由変数)をもつ(有意味な)文”のことを,述語と呼ぶことも分かった.ここで定義3.3,定義3.4を引っ張ってくると,命題とは”自由に代入していい部分をもたない(有意味な)文”ということになりそうだ.ここまでの文脈を汲むと,自由に代入していい部分を持たない文は命題と呼ばれ,真理値なる値をもつ.真理値には(True),(False)の2種類があり,かつ,2種類しかない.

例2 は,一方,x についての命題だ.このような文 P'(x)を構成する x のことを束縛変数という.このようなってどのようなだよ.見てのとおり,例2 における x は単に見かけの変数で,自由な代入は許されていない."すべての x について" という表現に束縛されていて,x は身動きが取れない.試しに P'(7) と代入してみるとこれは,”すべての7について7 < 5”というヤバい文になってしまう.直前の文 P'(7) の真理値が自明に偽である以上,文 P'(x) は代入した値の影響を受けず,最初から偽な命題だったということになる.

こうしてみると,ふつうの意味で”変数”ということばを使うとき,意図されているのは自由変数のことだと気がつく.たとえばプログラミングではふつう,代入可能な値についてのみ変数と呼ぶ.だからプログラミングで変数といえばやっぱり自由変数のことだ.自由と束縛.理論的には極めて妥当に思える変数についてのこうした命名だけど,実装の方から解釈するとどうにも不自然でかなり混乱する.その理由は,後ほど,ラムダ計算という概念を導入したときにでも説明しようかなと思っている*4

よしよし,変数を自由変数と束縛変数とに区別して考えるのは分かった.なんで区別するのか,区別するとどう嬉しいのかはまだ分からないけど,区別が必要なことまでは分かった.そもそもなんで変数の区別について考えていたんだっけ.それは,コンビネータの定義2.1に自由変数ということばが登場したからだった.コンビネータの定義をそれぞれ見直してみよう.文の中に知らないことばがなくなって,ちゃんと意味が分かるようになった.やったぜ

コンビネータ(2)

あえて言及しなかったんだけど,コンビネータ(1)の”(1)”,たぶん気になりましたよね.見出しをご覧ください.つまりそういうことです.ということで,自由変数の意味を押さえた上で,もう一度,定義2.1を確認してみる.

定義2.1
関数  c高階関数であり,かつ, c に自由変数が含まれないとき,関数  cコンビネータである.

これをほぐすと,おおよそ以下のような日本語になる.

定義2.1.1
関数  c関数を引数に取れる関数であり,かつ, c自由に対象を代入できる変数が含まれないとき,関数  cコンビネータである.

おお,かなり見通しが良くなった.では次に,定義2.1とその同値変形である定義2.1.1を踏まえて,定義2.2をほぐしていこう.

定義2.2
高階関数  cコンビネータ,または,引数のみからなる関数適用によってその結果が定義されるとき, cコンビネータである.

定義2.2の下線部は,引数を変数の部分だと解釈すると,以下のように変形できる.

定義2.2.1
高階関数  cコンビネータ,または,自由変数ではない変数のみからなる関数適用によってその結果が定義されるとき, cコンビネータである.

定義3.4よりこの文はあきらかに,

定義2.2.2
高階関数  cコンビネータ,または,束縛変数のみからなる関数適用によってその結果が定義されるとき, cコンビネータである.

という文と同値となる.定義しといてよかった束縛変数.さて,ここまでに揃った定義を使って,コンビネータを再定義しておこう.

定義2.1.1
関数  c関数を引数に取れる関数であり,かつ, c自由に対象を代入できる変数が含まれないとき,関数  cコンビネータである.
定義2.2.2
高階関数  cコンビネータ,または,束縛変数のみからなる関数適用によってその結果が定義されるとき, cコンビネータである.

ふむ,なんだかいけそうな気がしてきた.何がとはいえないが,何かがいけそうな気がしてきた.さあ,コンビネータの定義が腑に落ちてきたところで,じゃあなんのためにコンビネータについて思い巡らせてきたのかを思い出したい.思い出した不動点コンビネータが何かを分かりたかったんだ.ほほう,”不動点/コンビネータ”とな.不動点についてはさっき定義したし,コンビネータについてはいま定義した.いけそうな気がする.無性にいけそうなこの気持ちを抱いて,さあ,次は不動点コンビネータを定義していこう.

*1: f:X \to X において  (\exists x ∈ X) ( f(x) = x) となる点 x

*2:ちなみに数学的帰納法はおもいっきり演繹論理なので気をつけて.罠だ!! 大人は汚い!!

*3:もし記述されたプログラムの中に無意味な名前 ––つまり名前として意味不明なものや,名前に役割がないもの–– がたくさんあったら,それらはぜんぶ消されるべきものだ.なぜって,それらは単に無意味なだけでなく,しばしば有害だから.

*4:こうやって書いておかないと説明し忘れそうだから,メモ

おためし,はてな記法

見出しだぞい

これで小見出しだぞい

  • リスト
    • 小リスト
定義リスト
こんな感じかな
定義リスト
あんな感じかな

コードの埋め込み(Haskell)

collatz :: Integer -> Maybe Integer
collatz n = helper n 0
  where
     helper :: Integer -> Integer -> Maybe Integer
     helper n count
	    | n == 0 || n < 0 = Nothing
	    | n == 1 	      = Just count
	    | even n 	      = helper (div n 2) (count + 1)
	    | otherwise       = helper ((3*n) + 1) (count + 1) 

なるほどな.

コードの埋め込み(Scheme)

(define (factorial x)
  (let loop ((x x)
             (acc 1))
    (if (zero? x)
        acc
        (loop (sub1 x) (* x acc)))))

schemeシンタックスハイライト,配色可愛い.萌え.

という訳で,ここはおべんつよ系の雑文置き場になりました.