2012年9月13日木曜日

ClojureScriptのテスト

皆さんテスト書いてますか?
私はあまり書いてません。
テストは大事です、書きましょう。

私は現在ClojureScriptを使っているのですが、JavaScriptでのテスト方法とかよくわからないし、Clojureでテスト書きたいので、ClojureからClojureScriptを読み込もうという考えに至りました。

が、しかし。
requireなどを使って、ClojureScriptのコードを読み込むことはできませんでした。
なぜなら拡張子がcljsだから。
load-fileなども使ってみましたが、無理でした。
ClojureScriptのコードの中でrequireを使っているからです。

どうにかして拡張子がclj,classのファイル以外を読み込むことはできないのかと調べてみたところ・・・・

https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L403

ベタ書き・・・!
差し替えることなど到底無理っぽい。

ならば作るしかありません、cljファイルを。


copyしてuseしてdeleteする、悲しみあふれるコード。
cleanupのところはtry-finallyした方が良いかも。

clojure.testはfailした時にマクロを利用して述語に渡した値を展開してくれたりでなかなか便利なので割と好きです。
しかし、ここまでして使うのもどうかなと。

素直にJavaScriptのテストフレームワークを使うとか、assertで済ませる(ClojureScript本体のテスト方法)などでいいかもしれません。

2012年9月10日月曜日

Scalaでdo記法

函数プログラミングの集いで限定継続の話が面白かったので書いておきます。

ScalaでもHaskellのdo記法っぽく書ける。

調度良くScalaでHaskellしてる記事があったので、これを参考にさせていただきます。

\めんどい/

2012年9月5日水曜日

Clojureのinsert

Clojureでlistやvectorのn番目に値をinsertしたい時はありませんか?
Clojureには標準でlistやvectorに対するinsertが定義されていません。
でもzipperにはinsertがあるみたい。
そこでvectorとzipper、どちらのinsertが速いか測ってみました。

コード


結果


ClojureScript

Clojure

思っていたよりは差がつかなかった。

vectorやlistはいつもこんな感じでinsertしてるのですが、もっと綺麗で効率のよい方法があったら教えて下さい。

効率のよいinsertが出来るシーケンスを標準で用意してくれてもいいのではと思わなくもないです。

2012年9月1日土曜日

Machines

先日rpscalaに行った時、@xuwei_kさんにscala-machinesというものを教えてもらったので調べてみました。

Example.scalaを参考に、コードを読んでいきます。

少々書き換えました。

ぱっと見ても全くわからないですね。

IO


副作用(アクション)を表します。
IO.applyは値をcall by nameでとります。
unsafePerformIOを呼ぶことで、アクションを実行します。

bufferFile


ファイルをとってBufferedReaderを返す関数です。
IOでラップしています。

rReadLn


BufferedReaderから一行読み取ります。
readLineは文字列かnullを返すのでOption.applyを使用しています。
これもIOでラップしています。

closeReader


Readerを閉じます。
当然副作用なのでIOでラップされます。

Process


Process[I, O]はI => Oをラップしたものと考えていい・・・・はず。
Process[I, O]はMachine[I => Any, O]のシノニムで、Machine[K, O]はPlan[K, O, Nothing]のシノニムなので、実体はPlanなわけですが、まだ私は理解できていません。
IterateeのIteratee,Enumeratee,Enumeratorの内のIterateeにあたるようなものだと思われます。
なのでストリームの要素はこいつを使って処理していきます。

countLn


文字列をとって1を返すProcessです。

getFileLines


型パラメータAとProcess[String, A]とFileをとり、Procedure[IO, A]を返します。
モナド変換子と同じく、Procedureから値Aを取り出す時にIOにラップされるようになります。
ProcedureはEnumeratorのようなもので、ストリームとそれを処理するMachineを持ちます。
ProcedureはMachineの第一型パラメータとMachine、抽象メソッドのwithDriverを定義します。
Process[I, O]は先述の通りMachine[I => Any, O]と定義されているので、抽象タイプメンバにはString => Any、Machineは引数としてとったProcessで定義します。
Driverでは入力を定義するのですが、当然、入力にはリソースが必要になります。
そこで、このwithDriverではリソースの作成、開放、Driver(入力)の作成、Driverの適用を定義します。
リソースの作成はbufferedReaderを使います。
IO#bracket(f)(g)は処理gの後に必ず処理fが処理されます。
事後処理としてcloseReaderを渡し、Driverの作成と適用をします。
DriverにはrReadLnを使い、入力を適用するapplyを定義します。
最後にwithDriverがとる関数kにDriverを適用します。

exec


ファイルを関数に適用し、実行、出力する関数です。
executeはProcedureの結果の型Aがモノイドである必要があります。
ストリームを処理した結果をaccumulatorに加える関数で合成していきます。

runc


main関数で呼ばれる関数で、裏でunsafePerformIOを呼びます。

CSVファイルの読み込みと要素のカウントを書いてみた。

まとめ


IterateeよりはIOに特化していると思われる。
リソースの処理をProcedureでやっているところとか。
ProcessやProcedureを自分で書くのは、IterateeやEnumeratorを自分で書くよりは楽・・・・な気がしなくもない。

今回は本当にExampleをさわっただけで、詳細はわかっていない。
作者のドキュメントに期待です。