体の各部の比率 (大雑把) なメモ
身長を 10 として、
胴体 3
太もも + ふくらはぎで 5 (あとかかとの分もある、でもまだ、かかとはよくわからない)
人間を上下に等分割すると、ちょうど下腹部くらいが境目になる。
ウィトルウィウス的人体図 にはこのあたりの情報が異常に詳しく描かれているらしくとても参考になるっぽい。
@ Clojure ガチ勢#() マクロで 次のような関数は作れますか(fn [& xs] (apply f xs)) ; #(f %*) などと書きたい(fn [x] (fn [y] (f x y))) ; constantly みたいに関数を返したい
— パスベルス・スカーレット (@pasberth)私 Scala な人ですが,Clojure ガチ勢の召喚に期待して書いてみます.
その 1
user=> (def f1 (fn [& xs] (apply + xs))) #'user/f1 user=> (f1 1 2 3...
GHC 7.6.3 を Mac にインストールしようとしたのだが、いろいろとはまった。 こんな感じ:
ぼくは Mac に Gentoo Prefix をインストールして使っているのだが、どうも libiconv がコンフリクトしているみたいなのだ。
/usr/lib にあるものと、 $EPREFIX/usr/lib にあるものが同名になっている。
ghc でコンパイルする時は、 -L オプションを指定すればうまく通った。
cabal の場合、 --ghc-options にライブラリのパスを指定してやれば解決した。
つまり
$ cabal install hoge
などとするときに
$ cabal install hoge --ghc-options=-L/usr/lib
などとする。
モナドといえば Haskell 。他の言語でもモナドライブラリはいろいろとあるのですが、イマイチ使いやすいと思えない。
これはやっぱり >>= を使って束縛するとネストしまくるのがアレだと思うのです。でも、これを、do記法なしでネストさせずに書くことができる事に気づきました。
あるモナド m と k について、 中の値 x y を束縛しつつアレするのを 単純に書くとこのようになります:
(>>= m (fn [x]
(>>= k (fn [y]
do-something..))))
これはとても書きにくいし、構文にしろ、なにか違う書き方がしたいものです。
ここで product が使えます。 (product m k f) はこのように定義できます:
(defn product [m k f]
(>>= m (fn [x] (>>= k (fn [y] (f x y))))))
Haskell ではこうです:
ここで product 関数を使ってこのように書くことができます:
(product m k (fn [x y] do-something..))
ずいぶんと書きやすくなりました。
当然ですが、これは Maybe とかにも適用できる一般的な関数となります。つまり product は リストに対して適用すると for マクロのように働くし、 maybe に対して適用すれば すべてが Just の場合だけ適用する関数のように働きます。
Clojure or other lisp な言語でモナドをする場合、 return をどう定義するか と悩んでいる人は多いと思います。 これの問題点を整理すると:
です。 Haskell には、たとえば read 関数など、引数では型を決定できない関数がいろいろとあります。
Haskell では、これを型による分岐 (型クラス) でうまく表現する事ができます。つまり:
read "1" :: Int
1
read "1" :: String
"*** Exception: Prelude.read: no parse
のように、型を指定して分岐するわけです。 今回は明示しましたが、実際には、戻り値型や、他の引数の関数の型によって推論されるので、明示しない事もあります。
動的言語の場合、型クラスは Object 指向おける mixin でうまく表現できる事は http://rb.blog.pasberth.com/post/49952066672 この記事で説明しました。
ということは、同じこと — つまり オブジェクト指向 — を Clojure にも実装してやれば、うまくできるはずです。
原則として、 return や guard といった関数は、引数を明示しなければならないものとします。
結局のところ、 (return option 42) のように書けばすべての問題は解決します。でも、その書き方はキモいし、 mzero とか、 guard とか、いちいち指定していたらやってられません。だから省略できるようにしてやればいいわけです。
ちなみに、 return の表現としては、マクロを使用して unit に束縛済みの unit を束縛したりする方法もあります。でも、そうすると guard の実装のためにも unit を束縛しつつ guard を定義する defmonadfn みたいなマクロを定義する必要があります。それはあまりにも面倒だし、 Applicative とかとの連携を考えるといい方法とは思いません。
それで、いろいろ考えていて思った事は、動的型づけに、少なくとも文脈としてのオブジェクトは必要不可欠だという事です。 静的型づけだと、型がその代わりになりますが、動的型かつ関数型であると、文脈が常に同じになってしまうので、この手の関数を定義するときに問題が起こります。
ちなみに、Clojureには潜在的にそのような関数は存在します。たとえば empty 関数は空のリストを返す関数です。これはMonadPlus の mzero とか Applicative の empty のような関数に相当する関数だと考えられますが、実際に Clojure の場合は無意味な引数をひとつ指定する必要があります。
非常に簡単に定義できるけど、いちおうブログにしておくことにした。
こんな感じ。すると (cfn [a b c] x) とかするとカリー化された関数になる。(f r) などと 引数をひとつ与えると a が部分適用された関数が返されるし、 (f r k) とふたつ与えると a と b が部分適用された関数が返される。
Clojure でモナド的型クラスを模倣する方法は http://rb.blog.pasberth.com/post/49691550419/clojure-protocol で述べたが、これはとても一般的ではなく限定的な範囲にしか適用できない。しかし、これでしていることを一般化する方法について。
先のブログでは maybe-context という関数を作っていたけど、この中で instance? によって分岐している部分がある。まずはこれを protocol にしよう。つまり、 TypeClass というprotocolを作るのだ。
instance? による分岐は代わりに protocl による分岐になり、ループは再帰になる。
具体的な実装は https://github.com/pasberth/granjure/blob/30e76a57fb8ed783514fda2f2ae65a6908ef6d19/src/granjure/control.clj などを見てほしい。
次に、 (return a) のような関数を、 (Unit. a) のようなレコードとして定義しよう。そしてこれを TypeClass protocol の実装とする。具体的には https://github.com/pasberth/granjure/blob/30e76a57fb8ed783514fda2f2ae65a6908ef6d19/src/granjure/control/monad.clj#L26 このような実装になる。
return は単に Unit を返すだけだから、もちろんこれは実際のデータとして利用することはできない。これを specialize 関数を持って変換するわけだけど、明示しないで変換するのは簡単だ。これを具体的にデータに直すタイミングというのが存在するのだ。たとえば (>>= (Just. 42) (fn [a] (return a))) としたら、 return a は Unit を返すわけだが、 >>= 関数は (Just. 42) という値を知っているので、これをもって Unit を具体的にできる。だから >>= を呼んだ瞬間 Unit は Just に変換され、コンテキストを明示していないかのように振る舞うのだ。
しかも specialize は TypeClass protocol で分岐できるので、 https://github.com/pasberth/granjure/blob/30e76a57fb8ed783514fda2f2ae65a6908ef6d19/src/granjure/control/applicative.clj#L21 たとえばこんなふうに、いくらでもコンテキストを追加できる。これで pure 関数も実装できた。 (>>= (Just. 42) (fn [a] (pure a))) も同じように変換されるのだ。
そして Unit とかは単なるデータだから、多態な関数の実装も簡単だ。 https://github.com/pasberth/granjure/blob/30e76a57fb8ed783514fda2f2ae65a6908ef6d19/src/granjure/control/monad.clj#L45 たとえばこんなふうに実装できる。驚くべきことに、 map-m は f が返した値によって分岐するのだ。もちろん、 Nothing を返したらその時点で Nothing が帰る。しかも Unit を返したらそれがさらに多態な関数を生み出すのだ。
Haskell などの言語には、 型クラスという便利なシステムがある。 型クラスというのは、簡単にいうとコンテキストによる関数の選択のような機能だ。
しかし Clojure でプログラミングしているうちに、 Clojure では型クラスに似たことをするのがたいへんだという事実に気づいてしまった。たとえば モナドの return だとか、そういった関数を定義するのが難しいのだ。 Clojure には protocol という仕組みがあるけど、 それは 第一引数で選択される。 multimethod という仕組みもあるけど、引数で選択される以上問題の解決にはならない。むしろ、たとえば引数のシグネチャによって選択されるような仕組みが欲しかった。つまり f :: [a] -> a みたいな関数を (f (return a)) のように呼び出した場合に、 return :: a -> [a] が選択されるような。
これは Clojure が動的な言語に起因する、と思ったけど、実は Ruby では型クラス相当のことが比較的簡単にできることに気づいた。
つまり、まず型クラスである Monad を module として宣言する。それから、 それを include したクラスを List とし、 return とか bind とかを定義する。そして、あらゆる Monad に対して機能する関数は、 Monad のメソッドとして実装する。 Ruby ではレシーバは省略可能だから、 mzero とかをさも関数かのように、 かつレシーバに依って選択されるように振る舞わせることができる。
これを Clojure ですると、まず引数の選択ができるように第一引数を単に無視するような関数を持つ protocol を作って、なにかラッパを噛ませてそれに渡すのだけど、これは結局オブジェクト指向としていることは同じだった。
実は、静的な関数型言語における型というのは、コンテキストの振る舞いを定義するオブジェクトに似たようなものだと気づいた。つまり、型というのは実は暗黙のレシーバなのだ。
f :: a -> [a]
のように定義するのは、実は動的なオブジェクト指向言語の
module [a]
def f; end
end
に似た行為であって、グローバルな環境にfを宣言しているわけではなかったのだ。
逆に言うと、これを参考にマクロを定義すれば、型クラス相当の機能を持つ関数も定義可能ということになる。ただし、たぶん、呼び出し側でもマクロを使ってコンテキストを設定する必要があるだろう
Clojure の protocol でモナドを定義したい!! そう思ったけれど、 protocol の仕組み上 return の定義がけっこう難しいのである。
なぜかというと protocol という仕組みは第一引数に依る分岐なので、型クラスのように型に依る分岐ではないからである
しかしこれをいい感じに解決する構造を考えた。
まず、 MonadUnit と MonadBind 、 そしてそれらを対応するデータ型に変換する monad-context 関数を持つ Monad protocol を定義する。
たとえば (maybe-context (MonadUnit. 1)) などとすると (Just. 1) が返されるわけである
それから、 return a は単に (MonadUnit. a) とする。
そして m >>= k は、もし m が具体的なデータ(つまり、MonadUnit ではない Just などのデータ )であった場合に (MonadBind. m k) を monad-context をもって具体的なデータに変換して返し、そうでない抽象的なデータの場合、 (MonadBind. m k) を返すとする。
すると >>= は、たとえば (>>= (return 42) return) は (MonadBind. (MonadUnit. 42) k) を返すが、 (>>= (Just. 42) return) の場合は (Just. 42) を返すように働く。
つまり、データが具体的になったタイミングで評価して、それまでは抽象的なデータだけをやり取りする。
すると return や >>= だけをもって、すべてのモナドに対して適用可能な関数を簡単に定義することができる。たとえば (return 42) はすべてのモナドに対して適用できるモナドになる。
もし、 (return 42) を (Just. 42) として扱いたいなら、 maybe-context に明示的に与えてやる必要がある。でも、それは Haskell でも同じことで、それはちょうど型を明示することに似ている。
コツコツいろんな言語試しては echo コマンド実装したりしてる。 いつもはブログにしないけどまとめてブログにしておく
あとなんかDとかScalaとかアセンブリとかでこんなのいろいろしてみてたのでgist漁ろうと思ったけど多すぎて挫折したアレ