<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3816569039331410742</id><updated>2011-12-08T05:00:11.991+09:00</updated><category term='install'/><category term='ubuntu'/><category term='tokyocabinet'/><title type='text'>Clojureで行こう</title><subtitle type='html'>ClojureとScalaを勉強しつつメモを。</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>18</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-2758102552684520509</id><published>2011-12-07T11:55:00.003+09:00</published><updated>2011-12-08T05:00:12.002+09:00</updated><title type='text'>Play Framework 2.0 と JSON ライブラリ Jerksonについて調べてみました。</title><content type='html'>この記事は&lt;a href="http://atnd.org/events/22247"&gt;Play! framework Advent Calendar 2011 jp #play_ja&lt;/a&gt;&lt;br /&gt;の7日目です。&lt;br /&gt;勢いで参加してみたものの、ネタが見つからず困っていたところ、@mumoshu さんのつぶやきが目にとまりました。&lt;br /&gt;&lt;br /&gt;&lt;!-- QuoteURL styled embed start --&gt; &lt;blockquote class="quoteurl-block" style="margin:0;padding:0;"&gt; &lt;ol class="quoteurl-quote" style="background-color:#fff;color:#000;padding:.4em;border:1px solid #888;-moz-border-radius: .5em;border-radius: .5em;width:90%;max-width:700px;margin:auto;"&gt; &lt;li class="hentry status u-mumoshu" style="clear:both;list-style:none;padding-top:.7em;padding-bottom:.7em;border-top:1px dashed #ccc;position:relative;background-color:#fff;"&gt; &lt;div class="thumb vcard author" style="float:left;margin-right:1em;margin-left:.5em;"&gt; &lt;a class="url" href="http://twitter.com/mumoshu"&gt;&lt;img width="48" height="48" style="border:none;" src="http://a0.twimg.com/profile_images/1662768704/dae8bfa8-e289-46b4-9424-843f9a914330_normal.png" class="photo fn" alt="むもしゅ" /&gt;&lt;/a&gt; &lt;/div&gt; &lt;div class="status-body" style="margin-right:30px;padding-right:1em;"&gt; &lt;a class="author" style="font-weight:bold;" title="むもしゅ" href="http://twitter.com/mumoshu"&gt;mumoshu&lt;/a&gt; &lt;span class="entry-content" style="font-style:normal"&gt;この２日間のPlay 2.0は、Scala向けのJSON APIが変更されたり、Actionの合成方法がドキュメントにそった形になった。JSONの方は、使うライブラリがsjsonからJerksonへ変更。Jerksonをベースに、sjsonのようなAPIを実装している。&lt;/span&gt; &lt;span class="meta entry-meta" style="color:#888;font-family:georgia;font-size:0.8em;font-style:italic;"&gt; &lt;a rel="bookmark" class="entry-date" style="color:#888;text-decoration:none;" href="http://twitter.com/mumoshu/status/143316801227468800" onmouseover="this.style.textDecoration='underline';" onmouseout="this.style.textDecoration='none';"&gt; &lt;span title="2011-12-04 13:12:42" class="published"&gt;04 Dec 2011&lt;/span&gt; &lt;/a&gt; &lt;span&gt;from &lt;a href="http://itunes.apple.com/us/app/twitter/id409789998?mt=12" rel="nofollow"&gt;Twitter for Mac&lt;/a&gt;&lt;/span&gt; &lt;/span&gt; &lt;/div&gt; &lt;div class="actions" style="position:relative;clear:both;"&gt;&lt;/div&gt; &lt;/li&gt; &lt;/ol&gt; &lt;/blockquote&gt;&lt;small class="quoteurl-cite" style="float:right;"&gt; -- &lt;a href="http://www.quoteurl.com/whk8k"&gt;this quote&lt;/a&gt; was brought to you by &lt;a href="http://www.quoteurl.com/"&gt;quoteurl&lt;/a&gt;&lt;/small&gt;   &lt;!-- QuoteURL embed end --&gt;&lt;br /&gt;&lt;br /&gt;Jerksonというライブラリは全く知りませんでした。&lt;br /&gt;調度良いのでこのJerksonについて調べたことを書こうと思います。&lt;br /&gt;（Play20のplay.api.jsonは、現在絶賛開発中といった感じなので、これからしばらく不安定な様子です。&lt;br /&gt;おそらくここに書くこともすぐに古くなると思いますのでご了承ください。）&lt;br /&gt;&lt;h2&gt;Jerksonって？&lt;/h2&gt;&lt;br /&gt;&lt;a href="https://github.com/codahale/jerkson"&gt;Jerkson&lt;/a&gt;は、Java JSON-processorであるところの&lt;a href="http://jackson.codehaus.org/"&gt;Jackson&lt;/a&gt;の、Scala wrapperです。&lt;br /&gt;&lt;br /&gt;これまで候補であったsjsonについては@eed3si9n さんの翻訳記事、&lt;br /&gt;&lt;a href="http://eed3si9n.com/ja/node/17" &gt;sjson: Scala の型クラスによる JSON シリアライゼーション&lt;/a&gt; に詳しく書かれています。sjsonは、implicit parameterによる型クラスエミュレートを使った便利なライブラリです。sjsonでは、型クラスを使うことでシリアライズ、デシリアライズプロトコルのカスタマイズ手段を提供しています。&lt;br /&gt;sjsonからJackson/Jerksonに移行した理由はわかりませんが、ScalaとJavaを両方サポートするPlay!ではJavaベースのJacksonとそのScala wrapperというのが都合が良かったのかもしれません。&lt;br /&gt;play.api.jsonでは、このJerksonに、sjsonライクなシリアライズ／デシリアライズプロトコルをかぶせた形になっています。&lt;br /&gt;apiとしては、まずJsValue型とStringの相互変換をする２つの関数が用意されています。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;def parseJson(input: String): JsValue  = JerksonJson.parse[JsValue](input) &lt;br /&gt;def stringify(json: JsValue): String = JerksonJson.generate(json)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;parseJsonは、StringからJsValueへ、stringfyはその逆で、JsValueをStringにします。&lt;br /&gt;これらの中身はJerksonにそのままお任せで、特にカスタマイズも想定されていますん。&lt;br /&gt;JsValue という型が中間表現になっていて、任意のScalaオブジェクトをJsValueにしてからStringにしたり、&lt;br /&gt;JSON文字列をパースしてまずはJsValueにしてから、任意のScalaオブジェクトに変換することになります。&lt;br /&gt;ちなみにJsValueという型は次のような構成になっています。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;sealed trait JsValue {...}&lt;br /&gt;case object JsNull extends JsValue {...}&lt;br /&gt;case class JsUndefined(error: String) extends JsValue {...}&lt;br /&gt;case class JsBoolean(override val value: Boolean) extends JsValue&lt;br /&gt;case class JsNumber(override val value: BigDecimal) extends JsValue&lt;br /&gt;case class JsString(override val value: String) extends JsValue&lt;br /&gt;case class JsArray(override val value: List[JsValue]) extends JsValue {...}&lt;br /&gt;case class JsObject(override val value: Map[String, JsValue]) extends JsValue  {...}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ベタにJSONのデータ構造そのままですね。&lt;br /&gt;それぞれcase classなので、このままでも使うことができますが、ちょっと不便です。&lt;br /&gt;そこで、任意の型との相互変換を行う関数として、次の２つが用意されています。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue = tjs.writes(o)&lt;br /&gt;def fromJson[T](json: JsValue)(implicit fjs: Reads[T]): T = fjs.reads(o)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Writes[T]がT型からJsValueへの変換を、&lt;br /&gt;Reads[T]がJsValueからT型への変換を提供するオブジェクトの型になっています。&lt;br /&gt;それぞれimplicitとなっているので、&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;import play.api.json.Reads._&lt;br /&gt;import play.api.json.Writes._&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;などとしておけば、&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;assert (toJson(Map("a"-&gt;2)).toString =="""{"a":2.0}""")&lt;br /&gt;assert (fromJson[Int](parseJson("1")) == 1)&lt;br /&gt;assert (fromJson[Map[String,SortedSet[Int]]](parseJson("""{"a":[1,5,3]}""")) == Map("a" -&gt; SortedSet(1,3,5)))&lt;br /&gt;assert (fromJson[Map[String,List[Int]]](parseJson("""{"a":[1,5,3]}""")) == Map("a" -&gt; List(1,5,3)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;といった形で、よくある感じの型にはデフォルトで変換プロトコルが提供されていて、&lt;br /&gt;簡単に使うことができます。&lt;br /&gt;ちなみにWrites[T] とReads[T]はこんな感じです。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;trait Writes[T] {&lt;br /&gt;  def writes(o: T): JsValue                                                                                                                                                                         &lt;br /&gt;}&lt;br /&gt;trait Reads[T] {&lt;br /&gt;  def reads(json: JsValue): T                                                                                                                                                                       &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;（Writes Readsの他に次のようなFormatという型も用意されています。おそらく今後これを実装したobjectが提供されるんだと思います。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;trait Format[T] extends Writes[T] with Reads[T]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;）&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;カスタム変換プロトコル&lt;/h2&gt;&lt;br /&gt;次に自分の好きな変換を提供する方法について考えてみます。&lt;br /&gt;まずは、Reads[T]の実装をみてみます。&lt;br /&gt;シンプルな例としてReads[Boolean]は次のようになっています。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;implicit object BooleanReads extends Reads[Boolean] {&lt;br /&gt;  def reads(json: JsValue) = json match {&lt;br /&gt;    case JsBoolean(b) =&gt; b&lt;br /&gt;    case _ =&gt; throw new RuntimeException("Boolean expected")&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;def reads(json: JsValue):Boolean&lt;br /&gt;な型のメソッドを持つReads[Boolean]なオブジェクトをimplicitで提供するだけですね。&lt;br /&gt;変換したい型が、List[T]などのパラメータ化された型の場合はもう少し複雑になります。&lt;br /&gt;List[Int]、List[Boolean]などについて個別にReads[List[Int]]などを用意していけば、&lt;br /&gt;Reads[Boolean]と同じ形に書けますが、これだとList[List[List[Int]]]などのネストを考えると&lt;br /&gt;事前にすべて用意する訳にはいかなそうです。&lt;br /&gt;そこで、Tがなにかはわからないけれど、T（例えばIntだったりする何か）について変換する方法を&lt;br /&gt;教えてくれれば、List[T]の変換方法を教えましょう、という形の関数を書くことになります。&lt;br /&gt;Tの変換方法とはつまりReads[T]型のオブジェクトで、&lt;br /&gt;List[T]の変換方法とはReads[List[T]]型のオブジェクトです。&lt;br /&gt;なので型でいうと、Reads[T]型のオブジェクトを受け取って、Reads[List[T]]型のオブジェクトを返す関数を&lt;br /&gt;書くことになります。実際のソースの該当部分は次のようになります。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;implicit def listReads[T](implicit fmt: Reads[T]): Reads[List[T]] = new Reads[List[T]] {&lt;br /&gt;  def reads(json: JsValue) = json match {&lt;br /&gt;    case JsArray(ts) =&gt; ts.map(t =&gt; fromJson(t)(fmt))&lt;br /&gt;    case _ =&gt; throw new RuntimeException("List expected")&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ここで、implicit fmt: Reads[T]と、Reads[T]型の引数についてもimplicitが指定してあるので、&lt;br /&gt;例えば先ほどのimplicit object BooleanReads extends Reads[Boolean] &lt;br /&gt;などが提供されていれば、listReadsにBooleanReadsが暗黙に自動的に供給されて、&lt;br /&gt;Reads[List[Boolean]] も自動的に供給されることになります。&lt;br /&gt;この仕組みを使って、ここでは試しにReads[Either[A, B]]な変換方法の提供を考えてみたいと思います。&lt;br /&gt;JSONは基本動的型付けなので、[1, 2, null]のように、&lt;br /&gt;ひとつの配列の中に様々な型の値が一緒に入っていることがあります。&lt;br /&gt;連想配列であれば、違うキーに対しては、むしろ別の型の値が入っていることの方が普通でしょう。&lt;br /&gt;例えば、{"name":"milk", "price":150}のように。&lt;br /&gt;こういったものでもEither型への変換ができればすこしだけ便利かもしれません。&lt;br /&gt;Either[A,B]は型パラメータを二つとるので、変換方法もReads[A]とReads[B]の２種類が必要です。&lt;br /&gt;fromJsonを使って型の変換に失敗したときは例外が投げられるので、それをキャッチして&lt;br /&gt;分岐してもいいのですが、成功したときはSome[A]、変換に失敗したときなNoneを返してくれる&lt;br /&gt;便利なasOptメソッドがあるのでここではそれを使ってみました。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;implicit def eitherReads[A,B](implicit fmtA: Reads[A], fmtB: Reads[B]):Reads[Either[A, B]] =&lt;br /&gt;  new Reads[Either[A, B]] {&lt;br /&gt;      def reads(json: JsValue) =&lt;br /&gt;          json.asOpt[A](fmtA) match {&lt;br /&gt;             case Some(a) =&gt; Left(a)&lt;br /&gt;             case None =&gt; Right(fromJson[B](json)(fmtB))&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;assert (fromJson[Either[String,Int]](json.parseJson("""1""")) == Right(1))&lt;br /&gt;assert (fromJson[Either[String,Int]](json.parseJson(""""a"""")) == Left("a"))&lt;br /&gt;assert (fromJson[List[Either[Either[Boolean,String],Int]]](json.parseJson("""["a",true, 5]""")) &lt;br /&gt;              ==  List(Left(Right("a")), Left(Left(true)), Right(5.0)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これでちょっとは便利になりましたが、Either型をたくさん使うと&lt;br /&gt;どこかでパターンマッチをたくさん書くことになって、JsValueに対して&lt;br /&gt;パターンマッチを書いていくのとあまり変わらないことになってしまいます。&lt;br /&gt;なにより型の混ざった配列ならともかく、&lt;br /&gt;{"name":"milk", "price":150}のような連想配列は&lt;br /&gt;Map[String, Either[String,Int]]&lt;br /&gt;に変換するのではなく、&lt;br /&gt;case class Hoge(name:String, price:Int)&lt;br /&gt;のような何らかのクラスに変換したいのが普通でしょう。&lt;br /&gt;もちろん特定のcaseクラスへの変換を記述することはそれほど大変なことではありませんが、&lt;br /&gt;case クラスがたくさんある場合は、ちょっと面倒そうです。&lt;br /&gt;そんな時のために、sjsonでは、任意のcase クラスへの変換プロトコルを簡単に提供するための&lt;br /&gt;便利な関数が用意されています。Scalaでは可変長の型引数はサポートされていないので、&lt;br /&gt;case クラスのメンバの数に応じて、asProduct1、asProduct2、asProduct3...&lt;br /&gt;といったメソッドが用意されています。&lt;br /&gt;sjsonのソースでは、テンプレートを使ってこれらの定義が自動生成していますが、&lt;br /&gt;ここではplay.api.json用に、asProduct3だけをポーティングしてみたいと思います。&lt;br /&gt;まず、asProduct3のシグネチャは次のようになります。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;def asProduct3[S, T1, T2, T3]&lt;br /&gt;       (f1: String, f2: String, f3: String)&lt;br /&gt;       (apply : (T1, T2, T3) =&gt; S)&lt;br /&gt;       (unapply : S =&gt; Product3[T1, T2, T3])&lt;br /&gt;       (implicit bin1: Format[T1], bin2: Format[T2], bin3: Format[T3])&lt;br /&gt;       : Format[S]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;まず、型パラメータの[S, T1, T2, T3] です。&lt;br /&gt;ひとつめのSは、変換対象のcase クラスです。&lt;br /&gt;T1,T2,T3はそれぞれ、caseクラスのメンバ変数の型になります。&lt;br /&gt;最初の引数、&lt;br /&gt;(f1: String, f2: String, f3: String)&lt;br /&gt;は、メンバ変数に体操するJSONでのキーになります。&lt;br /&gt;メンバ変数の名前と同じものになることも多いでしょう。&lt;br /&gt;次の二つの引数 applyとunapplyは、&lt;br /&gt;       (apply : (T1, T2, T3) =&gt; S)&lt;br /&gt;       (unapply : S =&gt; Product3[T1, T2, T3])&lt;br /&gt;caseクラスのコンパニオンオブジェクトのapply unapplyメソッド&lt;br /&gt;を受け取ります。&lt;br /&gt;最後に、各メンバ変数の型ごとにReads[T1]やWrites[T1]を供給するために&lt;br /&gt;Format[T1]をimplicitに受け取ります。&lt;br /&gt;&lt;br /&gt;たとえば、case class Hoge(name:String, price:Int, flag:Boolean)&lt;br /&gt;を、{"name":"...", "price":1234, "flag":true}に変換するのであれば、&lt;br /&gt;&lt;br /&gt;asProduct3[Hoge, String, Int, Boolean]&lt;br /&gt;("name", "price", "flag")&lt;br /&gt;(Hoge.apply)&lt;br /&gt;(Hoge.unapply)&lt;br /&gt;といった引数を渡すだけで、&lt;br /&gt;Format[Hoge]の実装を得ることができます。&lt;br /&gt;asProduct3の実装は次のような感じになります。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;def asProduct3[S, T1, T2, T3](f1: String, f2: String, f3: String)&lt;br /&gt;                             (apply : (T1, T2, T3) =&gt; S)&lt;br /&gt;                             (unapply : S =&gt; Product3[T1, T2, T3])&lt;br /&gt;                             (implicit bin1: Format[T1], bin2: Format[T2], bin3: Format[T3])&lt;br /&gt;  = new Format[S]{&lt;br /&gt;      def writes(s: S) = {&lt;br /&gt;        val product = unapply(s)&lt;br /&gt;        JsObject(&lt;br /&gt;          Map(&lt;br /&gt;          (f1, toJson(product._1)),&lt;br /&gt;          (f2, toJson(product._2)),&lt;br /&gt;          (f3, toJson(product._3))&lt;br /&gt;        ))&lt;br /&gt;       }&lt;br /&gt;       def reads(js: JsValue) = js match {&lt;br /&gt;         case JsObject(m) =&gt;&lt;br /&gt;           apply(&lt;br /&gt;             fromJson[T1](m(f1)),&lt;br /&gt;             fromJson[T2](m(f2)),&lt;br /&gt;             fromJson[T3](m(f3))&lt;br /&gt;            )&lt;br /&gt;         case _ =&gt; throw new RuntimeException("object expected")&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;これを使えば、例えば次のようなcase クラスに対して、&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;case class Shop(store: String, item: String, price: Int)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;次のように変換プロトコルを簡単に用意することができます。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;object ShopProtocol {&lt;br /&gt;  import DefaultFormat._&lt;br /&gt;  implicit val ShopFormat: Format[Shop] =&lt;br /&gt;    asProduct3("store", "item", "price")(Shop)(Shop.unapply(_).get)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;詳しくは、sjsonのgithubの該当部分をご覧ください。&lt;br /&gt;https://github.com/debasishg/sjson/blob/master/src/main/scala/sjson/json/Generic.scala&lt;br /&gt;&lt;br /&gt;以上です。&lt;br /&gt;これを書いている間に日が変わってしまいました！ゴメンナサイ！！&lt;br /&gt;次回12月8日は@hagikuratakeshiさんです。&lt;br /&gt;&lt;br /&gt;よろしくお願いします！！&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-2758102552684520509?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/2758102552684520509/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2011/12/play-framework-20-json-jerkson.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/2758102552684520509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/2758102552684520509'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2011/12/play-framework-20-json-jerkson.html' title='Play Framework 2.0 と JSON ライブラリ Jerksonについて調べてみました。'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-4362008777725566878</id><published>2010-12-11T23:59:00.006+09:00</published><updated>2010-12-12T02:31:52.612+09:00</updated><title type='text'>SQLAlchemyのモデルクラス用のFlaskコンバータを作ってみる</title><content type='html'>最近、PythonのWebフレームワークの中では、軽量級のFlaskをよく使っています。&lt;br /&gt;Flaskは、wsgiツールキット的なWerkzeugを基盤に構築されたフレームワークです。&lt;br /&gt;Flaskではroutingの仕組みは、Werkzeugのものをほぼそのまま使い、&lt;br /&gt;関数を簡単にコントローラとして公開できるようなデコレータが用意されています。&lt;br /&gt;これはたとえば以下のように使います。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;@app.route("/post/&amp;lt;int:post_id&gt;")&lt;br /&gt;def show_post(post_id):&lt;br /&gt;   return "hello"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;int:post_id&gt;というところに注目してください。&lt;br /&gt;この定義により、"/post/10"や"/post/5"のようなURLにマッチし、&lt;br /&gt;show_post関数の引数post_idに、マッチした部分をintに変換し&lt;br /&gt;10や5といった値がわたってくる事になります。&lt;br /&gt;&lt;br /&gt;この&amp;lt;int:hoge&gt;の部分は、Converterと呼ばれる仕組みで自作することができるようになっています。&lt;br /&gt;今回は、SQLAlchemyとからめて、idを受け取って、特定のモデルクラスのインスタンスを&lt;br /&gt;抽出するようなConverterを書いてみたいと思います。&lt;br /&gt;モチベーションとしては、以下のようにIDをintとして受け取って、&lt;br /&gt;そのidでインスタンスを検索し、なかったら404にするような操作を、&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;@app.route("/post/&amp;lt;int:post_id&gt;")&lt;br /&gt;def show_post(post_id):&lt;br /&gt;   post = Post.query.get(post_id)&lt;br /&gt;   if not post:&lt;br /&gt;       raise werkzeug.exceptions.NotFound()&lt;br /&gt;   return post.hoge()&lt;br /&gt;&lt;/itntpost_id&gt;&lt;/pre&gt;&lt;br /&gt;次のように、モデルと同名のコンバータを指定する事で、&lt;br /&gt;自動でIDの文字列からインスタンスをfetchして引数として渡し、&lt;br /&gt;もし該当IDがなければ、自動で404としてくれれば、便利かなという感じです。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;@app.route("/post") # idが該当しなければ404&lt;br /&gt;def show_post(post):&lt;br /&gt;   return post.hoge()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;まず準備として、FlaskアプリとSQLAlchemyの初期化を行います。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;from flask import Flask, request, url_for, redirect&lt;br /&gt;from flaskext.sqlalchemy import SQLAlchemy&lt;br /&gt;from sqlalchemy import Table, Column, Integer, Unicode&lt;br /&gt;from sqlalchemy.orm import mapper&lt;br /&gt;from werkzeug.routing import IntegerConverter, ValidationError&lt;br /&gt;&lt;br /&gt;app = Flask(__name__)&lt;br /&gt;&lt;br /&gt;app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///test.db"&lt;br /&gt;db = SQLAlchemy(app)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;アプリやデータベースの準備ができたら、&lt;br /&gt;早速コンバータのベースクラスを定義してみましょう。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;class BaseModelConverter(IntegerConverter):&lt;br /&gt;   def __init__(self, map):&lt;br /&gt;       super(BaseModelConverter, self).__init__(map)&lt;br /&gt;&lt;br /&gt;   def to_python(self, value):&lt;br /&gt;       id_ = super(BaseModelConverter, self).to_python(value)&lt;br /&gt;       obj = self.model.query.get(id_)&lt;br /&gt;       if not obj:&lt;br /&gt;           raise ValidationError()&lt;br /&gt;       return obj&lt;br /&gt;&lt;br /&gt;   def to_url(self, value):&lt;br /&gt;       return str(value.id)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;IntegerConverterを継承するので、正規表現的に\d+にマッチするかのチェックが&lt;br /&gt;まず行われます。&lt;br /&gt;to_pythonメソッドでは、valueつまりマッチした部分の文字列を受け取ります。&lt;br /&gt;IntegerConverter.to_pythonに渡し、int値を得ます。（この時点で失敗すればその中でValidationErrorが投げられます）&lt;br /&gt;このint値、つまりIDをもとにself.model.query.get(id_)により、インスタンスを取得し、&lt;br /&gt;Noneが得られた場合はValidationErrorとします。&lt;br /&gt;to_urlメソッドの方は、その逆に、インスタンスを受けて、そのidをstrにしたものを返すだけです。&lt;br /&gt;&lt;br /&gt;そして、モデルを定義するたびにコンバータを追加していると手間なので、&lt;br /&gt;特定のベースモデルクラスを継承したモデルを定義すると自動で同名のコンバータを&lt;br /&gt;追加するようなベースクラスを用意します。&lt;br /&gt;ベースクラスとそのメタクラスの定義は次のようになります。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&lt;br /&gt;class BaseModelType(type):&lt;br /&gt;   def __init__(cls, name, bases, attrs):&lt;br /&gt;       super(BaseModelType, cls).__init__(name, bases, attrs)&lt;br /&gt;       if name == 'BaseModel':&lt;br /&gt;           return&lt;br /&gt;       conv = type(name + "Converter", (BaseModelConverter,), dict(model=cls))&lt;br /&gt;       app.url_map.converters[name] = conv&lt;br /&gt;&lt;br /&gt;class BaseModel(object):&lt;br /&gt;   __metaclass__ = BaseModelType&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;BaseModelType.__init__の中では、&lt;br /&gt;BaseModelのコンバータは不要なので、とりあえず名前で&lt;br /&gt;BaseModelかどうかのチェックを行います。&lt;br /&gt;そうでなければ、そのモデルの名前 + "Converter"&lt;br /&gt;たとえば UserConverterのような名前の、BaseModelConverterを継承したクラスを生成します。&lt;br /&gt;これは、&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;class UserConverter(BaseModelCOnverter):&lt;br /&gt;   model = User&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;と同等です。&lt;br /&gt;そして得られたコンバータクラスを、app.url_map.convertersに、モデルと同じ名前で登録します。&lt;br /&gt;BaseModelは、BaseModelTypeをメタクラスとして持ち後は空のクラスです。&lt;br /&gt;&lt;br /&gt;ではこれをつかって試しにUserクラスを定義します。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;user_table = Table(&lt;br /&gt;   "user_table", db.metadata,&lt;br /&gt;   Column('id', Integer, primary_key=True),&lt;br /&gt;   Column('name', Unicode(100)),&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;class User(BaseModel):&lt;br /&gt;   query = db.session.query_property()&lt;br /&gt;   def __init__(self, name):&lt;br /&gt;       self.name = name&lt;br /&gt;&lt;br /&gt;mapper(User, user_table)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;とりあえず、上記のように、idとnameだけを持つモデルを定義してみました。&lt;br /&gt;あとはこれをつかったコントローラを書きます。&lt;br /&gt;今回は紙面の都合上テンプレートの使用は省略して&lt;br /&gt;直接文字列を返しておきます。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;@app.route("/detail/&amp;lt;User:user&gt;", methods=('GET',))&lt;br /&gt;def detail(user):&lt;br /&gt;    return """&lt;br /&gt;    &amp;lt;html&gt;&amp;lt;body&gt;&lt;br /&gt;    &amp;lt;h1&gt;%s&amp;lt;/h1&gt;&lt;br /&gt;    &amp;lt;/body&gt;&amp;lt;/html&gt;&lt;br /&gt;    """ % user.name&lt;br /&gt;&lt;br /&gt;&lt;/user:user&gt;&lt;/pre&gt;&lt;br /&gt;もう、説明することがないくらいシンプルなコントローラです。&lt;br /&gt;あとは、Userインスタンスを登録し、そのリストを一覧でだせるようにしましょう。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;@app.route("/", methods=('GET',))&lt;br /&gt;def users():&lt;br /&gt;    userlist = "".join(&lt;br /&gt;        '&amp;lt;a href="http://www.blogger.com/%s"&gt;%s&amp;lt;/a&gt;' % (&lt;br /&gt;            url_for('detail', user=user), user.name)&lt;br /&gt;        for user in User.query.all())&lt;br /&gt;    return """&lt;br /&gt;    &amp;lt;html&gt;&amp;lt;body&gt;&lt;br /&gt;    %s&lt;br /&gt;    &amp;lt;hr /&gt;&lt;br /&gt;    &amp;lt;form method="post" action="/"&gt;&lt;br /&gt;    &amp;lt;input type="text" name="name"&gt;&lt;br /&gt;    &amp;lt;input type="submit"&gt;&lt;br /&gt;    &amp;lt;/form&gt;&lt;br /&gt;    &amp;lt;/body&gt;&amp;lt;/html&gt;&lt;br /&gt;    """ % userlist&lt;br /&gt;&lt;br /&gt;@app.route("/", methods=('POST',))&lt;br /&gt;def add_user():&lt;br /&gt;   user = User(request.form['name'])&lt;br /&gt;   db.session.add(user)&lt;br /&gt;   db.session.commit()&lt;br /&gt;   return redirect(url_for('users'))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;url_for('detail', user=user)の部分で、UserConverter.to_urlが内部で呼び出され、&lt;br /&gt;適切なURL、たとえば/detail/1　といったURLが生成されることになります。&lt;br /&gt;&lt;br /&gt;これで準備はととのいました。&lt;br /&gt;あとは、実行するだけです。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;   db.metadata.create_all(db.engine)&lt;br /&gt;   app.debug=True&lt;br /&gt;   app.run(host="127.0.0.1",port=8000)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;と最後にかいて、ファイル全体をmain.pyなどの適当な名前で保存し、&lt;br /&gt;python main.py&lt;br /&gt;とすればサーバが立ち上がります。&lt;br /&gt;適当にユーザを追加して動作を確認しましょう！&lt;br /&gt;&lt;br /&gt;今回はモデルのIDをマッチする単純なものでしたが、&lt;br /&gt;id以外のカラムにマッチしたり、難読化したidにマッチしたり、&lt;br /&gt;制限時間付きのhmacトークンを埋め込むURLを生成したりといったことが&lt;br /&gt;コンバータの追加で行う事ができます。ちょっとしたことではありますが、&lt;br /&gt;コンバータで抽出、整合性のチェックを行い、失敗すれば404にしてくれるのは&lt;br /&gt;地味に便利だったりします。&lt;br /&gt;皆さんもFlaskやWerkzeugを使って便利なコンバータを使って、&lt;br /&gt;すてきな Python Web FWライフを！&lt;br /&gt;ということでつぎは、@r_rudi　にバトンを渡したいと思います。よろしく！＞@r_rudi&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/int&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-4362008777725566878?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/4362008777725566878/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2010/12/modelconverter.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4362008777725566878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4362008777725566878'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2010/12/modelconverter.html' title='SQLAlchemyのモデルクラス用のFlaskコンバータを作ってみる'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-2470019516580890001</id><published>2009-12-16T19:24:00.002+09:00</published><updated>2009-12-18T13:10:23.323+09:00</updated><title type='text'>zope.sqlalchemyの紹介</title><content type='html'>&lt;a href="http://tarakomania.blog.so-net.ne.jp/2009-12-15"&gt;中西さん&lt;/a&gt;より&lt;a href="http://plone.jp/documentation/advent-calendar/2009"&gt;Zope/Ploneアドベントカレンダー&lt;/a&gt;のバトンを頂戴したので、Zope/Ploneネタを書いて16日目を担当したいと思います。&lt;br /&gt;&lt;br /&gt;&lt;div&gt;普段の仕事ではZope/PloneよりもSQLAlchemyなどを使いつつRDBMSを使用するようなフレームワークを使うことの方が多いです。&lt;/div&gt;&lt;div&gt;どんなネタにしようかとすこし悩みましたが、Zope/PloneからSQLAlchemy越しにRDBMSにアクセスする方法について調べてみました。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Zopeは自前のオブジェクトデータベースZODBを持っているのでZope/PloneからRDBMSを使用する機会はあまりありませんが、既存のデータベースとやりとりが必要になることは、たまにあります。&lt;/div&gt;&lt;div&gt;ZopeからRDBMSを操作する方法としては、&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zope.org/Documentation/Guides/ZSQL-HTML/ZSQL.html"&gt;Z SQL Method&lt;/a&gt;&lt;/div&gt;&lt;div&gt;を使用するが昔からあって、&lt;/div&gt;&lt;div&gt;select * from products where id=&amp;lt;dtml-sqlvar type="string"&amp;gt;&lt;/div&gt;&lt;div&gt;このような感じで使用するようですが、ちょっと古い感じがしないでもないです。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;現在PythonでSQLとなれば、&lt;/div&gt;&lt;div&gt;The Python SQL Toolkit and Object Relational Mapperであるところの&lt;/div&gt;&lt;div&gt;SQLAlchemyを使うのが一般的です。&lt;/div&gt;&lt;div&gt;ということで、&lt;a href="http://pypi.python.org/pypi/zope.sqlalchemy/0.4"&gt;zope.sqlalchemy&lt;/a&gt;を使う方法を調べてみました。&lt;/div&gt;&lt;div&gt;&lt;a href="http://pypi.python.org/pypi/zope.sqlalchemy/0.4"&gt;zope.sqlalchemy&lt;/a&gt;は、"&lt;span class="Apple-style-span"  style="  font-style: italic; line-height: 17px; font-family:Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;Minimal Zope/SQLAlchemy transaction integration"&lt;span class="Apple-style-span"  style=" font-style: normal; line-height: normal;  font-family:Georgia, serif;"&gt;ということで&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;ZopeのトランザクションとSQLAlchemyのトランザクションを統合してくれる&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;ツールのようで、その際、きちんと&lt;a href="http://ja.wikipedia.org/wiki/2%E7%9B%B8%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88"&gt;二相コミット&lt;/a&gt;もしてくれるようです。&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="  font-style: italic;  font-family:Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;&lt;span class="Apple-style-span"  style=" font-style: normal; line-height: normal;  font-family:Georgia, serif;"&gt;&lt;a href="http://pypi.python.org/pypi/zope.sqlalchemy/0.4"&gt;zope.sqlalchemy&lt;/a&gt;の守備範囲は、トランザクションに関してだけで、データベースエンジンやデータモデルの記述については特別なサポートはありません。&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="  font-style: italic; line-height: 17px; font-family:Arial, Verdana, Geneva, 'Bitstream Vera Sans', Helvetica, sans-serif;"&gt;&lt;span class="Apple-style-span"  style=" font-style: normal; line-height: normal;  font-family:Georgia, serif;"&gt;つまり、SQLAlchemyの作法に則って、DBエンジンやモデルについての記述を行うことはできるけれども、ZMIからデータベースエンジンの設定を行ったり、zcmlで定義したり、Zopeのオブジェクトモデルとマッピングしたり、といった機能はなく、あくまでトランザクションの統合機能だけが提供されます。&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; from sqlalchemy import create_engine&lt;br /&gt;&gt;&gt;&gt; engine = create_engine("mysql://hoge", convert_unicode=True)&lt;br /&gt;&gt;&gt;&gt; from sqlalchemy.orm import scoped_session, sessionmaker]&lt;br /&gt;&gt;&gt;&gt; from zope.sqlalchemy import ZopeTransactionExtension&lt;br /&gt;&gt;&gt;&gt; Session = scoped_session(sessionmaker(bind=engine,&lt;br /&gt;...     twophase=True, extension=ZopeTransactionExtension()))&lt;br /&gt;&gt;&gt;&gt; session = Session()&lt;br /&gt;&gt;&gt;&gt; session.add(Hoge(name='fuga'))&lt;br /&gt;&gt;&gt;&gt; session.query(Hoge).all()&lt;br /&gt;[&lt;hoge fuga=""&gt;]&lt;br /&gt;&gt;&gt;&gt; import transaction&lt;br /&gt;&gt;&gt;&gt; transaction.commit()&lt;br /&gt;&lt;/hoge&gt;&lt;/pre&gt;&lt;br /&gt;とこんな感じの使い方になるようです。&lt;br /&gt;（詳しくは&lt;a href="http://pypi.python.org/pypi/zope.sqlalchemy/0.4"&gt;zope.sqlalchemy&lt;/a&gt;を参照してください。）&lt;br /&gt;ポイントは、&lt;br /&gt;&lt;pre class="prettyprint"&gt;sessionmaker(bind=engine,&lt;br /&gt;twophase=True, extension=ZopeTransactionExtension())&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;の部分と&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; import transaction&lt;br /&gt;&gt;&gt;&gt; transaction.commit()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;のところです。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;コードの説明に入るまえに、そもそもなぜトランザクションの統合が必要になるか、ということについて簡単に説明します。&lt;br /&gt;&lt;br /&gt;&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3"&gt;Wikipediaのトランザクション&lt;/a&gt;の項によると、&lt;br /&gt;"トランザクション (transaction) とは、分ける事の出来ない一連の情報処理の単位である"とあります。&lt;br /&gt;つまりひとつのトランザクションに含まれる処理であれば、全部が成功もしくは全部が失敗のどちらかになるべきで、一部が失敗して一部が成功した、といった状態になったら困るわけです。これを実現するために、ひとつの&lt;br /&gt;トランザクション処理の途中で、なんらかの不都合が発生した場合、そこまでの処理をすべてなかったことにするロールバックや、すべての処理が成功した段階で、変更結果を永続化させるコミットの機能がZODBやRDBMSにより提供されます。&lt;br /&gt;&lt;br /&gt;ZopeのZODBはトランザクションをサポートしており、作法に則ったやり方でZODBにアクセスすれば、処理の途中で例外が発生した場合ロールバックされ、一貫性が保たれます。&lt;br /&gt;しかし、そこでロールバックされるのは当然ZODBに対する書き込み操作のみで、もし直接外部のRDBMSに対して書き込みを行っていた場合、その処理まで自動でロールバックされるわけではありません。すると、Zope/Ploneとしては処理が途中で失敗したのでロールバックを行い、ZODBの中身的には変更が無かったことになっているのに、外部のRDBMS には処理が成功したかのように変更が書き込まれてしまう、といった結果になる可能性があります。多くの場合において、これは望んだ結果ではないでしょう。&lt;br /&gt;これを防ぐには、ZODBとRDBMSに対するふたつのトランザクションを統合し、ZODBに対する処理とRDBMSに対する処理が、両方成功するか、両方失敗するか、どちらかであるようにする必要があります。&lt;br /&gt;&lt;br /&gt;これを実現する中核となるのが、zope.sqlalchemy.ZopeTransactionExtensionです。これはSQLAlchemyの&lt;br /&gt;&lt;a href="http://www.sqlalchemy.org/docs/05/session.html#extending-session"&gt;extending-session&lt;/a&gt;という機能を利用したもので、&lt;br /&gt;&lt;a href="http://www.sqlalchemy.org/docs/05/reference/orm/interfaces.html#sqlalchemy.orm.interfaces.SessionExtension"&gt;sqlalchemy.orm.interfaces.SessionExtension&lt;/a&gt;を継承（or実装。ここらへんはPythonなので微妙なところですね。）したクラスです。このクラスを使用することにより、zope.sqlalchemyはセッションの開始や、セッションの中でオブジェクトの追加や変更、削除などをフックすることができます。&lt;br /&gt;でもって、セッションの開始でTwoPhaseSessionDataManagerインスタンスを作成し、zope_transaction.get().join(TwoPhaseSessionDataManager(session, state))&lt;br /&gt;のような感じでzopeのトランザクションに結びつけます。&lt;br /&gt;そして、SQLAlchemyのセッションでオブジェクトの変更などをフックして、状態を初期状態のSTATUS_ACTIVEからSTATUS_CHANGEDに変更します。&lt;br /&gt;そんでもって&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; import transaction&lt;br /&gt;&gt;&gt;&gt; transaction.commit()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;によりzopeのトランザクションがコミットされた場合は、TwoPhaseSessionDataManagerのメソッドが呼び出され、&lt;br /&gt;もし状態がSTATUS_CHANGEDになっていれば、二相コミットを行います。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;STATUS_ACTIVE/STATUS_CHANGEDと、2相コミットに関して補足します。&lt;br /&gt;&lt;br /&gt;2相コミットとは、複数のデータベースが存在する時に、各トランザクションを統合して、全体として成功／失敗のどちらかにするための技術です。ちょっと説明が適当なので、&lt;br /&gt;詳しくは&lt;br /&gt;&lt;a href="http://ja.wikipedia.org/wiki/2%E7%9B%B8%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88"&gt;Wikipedia:"2相コミット"&lt;/a&gt;を参照してください。&lt;br /&gt;例えば、データベースAとBがあった場合、よくありそうな問題としては、全部の処理がうまくいったので、&lt;br /&gt;まずAにコミットして成功し、つぎにBに対してコミットを行おうとしたら、コミットに失敗してしまった、という状況です。&lt;br /&gt;これを防ぐために2相コミットでは、トランザクションのコミットしようとするとき、&lt;br /&gt;参加しているデータベースに対して、コミットが可能であるか、まず全員に質問します。ここで、全員から同意が得られれば、全体をあらためてコミットします。逆に、どれかひとつでも同意が得られなかった場合は、全体をロールバックさせます。この2相コミットを使用すれば、DBMSが落ちる、DBMSとのネットワークが切れるなどの事態が肝心なタイミングで発生しなければ、全体の一貫性を保つことができます。&lt;br /&gt;この2相コミットは、それなりに面倒な処理です。RDBMSの方に書き込みを行わなかった場合、そもそも2相コミットを行う必要がありません。このためにzope.sqlalchemyでは、SQLAlchemyのセッションに変更操作を行ったかどうかを、STATUS_ACTIVE/STATUS_CHANGEDという状態により管理し、zopeのtransaction.commit()時にSTATUS_ACTIVEのままであれば2相コミットを省略するような動きになっているようです。&lt;br /&gt;STATUS_ACTIVE/STATUS_CHANGEDの状態は、sqlalchemy.orm.interfaces.SessionExtensionのafter_bulk_updateメソッドなどが呼び出されるタイミングで、STATUS_CHANGEDに変更されます。&lt;br /&gt;このフックメソッドは、次のようにORMを通さないでSQLが発行された場合は呼び出されません。&lt;br /&gt;（よびだしてくれればいいような気がしますが。）&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; conn = session.connection()&lt;br /&gt;&gt;&gt;&gt; conn.execute(update_expression)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;この問題を防ぐため、このような場合には、つぎのように明示的にSTATUS_CHANGEDをセットします。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; from zope.sqlalchemy import mark_changed&lt;br /&gt;&gt;&gt;&gt; mark_changed(session)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;追記 2009-12-18&gt;&lt;div&gt;sessionmakerの作成の際に、&lt;span class="Apple-style-span" style="font-family: monospace; font-size: 13px; white-space: pre; "&gt;twophase=Falseにしておけば、2相コミットをせずに&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:monospace;font-size:100%;"&gt;&lt;span class="Apple-style-span" style="font-size: 13px; white-space: pre;"&gt;動いてくれます。当然不整合が起こる可能性は高くなりますが。&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:monospace;font-size:100%;"&gt;&lt;span class="Apple-style-span" style="font-size: 13px; white-space: pre;"&gt;あと、twophase=Trueの場合は、当然ですがRDBMS側でも2相コミットが可能な設定になっている必要があります。&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/追記&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;ということで、ということで、次のバトンは昔からブログでお世話になっているPythonの大先輩の&lt;a href="http://nakagami.blog.so-net.ne.jp/"&gt;nakagamiさん&lt;/a&gt;にお願いします。&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-2470019516580890001?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/2470019516580890001/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/12/zopesqlalchemy.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/2470019516580890001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/2470019516580890001'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/12/zopesqlalchemy.html' title='zope.sqlalchemyの紹介'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-5871497944739085435</id><published>2009-12-06T22:56:00.001+09:00</published><updated>2009-12-06T22:59:57.366+09:00</updated><title type='text'></title><content type='html'>&lt;a href="http://www.infoq.com/jp/news/2009/11/clojars-leiningen-clojure"&gt;InfoQ ClojarsとLeiningenを使ったClojure向け自動ライブラリ依存関係管理&lt;/a&gt;&lt;br/&gt;&lt;br /&gt;PythonのPYPI、easy_install/pipにあたるもののようだ。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-5871497944739085435?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/5871497944739085435/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/12/infoq-clojarsleiningenclojure.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/5871497944739085435'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/5871497944739085435'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/12/infoq-clojarsleiningenclojure.html' title=''/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-4435980654879239947</id><published>2009-12-06T18:02:00.003+09:00</published><updated>2009-12-06T22:53:39.632+09:00</updated><title type='text'>slime + swank-clojureの続き</title><content type='html'>slime + swank-clojureが、やっぱり調子が悪く、&lt;br /&gt;どうもClojure1.1との組み合わせの問題のようなので&lt;br /&gt;&lt;a href="http://github.com/stuarthalloway/swank-clojure/tree/clojure-1.1"&gt;http://github.com/stuarthalloway/swank-clojure/tree/clojure-1.1&lt;/a&gt;&lt;br /&gt;のswank-clojureを使ったらうまく動いた。&lt;br/&gt;&lt;br /&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;(追記)&lt;br/&gt;結局&lt;br /&gt;&lt;a href="http://github.com/technomancy/swank-clojure/tree/clojure-1.1"&gt;http://github.com/technomancy/swank-clojure/tree/clojure-1.1&lt;/a&gt;&lt;br /&gt;の方に変更した。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-4435980654879239947?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/4435980654879239947/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/12/slime-swank-clojure_06.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4435980654879239947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4435980654879239947'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/12/slime-swank-clojure_06.html' title='slime + swank-clojureの続き'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-1411787729946052690</id><published>2009-12-05T09:56:00.004+09:00</published><updated>2009-12-05T10:54:29.604+09:00</updated><title type='text'>slime + swank-clojureが動かない</title><content type='html'>&lt;a href="http://riddell.us/tutorial/slime_swank/slime_swank.html"&gt;Clojure with Emacs and Slime/Swank on Ubuntu&lt;/a&gt;を参考にemacs + slime + swank-clojureでプログラミングしてるのだけれど、&lt;br /&gt;最近のslimeとswank-clojureの組み合わせがこける。&lt;br /&gt;&lt;a href="http://groups.google.com/group/clojure/browse_thread/thread/3e5f416e3f2a1884"&gt;SLIME REPL broken&lt;/a&gt;&lt;br /&gt;を参考に、&lt;br/&gt;&lt;br /&gt;"/home/hokari/opt/swank-clojure/src/main/clojure/swank/core/protocol.clj"&lt;br /&gt;を&lt;br/&gt;&lt;br /&gt;@@ -4,6 +4,20 @@&lt;br /&gt;&lt;br&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;;; Read forms&lt;br /&gt;&lt;br&gt; + &amp;nbsp;(def #^{:private true}&lt;br /&gt;&lt;br&gt; + &amp;nbsp; &amp;nbsp; *percent-re* #"%")&lt;br /&gt;&lt;br&gt; +&lt;br /&gt;&lt;br /&gt;&lt;br&gt; +(defn- fix-percent&lt;br /&gt;&lt;br&gt; + &amp;nbsp;"Replace double colons with a /."&lt;br /&gt;&lt;br&gt; + &amp;nbsp;([text] (.replaceAll (re-matcher *percent-re* text) "?")))&lt;br /&gt;&lt;br&gt; +&lt;br /&gt;&lt;br&gt; +(def #^{:private true}&lt;br /&gt;&lt;br&gt; + &amp;nbsp; &amp;nbsp; *double-colon-re* #"::")&lt;br /&gt;&lt;br /&gt;&lt;br&gt; +&lt;br /&gt;&lt;br&gt; +(defn- fix-double-colon &lt;br&gt; + &amp;nbsp;"Replace double colons with a /."&lt;br /&gt;&lt;br&gt; + &amp;nbsp;([text] (.replaceAll (re-matcher *double-colon-re* text) "/")))&lt;br /&gt;&lt;br&gt; +&lt;br /&gt;&lt;br&gt; (def #^{:private true}&lt;br /&gt;&lt;br&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; *namespace-re* #"(^\(:emacs-rex \([a-zA-Z][a-zA-Z0-9]+):")&lt;br /&gt;&lt;br /&gt;&lt;br&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;(defn- fix-namespace&lt;br /&gt;&lt;br&gt; @@ -50,7 +64,7 @@&lt;br /&gt;&lt;br&gt; &amp;nbsp; &amp;nbsp;([#^java.io.Reader reader]&lt;br /&gt;&lt;br&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; (let [len &amp;nbsp;(Integer/parseInt (read-chars reader 6 read-fail-exception) 16)&lt;br /&gt;&lt;br&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg &amp;nbsp;(read-chars reader len read-fail-exception)&lt;br /&gt;&lt;br /&gt;&lt;br&gt; - &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; form (read-string (fix-namespace msg))]&lt;br /&gt;&lt;br&gt; + &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; form (read-string (fix-namespace (fix-double-colon (fix-percent msg))))]&lt;br /&gt;&lt;br&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (if (seq? form)&lt;br /&gt;&lt;br /&gt;&lt;br&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (deep-replace {'t true} form)&lt;br /&gt;&lt;br&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; form))))&lt;br /&gt;&lt;br /&gt;のようなかんじにしたら動いた。&lt;br /&gt;Common LispとClojureで"%"や"::"の意味とか許可/不許可が違うせい、&lt;br /&gt;ということみたい。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-1411787729946052690?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/1411787729946052690/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/12/slime-swank-clojure.html#comment-form' title='2 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/1411787729946052690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/1411787729946052690'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/12/slime-swank-clojure.html' title='slime + swank-clojureが動かない'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-5057456305307757342</id><published>2009-11-16T19:01:00.003+09:00</published><updated>2009-11-16T19:14:46.784+09:00</updated><title type='text'>ClojureのvectorやlistをJavaの配列にキャストする方法</title><content type='html'>ClojureからJavaのメソッドを呼び出すときなどに、&lt;br /&gt;ClojureのvectorからString[]やint[]などにキャストする必要がある場合、&lt;br /&gt;to-arrayやinto-arrayを使うとキャストできる。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;user&gt; (to-array ["a" "hello"])&lt;br /&gt;#&lt;object[]&gt;                                                                                                                                        &lt;br /&gt;user&gt; (into-array ["a" "hello"])&lt;br /&gt;#&lt;string[]&gt;                                                                                                                                        &lt;br /&gt;user&gt; (into-array String ["a" "hello"])&lt;br /&gt;#&lt;string[]&gt;                                                                                                                                        &lt;br /&gt;user&gt; (into-array Number [1 1.2])&lt;br /&gt;#&lt;number[]&gt;                                                                                                                                       &lt;br /&gt;user&gt; (into-array [1 2])&lt;br /&gt;#&lt;integer[]&gt;                                                                                                                                      &lt;br /&gt;user&gt; (into-array [1 1.2]) ;これはエラーに&lt;br /&gt;; Evaluation aborted.&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-5057456305307757342?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/5057456305307757342/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/11/java.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/5057456305307757342'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/5057456305307757342'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/11/java.html' title='ClojureのvectorやlistをJavaの配列にキャストする方法'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-5497916944781985134</id><published>2009-06-05T00:15:00.002+09:00</published><updated>2009-06-05T00:23:15.227+09:00</updated><title type='text'>UsedのThinkpad X60s購入</title><content type='html'>UsedのThinkpad X60sを￥39,800で購入しました。ちなみに英語キーボードです。&lt;br /&gt;Ubuntuを入れて快調です。メモリは２G追加。&lt;br /&gt;インストールはUSBメモリからで、ひっかかることもなく&lt;br /&gt;とても簡単にできました。無線LANもデフォルトで使用できました。&lt;br /&gt;右パームレスト部分が無線LANカードの発熱のために熱くなりますが、&lt;br /&gt;これは仕様なようです。無線OFFにしておくと熱くならなかったし、&lt;br /&gt;無線もともと使わない予定なので問題なし。&lt;br /&gt;久方ぶりにマイノートPCを手に入れ感無量。&lt;br /&gt;半年くらいしたらSSDにしたいなぁと妄想は膨らむ。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-5497916944781985134?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/5497916944781985134/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/06/usedthinkpad-x60s.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/5497916944781985134'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/5497916944781985134'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/06/usedthinkpad-x60s.html' title='UsedのThinkpad X60s購入'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-4223971745922113202</id><published>2009-05-31T19:54:00.004+09:00</published><updated>2009-06-01T08:54:19.314+09:00</updated><title type='text'>JavaOne 2009 Script Bowl</title><content type='html'>JavaOne 2009では、去年のJavaOne 2008に引き続きScript Bowlのセッションがあるようです。&lt;br /&gt;Script Bowlでは、JavaVM上のスクリプト言語同士でプログラミング対決をしたり、&lt;br /&gt;言語の特長を紹介したりするようです。&lt;br /&gt;去年は、Scala,Groovy,JRuby, Jythonだったのですが、&lt;br /&gt;今年はこれにClojureが加わって5つの言語で対決するようです。&lt;br /&gt;JavaVM上のスクリプト言語は、他にも山ほどあるわけで、&lt;br /&gt;JavaOneで5つめに選ばれたってことは、けっこう注目度がたかいということでしょうか。&lt;br /&gt;これをきっかけにさらに盛り上がるといいなぁ。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-4223971745922113202?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/4223971745922113202/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/javaone-2009-script-bowl-call.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4223971745922113202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4223971745922113202'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/javaone-2009-script-bowl-call.html' title='JavaOne 2009 Script Bowl'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-6639588350502348217</id><published>2009-05-19T22:26:00.003+09:00</published><updated>2009-05-20T00:20:33.946+09:00</updated><title type='text'>Clojure勉強の進捗</title><content type='html'>1.Clojureでマクロ&lt;br /&gt;マクロの練習に、パターンマッチングのマクロを書いた。&lt;br /&gt;マクロの基本的な書き方や、mapやvector、sequenceの基本もわかってきた。&lt;br /&gt;&lt;br /&gt;gitの使い方を覚えて、antの使い方の基礎を覚えたら、&lt;br /&gt;githubにのせてみたい。&lt;br /&gt;&lt;br /&gt;2.Clojureでモナド&lt;br /&gt;Clojure.contrib.monadsの使い方を下記の解説をみて覚える。&lt;br /&gt;&lt;a href="http://intensivesystems.net/tutorials/monads_101.html"&gt;Monads in Clojure&lt;/a&gt;&lt;br /&gt;&lt;a href="http://onclojure.com/2009/03/05/a-monad-tutorial-for-clojure-programmers-part-1/"&gt;A monad tutorial for Clojure programmers (part 1)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://onclojure.com/2009/03/06/a-monad-tutorial-for-clojure-programmers-part-2/" rel="bookmark"&gt;part 2&lt;/a&gt;&lt;br /&gt;&lt;a href="http://onclojure.com/2009/03/23/a-monad-tutorial-for-clojure-programmers-part-3/" rel="bookmark"&gt;part 3&lt;/a&gt;&lt;br /&gt;&lt;a href="http://onclojure.com/2009/04/24/a-monad-tutorial-for-clojure-programmers-part-4/"&gt;part 4&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://stefan-klinger.de/"&gt;http://stefan-klinger.de/&lt;/a&gt;&lt;br /&gt;のところにある&lt;br /&gt;&lt;a href="http://stefan-klinger.de/files/monadGuide.pdf" title="download file" class="paper"&gt;The Haskell Programmer's Guide to the IO Monad — Don't Panic&lt;/a&gt;.&lt;br /&gt;をもう一度読む。&lt;br /&gt;&lt;a href="http://www.haskell.org/arrows/syntax.html" target="_blank"&gt;Arrow syntax&lt;/a&gt;&lt;span style="text-decoration: underline;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;a href="http://www.kotha.net/ghc_users_guide_ja/arrow-notation.html#id2667629" target="_blank"&gt;8.10. アロー記法&lt;/a&gt;&lt;br /&gt;も読んでArrowも理解したい。&lt;br /&gt;&lt;br /&gt;3.その他&lt;br /&gt;軽くJavaの復習。antやJDEEなども。&lt;br /&gt;あと&lt;a href="http://www.amazon.com/dp/0201310090"&gt;Concurrent-Programming-Java-TM-Principles&lt;/a&gt;&lt;br /&gt;を注文したのできたら読む。&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.lwjgl.org/"&gt;LWGL - Lightweight Java Game Library&lt;/a&gt;&lt;br /&gt;というのを見つけた。よさそう。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-6639588350502348217?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/6639588350502348217/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/clojure.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/6639588350502348217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/6639588350502348217'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/clojure.html' title='Clojure勉強の進捗'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-4721104365215816684</id><published>2009-05-10T18:03:00.012+09:00</published><updated>2009-05-13T10:10:18.067+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tokyocabinet'/><category scheme='http://www.blogger.com/atom/ns#' term='install'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Tokyo CabinetをUbuntuにインストール</title><content type='html'>&lt;a href="http://tokyocabinet.sourceforge.net/"&gt;Tokyo Cabinet&lt;/a&gt;&lt;br /&gt;Tokyo CabinetはHirabayashi Mikio 氏により新しく実装された高速なDBMです。&lt;br /&gt;&lt;br /&gt;DBMというのは、Database Managerの略で、非常にシンプルなキー：バリュー型の&lt;br /&gt;データベースです。プログラミングでいうところのハッシュマップや連想配列、辞書と同じような機能を、ファイル上に永続化して使用できるというようなものです。&lt;br /&gt;&lt;a href="http://ja.wikipedia.org/wiki/Berkeley_DB"&gt;Berkeley DB&lt;/a&gt;などが有名です。&lt;br /&gt;&lt;br /&gt;Tokyo Cabinetは、とても新しく実装されたDBMで、&lt;br /&gt;高速な処理や非常に大きいデータベースファイルを扱えるなどのすぐれた&lt;br /&gt;特徴を持ち、Java,Perl,Ruby,Lua用のAPIが公式に用意されています。&lt;br /&gt;&lt;br /&gt;ClojureでオレオレDBMSを実装するためのバックエンドとして使いたいと思っているので、&lt;br /&gt;今回はまず、UbuntuにTokyo CabinetとJava APIをインストールします。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;sudo apt-get update&lt;br /&gt;wget http://tokyocabinet.sourceforge.net/tokyocabinet-1.4.20.tar.gz&lt;br /&gt;sudo apt-get install build-essential&lt;br /&gt;sudo apt-get install zlib1g-dev &lt;br /&gt;sudo apt-get install libbz2-dev&lt;br /&gt;&lt;br /&gt;wget http://tokyocabinet.sourceforge.net/tokyocabinet-1.4.20.tar.gz&lt;br /&gt;tar xzvf tokyocabinet-1.4.20.tar.gz&lt;br /&gt;cd tokyocabinet-1.4.20&lt;br /&gt;export CPPFLAGS="-I/usr/lib/jvm/java-6-openjdk/include/"&lt;br /&gt;export JAVA_HOME=/usr/lib/jvm/java-6-openjdk&lt;br /&gt;export PATH=$JAVA_HOME/bin:$PATH&lt;br /&gt;&lt;br /&gt;./configure&lt;br /&gt;make&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ここで前回インストールしたPacoを使用して&lt;br /&gt;&lt;pre class="prettyprint"&gt;sudo make install&lt;/pre&gt;&lt;br /&gt;のかわりに、&lt;br /&gt;&lt;pre class="prettyprint"&gt;sudo paco -D make install&lt;/pre&gt;&lt;br /&gt;としてインストールしてみた。&lt;br /&gt;つぎにJava用APIをインストール。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;wget http://tokyocabinet.sourceforge.net/javapkg/tokyocabinet-java-1.18.tar.gz&lt;br /&gt;tar xzvf tokyocabinet-java-1.18.tar.gz&lt;br /&gt;cd tokyocabinet-java-1.18&lt;br /&gt;./configure&lt;br /&gt;make&lt;br /&gt;make check&lt;br /&gt;sudo paco -D make install&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://tokyocabinet.sourceforge.net/javadoc/"&gt;http://tokyocabinet.sourceforge.net/javadoc/&lt;/a&gt;&lt;br /&gt;にあるサンプルを実行。&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;import tokyocabinet.*;&lt;br /&gt;% cat TCHDBEX.java&lt;br /&gt;import tokyocabinet.*;&lt;br /&gt;&lt;br /&gt;public class TCHDBEX {&lt;br /&gt;    public static void main(String[] args){&lt;br /&gt;        HDB hdb = new HDB();&lt;br /&gt;        if(!hdb.open("casket.tch", HDB.OWRITER | HDB.OCREAT)){&lt;br /&gt;            int ecode = hdb.ecode();&lt;br /&gt;            System.err.println("open error: " + hdb.errmsg(ecode));&lt;br /&gt;        }&lt;br /&gt;        if(!hdb.put("foo", "hop") ||&lt;br /&gt;           !hdb.put("bar", "step") ||&lt;br /&gt;           !hdb.put("baz", "jump")){&lt;br /&gt;            int ecode = hdb.ecode();&lt;br /&gt;            System.err.println("put error: " + hdb.errmsg(ecode));&lt;br /&gt;        }&lt;br /&gt;        String value = hdb.get("foo");&lt;br /&gt;        if(value != null){&lt;br /&gt;            System.out.println(value);&lt;br /&gt;        } else {&lt;br /&gt;            int ecode = hdb.ecode();&lt;br /&gt;            System.err.println("get error: " + hdb.errmsg(ecode));&lt;br /&gt;        }&lt;br /&gt;        hdb.iterinit();&lt;br /&gt;        String key;&lt;br /&gt;        while((key = hdb.iternext2()) != null){&lt;br /&gt;            value = hdb.get(key);&lt;br /&gt;            if(value != null){&lt;br /&gt;                System.out.println(key + ":" + value);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        if(!hdb.close()){&lt;br /&gt;            int ecode = hdb.ecode();&lt;br /&gt;            System.err.println("close error: " + hdb.errmsg(ecode));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;% javac TCHDBEX.java&lt;br /&gt;% ls&lt;br /&gt;TCHDBEX.class TCHDBEX.java% java TCHDBEX&lt;br /&gt;hop&lt;br /&gt;foo:hop&lt;br /&gt;bar:step&lt;br /&gt;baz:jump&lt;br /&gt;&lt;br /&gt;% ls&lt;br /&gt;TCHDBEX.class TCHDBEX.java  casket.tch&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;インストールは成功した模様。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-4721104365215816684?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/4721104365215816684/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/tokyo-cabinetubuntu.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4721104365215816684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4721104365215816684'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/tokyo-cabinetubuntu.html' title='Tokyo CabinetをUbuntuにインストール'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-6243265063348764952</id><published>2009-05-10T17:53:00.007+09:00</published><updated>2009-05-10T18:04:52.025+09:00</updated><title type='text'>PacoをUbuntuにインストール</title><content type='html'>&lt;a href="http://d.hatena.ne.jp/rx7/20081011/p2"&gt;"make install"したソフトウェアを管理できる超便利ツール「Paco」&lt;/a&gt;&lt;br /&gt;を参考にPacoをUbuntuにインストール。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;sudo apt-get update&lt;br /&gt;sudo apt-get install build-essential&lt;br /&gt;sudo apt-get install libgtkmm-2.4-dev&lt;br /&gt;wget http://downloads.sourceforge.net/paco/paco-2.0.6.tar.gz&lt;br /&gt;tar zxvf paco-2.0.6.tar.gz&lt;br /&gt;cd paco-2.0.6/&lt;br /&gt;./configure&lt;br /&gt;make&lt;br /&gt;sudo make install&lt;br /&gt;sudo make logme&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-6243265063348764952?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/6243265063348764952/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/pacoubuntu.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/6243265063348764952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/6243265063348764952'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/pacoubuntu.html' title='PacoをUbuntuにインストール'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-4507331333856996257</id><published>2009-05-08T09:20:00.005+09:00</published><updated>2009-05-08T16:35:00.158+09:00</updated><title type='text'>Clojure 1.0</title><content type='html'>&lt;a href="http://groups.google.com/group/clojure/browse_thread/thread/1e661d16bd910ddd"&gt;Clojure 1.0 - Clojure&lt;/a&gt; Clojure | Google Groups&lt;br /&gt;&lt;a href="http://clojure.blogspot.com/2009/05/clojure-10.html"&gt;Clojure: Clojure 1.0&lt;/a&gt;News about Clojure by Rich Hickey&lt;br /&gt;&lt;br /&gt;ついにClojureの1.0がでました。&lt;br /&gt;Congratulation!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-4507331333856996257?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/4507331333856996257/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/clojure-10.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4507331333856996257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/4507331333856996257'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/clojure-10.html' title='Clojure 1.0'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-7078281337573115657</id><published>2009-05-04T11:30:00.004+09:00</published><updated>2009-05-04T11:39:05.861+09:00</updated><title type='text'>zsh上でsvnの補完がエラーになる</title><content type='html'>zsh上でsvnコマンドの補完が&lt;br /&gt;_arguments:comparguments:303: invalid argument: ARG&lt;br /&gt;というメッセージとともにエラーになる症状になった。&lt;br /&gt;どうも、subversion1.5とzsh4.3.4の組み合わせで、&lt;br /&gt;zsh4.3.4の&lt;br /&gt;/usr/share/zsh/4.3.4/functions/_subversion&lt;br /&gt;が、subversion1.5での変更に対応していないことが原因のよう。&lt;br /&gt;&lt;a href="http://d.hatena.ne.jp/sotarok/20080813/1218649287"&gt;http://d.hatena.ne.jp/sotarok/20080813/1218649287&lt;/a&gt;&lt;br /&gt;こちらを参考に、&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;% curl -O http://gvn.googlecode.com/svn/trunk/contrib/zsh/_subversion&lt;br /&gt;% sudo mv _subversion /usr/share/zsh/site-functions&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;で解決しました。&lt;br /&gt;ありがとうございます。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-7078281337573115657?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/7078281337573115657/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/zshsvn.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/7078281337573115657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/7078281337573115657'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/zshsvn.html' title='zsh上でsvnの補完がエラーになる'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-7935051221940631470</id><published>2009-05-01T21:48:00.004+09:00</published><updated>2009-05-03T18:43:39.344+09:00</updated><title type='text'>ScalaをUbuntuにインストール</title><content type='html'>UbuntuにScalaをインストール。&lt;br /&gt;http://lovehateubuntu.blogspot.com/2008/05/adventures-in-scala-part-i.html&lt;br /&gt;http://d.hatena.ne.jp/riue/20080105/1199552108&lt;br /&gt;を参考にしました。&lt;br /&gt;&lt;br /&gt;Javaは前回のClojureのインストール時に&lt;br /&gt;sudo apt-get install openjdk-6-jdk&lt;br /&gt;でインストール済み。&lt;br /&gt;&lt;pre class="prettyprint"&gt;wget http://www.scala-lang.org/downloads/distrib/files/scala-2.7.4.final.tgz&lt;br /&gt;tar xzvf scala-2.7.4.final.tgz &lt;br /&gt;sudo mv scala-2.7.4.final /usr/share/scala&lt;br /&gt;sudo ln -s /usr/share/scala/bin/scala /usr/local/bin/scala&lt;br /&gt;sudo ln -s /usr/share/scala/bin/scalac /usr/local/bin/scalac&lt;/pre&gt;&lt;br /&gt;ビルドをしていないので、&lt;br /&gt;ダウンロードしてパスを通しただけです。&lt;br /&gt;.emacsに&lt;br /&gt;&lt;pre class="prettyprint"&gt;(add-to-list 'load-path "/usr/share/scala/misc/scala-tool-support/emacs")&lt;br /&gt;(require 'scala-mode-auto)&lt;br /&gt;(setq scala-interpreter "/usr/local/bin/scala")&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-7935051221940631470?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/7935051221940631470/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/scalaubuntu.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/7935051221940631470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/7935051221940631470'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/05/scalaubuntu.html' title='ScalaをUbuntuにインストール'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-8541455251493152217</id><published>2009-04-29T22:50:00.008+09:00</published><updated>2009-05-03T18:36:47.033+09:00</updated><title type='text'>Clojureの紹介</title><content type='html'>Clojureの特徴。&lt;br /&gt;&lt;br /&gt;-JavaVMの上で動く&lt;br /&gt;-Lispの新しい方言&lt;br /&gt;-動的型付けの純粋でない関数型言語&lt;br /&gt;-STMによる並行処理&lt;br /&gt;-豊富でimmutableなデータ型&lt;br /&gt;-遅延シーケンス&lt;br /&gt;&lt;br /&gt;というところあたり。&lt;br /&gt;&lt;br /&gt;1.JavaVMの上で動く&lt;br /&gt;JavaVMの上で動く言語は、ScalaやGroovy、ABCLなど&lt;br /&gt;たくさんある。&lt;br /&gt;&lt;br /&gt;これらの中でClojureは新しいほう。&lt;br /&gt;JavaVMの上での言語開発の利点は、Javaとの連携が容易で&lt;br /&gt;Javaの豊富なライブラリを利用できる点と、&lt;br /&gt;JVMの高いパフォーマンス、マルチプラットフォーム対応&lt;br /&gt;ガーベージコレクタなどの恩恵を受けられる点など。&lt;br /&gt;&lt;br /&gt;2.Lispの新しい方言&lt;br /&gt;&lt;br /&gt;Common LispやR5RSなどの仕様によらない、&lt;br /&gt;新しく設計されたLispの方言。&lt;br /&gt;当然コードはS式でマクロが使える。&lt;br /&gt;新しいLispという意味では、ポールグレアムのArcなどと同様ですね。&lt;br /&gt;JVM上のLispは、Common Lisp on JVMのABCL、&lt;br /&gt;Scheme on JVM のSISCやKAWAなどがあるが、&lt;br /&gt;新規設計のLisp方言onJVMはClojure以外にいまのところなさそう。&lt;br /&gt;リーダマクロが独自定義できない、&lt;br /&gt;末尾再帰最適化がないなどの理由から、&lt;br /&gt;Common Lispなどからきたユーザには窮屈に感じられそう。&lt;br /&gt;&lt;br /&gt;Lisp以外からきたユーザには、Common LispやSchemeより&lt;br /&gt;逆にとっつきが良いかもしれない。&lt;br /&gt;&lt;br /&gt;主観的な感想だが、&lt;br /&gt;Common Lispの無骨な感じや歴史からくる複雑さ、&lt;br /&gt;Schemeの原理主義的な感じから比べて、&lt;br /&gt;Clojureには、PythonやRubyのような&lt;br /&gt;ユーザに優しい雰囲気が感じられる。&lt;br /&gt;&lt;br /&gt;3.動的型付けの純粋でない関数型言語&lt;br /&gt;Haskellなどのような純粋関数型ではないが、&lt;br /&gt;immutableなデータ型とそれへの参照を使って状態が管理されるので、&lt;br /&gt;Python,Common Lisp などに比べると、&lt;br /&gt;比較的破壊的な操作が少ない。&lt;br /&gt;&lt;br /&gt;基本はimmutableで、プロセスディクショナリやetsを持っている&lt;br /&gt;Erlangとポジションは似ているかも。&lt;br /&gt;&lt;br /&gt;ここまでは前提条件的なもの。&lt;br /&gt;ここからが、とくにClojureの魅力的な点。&lt;br /&gt;&lt;br /&gt;4.STMによる並行処理&lt;br /&gt;&lt;br /&gt;STMは、Software Transactional Memory&lt;br /&gt;&lt;a href="http://ja.wikipedia.org/wiki/%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E3%83%A1%E3%83%A2%E3%83%AA"&gt;wikipedia:ソフトウェアトランザクショナルメモリ&lt;/a&gt;&lt;br /&gt;複数スレッドからの共有メモリの読み書きを、&lt;br /&gt;データベースのトランザクション処理のように制御する方法。&lt;br /&gt;&lt;a href="http://research.microsoft.com/%7Esimonpj/papers/stm/beautiful.pdf"&gt;"Beautiful concurrency" Simon Peyton Jones (PDF)&lt;/a&gt;&lt;br /&gt;や、書籍「ビューティフルコード」など。&lt;br /&gt;並行制御として、ErlangやScalaのようなアクターモデルと&lt;br /&gt;このSTMと、あとはロックを使う方法などがあって、&lt;br /&gt;どれが有用なのかについては、議論の的になっているようだ。&lt;br /&gt;&lt;br /&gt;5.豊富でimmutableなデータ型&lt;br /&gt;&lt;br /&gt;データ型の便利さにPythonに近いものを感じる。&lt;br /&gt;Erlangの持つimmutableでファーストクラスのコンテナは、&lt;br /&gt;リストとタプルしかない。&lt;br /&gt;dictはリストの組み合わせでしかなく、リレラルもない。&lt;br /&gt;これはErlangの弱点だと思われる。&lt;br /&gt;また、immutableなコンテナは、パフォーマンスの低下を&lt;br /&gt;招きかねないが、そこらへんも効率よく実装されているらしい。&lt;br /&gt;&lt;br /&gt;6.遅延シーケンス&lt;br /&gt;Clojureはもちろん遅延評価ではないが、&lt;br /&gt;遅延シーケンスが言語レベルでサポートされている。&lt;br /&gt;Pythonのイテレータやジェネレータの感覚にとても近いものを感じるが、&lt;br /&gt;遅延シーケンス自体もimmutableなので、状態を内在している&lt;br /&gt;Pythonのジェネレータより扱いやすい点もありそう。&lt;br /&gt;ここは要調査。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-8541455251493152217?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/8541455251493152217/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/04/clojure_29.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/8541455251493152217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/8541455251493152217'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/04/clojure_29.html' title='Clojureの紹介'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-2849029693257190993</id><published>2009-04-29T21:33:00.005+09:00</published><updated>2009-04-29T22:26:57.363+09:00</updated><title type='text'>Clojureのlistとvector初見</title><content type='html'>Clojureは便利な組み込みのデータ型を備えている。&lt;br /&gt;これらはぜんぶが全部immutable、変更不可能になっている。&lt;br /&gt;でも参照を扱う仕組みが別にあって、参照先が変更可能なので、&lt;br /&gt;ErlangやHaskellより命令的に破壊的な操作？を書ける。&lt;br /&gt;そこはSTMと絡んでくるので後回し。&lt;br /&gt;&lt;br /&gt;Clojureは動的型で、Pythonの組み込み関数typeと同じように&lt;br /&gt;型について調べる事ができる。&lt;br /&gt;&lt;pre class="prettyprint"&gt;user&gt; (type 1)&lt;br /&gt;java.lang.Integer&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;整数や文字列などはJavaのクラスがそのまま使われているようだ。&lt;br /&gt;JavaのVMの仕組みはわからないけれど、&lt;br /&gt;Javaは静的型なので、こういったインスタンスやクラスの情報は&lt;br /&gt;なんらかの形でラップされて扱われているんだろうと思われる。&lt;br /&gt;型もふつうのオブジェクトのようで、比較ができる。&lt;br /&gt;&lt;pre class="prettyprint"&gt;user&gt; (= (type 1) (type 3))&lt;br /&gt;true&lt;br /&gt;user&gt; (= (type 1) (type 30000000000000000000000))&lt;br /&gt;false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;typeのtype、Pythonだとメタクラスになるところだが、&lt;br /&gt;おなじようなものだろう。&lt;br /&gt;&lt;pre class="prettyprint"&gt;user&gt; (type (type 1))&lt;br /&gt;java.lang.Class&lt;br /&gt;user&gt; (type (type (type 1)))&lt;br /&gt;java.lang.Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;文字列や、大きい整数もこんな具合。&lt;br /&gt;&lt;pre class="prettyprint"&gt;user&gt; (type "abc")&lt;br /&gt;java.lang.String&lt;br /&gt;user&gt; (type 10000000000000)&lt;br /&gt;java.lang.Long&lt;br /&gt;user&gt; (type 100000000000000000000000000)&lt;br /&gt;java.math.BigInteger&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;コンテナ型は、Clojure独自に定義されている。&lt;br /&gt;immutableということで、PersistentHoge&lt;br /&gt;という名前が付けられている。&lt;br /&gt;&lt;pre class="prettyprint"&gt;user&gt; (type [])&lt;br /&gt;clojure.lang.PersistentVector&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これはPersistentVector。単にvectorと呼ばれることの方が多い。&lt;br /&gt;IPersistentVectorというのは明らかにインターフェースと思われるが&lt;br /&gt;MultiMethodsと関係してくるのだろうか？今は不明。&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;user&gt; (type ())&lt;br /&gt;clojure.lang.PersistentList$EmptyList&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;こっちはPersistentList。EmptyListとついているのが気になる。&lt;br /&gt;これがうわさのMetadataか？&lt;br /&gt;listとvectorが別にある。vectorの方が可変長配列でlistはリンクリスト&lt;br /&gt;ってことだろうか。&lt;br /&gt;listは当然だがvectorにもリテラルが用意されている。&lt;br /&gt;&lt;pre class="prettyprint"&gt;user&gt; [1 2 3 4]&lt;br /&gt;[1 2 3 4]&lt;br /&gt;user&gt; [1, 2, 3, 4]&lt;br /&gt;[1 2 3 4]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;カンマは、あっても無くても同じ。&lt;br /&gt;好き嫌いが分かれそうだが、僕は嫌いじゃない。&lt;br /&gt;関数で生成することもできる。&lt;br /&gt;&lt;pre class="prettyprint"&gt;user&gt; (vector 1 2 3 4)&lt;br /&gt;[1 2 3 4]&lt;br /&gt;user&gt; (vector)&lt;br /&gt;[]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;listのほうはこんなかんじ。&lt;br /&gt;&lt;pre class="prettyprint"&gt;user&gt; '(1 2 3 4)&lt;br /&gt;(1 2 3 4)&lt;br /&gt;user&gt; '(1, 2, 3, 4)&lt;br /&gt;(1 2 3 4)&lt;br /&gt;user&gt; (list 1 2 3 4)&lt;br /&gt;(1 2 3 4)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lispですね。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-2849029693257190993?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/2849029693257190993/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/04/clojurelistvector.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/2849029693257190993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/2849029693257190993'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/04/clojurelistvector.html' title='Clojureのlistとvector初見'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3816569039331410742.post-9145042692944212309</id><published>2009-04-29T10:45:00.007+09:00</published><updated>2009-05-12T14:25:26.513+09:00</updated><title type='text'>Clojureのインストール（Ubuntu 9.04）</title><content type='html'>&lt;a href="http://riddell.us/tutorial/clojure/clojure.html"&gt;Clojure on Ubuntu&lt;/a&gt;&lt;br /&gt;を参考にUbuntu 9.04にClojureをインストール。&lt;br /&gt;&lt;pre class="prettyprint"&gt;sudo apt-get install openjdk-6-jdk&lt;br /&gt;sudo apt-get install ant subversion git-core&lt;br /&gt;mkdir ~/opt&lt;br /&gt;cd ~/opt&lt;br /&gt;svn co http://clojure.googlecode.com/svn/trunk clojure&lt;br /&gt;svn up&lt;br /&gt;cd clojure&lt;br /&gt;ant&lt;br /&gt;mkdir ~/.clojure&lt;br /&gt;cp clojure.jar ~/.clojure&lt;br /&gt;cd ~/.clojure&lt;br /&gt;java -cp clojure.jar clojure.lang.Repl&lt;br /&gt;user=&gt; (+ 1 2)&lt;br /&gt;3&lt;br /&gt;cd ~/opt&lt;br /&gt;git clone git://github.com/kevinoneill/clojure-contrib.git&lt;br /&gt;cd clojure-contrib&lt;br /&gt;git pull&lt;br /&gt;ant -Dclojure.jar=../clojure/clojure.jar&lt;br /&gt;cp *.jar ~/.clojure&lt;br /&gt;emacs ~/.profile&lt;br /&gt;=============&lt;br /&gt;export CLOJURE_EXT=~/.clojure&lt;br /&gt;PATH=$PATH:~/opt/clojure-contrib/launchers/bash&lt;br /&gt;alias clj=clj-env-dir&lt;br /&gt;=============&lt;br /&gt;&lt;br /&gt;user=&gt; (System/getProperty "java.class.path")&lt;br /&gt;"/home/xxx/.clojure/clojure.jar&lt;br /&gt;:/home/xxx/.clojure/clojure-contrib-slim.jar&lt;br /&gt;:/home/xxx/.clojure/clojure-contrib.jar"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3816569039331410742-9145042692944212309?l=ctrkode-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ctrkode-clojure.blogspot.com/feeds/9145042692944212309/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/04/export-clojureext.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/9145042692944212309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3816569039331410742/posts/default/9145042692944212309'/><link rel='alternate' type='text/html' href='http://ctrkode-clojure.blogspot.com/2009/04/export-clojureext.html' title='Clojureのインストール（Ubuntu 9.04）'/><author><name>pokarim</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
