---
title: Clojureの関数にcondition-mapでPre・Postチェック
tags: []
categories: ["Programming", "Lisp", "Clojure"]
date: 2010-03-24T17:08:44Z
updated: 2010-03-24T18:15:05Z
---

<p>Clojureの関数の形式は実は</p>
<pre class="prettyprint lang-cl">
(defn function-name doc-string? attr-map? [parameter-list]
  conditions-map?
  (expressions))
</pre>
<p>です。<code> doc-string?</code>はドキュメント、<code>attr-map?</code>はメタ情報です。引数の後にある<code>condition-map?</code>はなんぞやというと、関数の入出力の条件を設定できるmapです。(<a href="http://bit.ly/b3sGEm">Clojure 1.1.0から</a>かな？)
<br />
<code>condition-map?</code>の形式は
</p>
<pre class="prettyprint lang-cl">
{:pre 入力値をチェックする条件S式のシーケンス, :post 出力値をチェックする条件S式のシーケンス}
</pre>
<p>
です。例えば、引数が全て正でかつ、関数の結果が10より大きいという条件を付けたい場合、
</p>
<pre class="prettyprint lang-cl">
(defn hoge [x y]
  {:pre [(> x 0) (> y 0)]
   :post [(> % 10)]}
  (+ x y))
</pre>
<p>
という風になります。%には返り値が入ります。<br />
条件を満たさない場合は、<code>java.lang.AssertionError</code>が発生します。<br />
(ちなみにleiningen1.0.1のreplのバージョンであるClojure 1.1.0-master-SNAPSHOTでは<code>java.lang.Exception</code>でした。。。これはExceptionかErrorかという大きな違いであり、明文化が待たれます)
<br />
<br />
実行してみると、
</p>
<pre class="prettyprint lang-cl">
user=> (hoge 5 6)
11
user=> (hoge 0 11)
java.lang.AssertionError: Assert failed: (> x 0) (NO_SOURCE_FILE:0)
user=> (hoge 11 0) 
java.lang.AssertionError: Assert failed: (> y 0) (NO_SOURCE_FILE:0)
user=> (hoge 5 5) 
java.lang.AssertionError: Assert failed: (> % 10) (NO_SOURCE_FILE:0)
</pre>
<p>
となります。
</p>
<h3>モジュラリティを高める</h3>
<p>
このままだと常にチェックが走りますが、単純に関数を使う場合を分けたいことがあると思います。次のように書くと、関数の機能とチェック機能を分離させることができ、モジュラリティを高めることができます。
</p>
<pre class="prettyprint lang-cl">
user=> (defn hoge [x y] (+ x y))
#'user/hoge
user=> (hoge 5 5)
10
user=> (defn with-condition [f x y] {:pre [(> x 0) (> y 0)], :post [(> % 10)]} (f x y))
#'user/with-condition
user=> (with-condition hoge 5 5)
java.lang.AssertionError: Assert failed: (> % 10) (NO_SOURCE_FILE:0)
</pre>
<p><code>partial</code>で部分適用しておけば、元の関数を同じものを定義できますね。</p>
<pre class="prettyprint lang-cl">
user=> (def always-condition (partial with-condition hoge))
#'user/always-condition
user=> (always-condition 5 5)
java.lang.AssertionError: Assert failed: (> % 10) (NO_SOURCE_FILE:0)
</pre>
<hr />
<p>
エンタープライズを乗っとるには堅牢性が必要になると思います。<code>condition-map?</code>を上手に使うことは乗っ取りの第一歩ですね！
</p>
<p>
ちなみにここ数回のClojureネタは<a href="http://bit.ly/2bHmQG">Clojure in Action</a>(MEAP版)を読んで、おおっと思ったことのまとめです。
<br />
<a href="http://bit.ly/2bHmQG"><img src="http://www.manning.com/rathore/rathore_cover150.jpg" /></a>
</p>
