コンテンツにスキップ

自分のルールが入るか

rulec が相手にするのは、一件の取引を、そのままの値から一度で判定して、金額・可否・区分・順序のどれかを返す規則です。自分のルールがそれに入るかを、このページで確かめられます。五つの問いと、よく訊かれる二つ — 按分と、向いていないもの — です。最後に、この言語がなぜこの形なのかと、似たものを並べます。

五つの問い

全部 はい なら入ります。お金の有無は条件に入りません。

  1. 入力がそのままの値か(注文.配送先.都道府県 ではなく、都道府県そのものを渡せる)
  2. 値の個数が決まっているか。決まっていなくても、同じ形の要素が並んでいるだけなら書けます(elements と fold。ルール(.rule)を書く)
  3. 一回の判定か(並べ替えと、判定をまたぐ繰り返しは呼び出し側に置けるか。一回の判定が次に残す状態は、machine で渡し直せます)
  4. 同じ入力ならいつでも同じ答えか(「今日」も在庫も引数として渡す)
  5. その答えを人が承認するか、期日で改定されるか

1〜4 のどれかが いいえ なら、構造として入りません。5 だけが いいえ なら、動きはしますがツールが過剰です。

お金が絡まなくても構いません

たとえばこれは、円が一度も出てこない規則です。日付が一つ入って、区分が一つ出るだけ。そのまま検査を通ります。

rule 期間区分(period) v1

enum 期間(kind) = 改定前(before) | 春季(spring) | 通常(normal) | 年末(year_end)

inputs
  注文日(order_date) : date  range >=2026-01-01 <=2026-12-31

outputs
  区分(kind) : 期間

table 期間判定(pick)
policy unique
| 注文日                    | -> 区分(kind) : 期間 |
| <=2026-03-31              | 改定前               |
| >=2026-04-01 <=2026-06-30 | 春季                 |
| >=2026-07-01 <=2026-11-30 | 通常                 |
| >=2026-12-01              | 年末                 |

決め手は判定の形であって、扱う値が何かではありません。だから「送料のためのツール」でも「EC のためのツール」でもありません。

法令の条文も、同じ形で書けます

法令には、表の形をした規定がたくさんあります。印紙税法の別表第一のような税額表はそのまま表になり、その上に軽減税率やただし書や準用が重なります。その重なり方を、そのまま書けるようにしてあります。

条文の形 書き方
本則と、それに優先する特例(印紙税の本則と、租税特別措置法の軽減税率) 表を二つ書き、特例の表に overrides 本則 と一行添える
ただし書のように、条件が列に並ばない一行の決まり 表にせず、文のまま clause で書く
準用(「第 20 条の規定は…準用する。この場合において『勤続年数』とあるのは『在職期間』と読み替える」) apply で、読み替えをそのまま書く
どの文書のどこから転記したか source で文書を宣言し、行末に @法 第91条(料金表や社内規程なら @規約 表1)と書いて引用する。文書は、法令なら e-Gov 法令検索(政府の法令データベース)から取った条文のコピー、規約や料金表なら隣に置いたファイル。規則はそのコピーのハッシュに縛られ、コピーが変われば、それを引用している表を名指しして止まる。表を引いたなら、その表の金額がコピーと食い違えば落ちる

検査は、本則と特例を合わせて完全性と重なりを見ます。準用では、この規則が渡す値が元の規則の範囲に収まることまで証明します。人が読む資料には、引用した条文がそのまま載ります。書き方はルール(.rule)を書くに、動く例は例で見るにあります。

按分は、どちらのやり方でも書けます

値引きを明細に配分するような按分は、配り方の決め方が二通りあり、どちらも書けます。

順に充てていく形。「値引きを、明細に順に、上限まで充てる」。一明細ぶんの判定だけを規則にして、繰り返しは呼び出し側に置きます。

inputs
  明細定価(list)      : money[円, incl_tax]  range >=0円 <=100万円
  残り値引(remaining) : money[円, incl_tax]  range >=0円 <=100万円
  対象(eligible)      : bool

outputs
  充当額(applied) : money[円, incl_tax]  round down(1円)

define 充てられる額(cap) : money[円, incl_tax] = min(明細定価, 残り値引)

table 充当可否(applies)
policy unique
| 対象  | -> 充当(on) : money[円, incl_tax] |
| true  | 充てられる額                      |
| false | 0円                               |

result 充当額 = 充当

呼び出し側は明細を順に回して 残り値引 を引いていくだけです。この形なら合計は必ずぴったり合います — 配りすぎも配り残しも起きません(2,000 通りの明細で確かめました)。

定価の比で割り付ける形。 「明細の定価の比で按分する」は allocate で書けます。割り算が定数でしか書けないのは変わりませんが(変数で割ると E115)、配分だけはその一つの例外です。

constraint ここまでの定価 <= 定価合計

derive ここまでの配分(to_upto) : money[円] = allocate(値引き総額, ここまでの定価, 定価合計)  range >=0円 <=100万円

result 配分額 = ここまでの配分 - 直前までの配分

呼び出し側が持つのは定価の累計だけです。明細を順に回しながら足していきます。一行ぶんは「ここまでの配分」から「直前までの配分」を引いた差で、この形なら端数は最後の行に寄り、配った合計は総額にぴったり一致します。上の「順に充てる」形は 2,000 通りの明細で確かめただけでしたが、こちらは proofs/ に定理があります。

向いていないもの

  • ワークフローを動かすこと — 段階そのもの、段階のあいだの待ち、外への副作用。状態を持つ手続きの一歩は書けます(machine。注文の状態を、状態とイベントの表にします。どんな呼び出しの並びでも起きてはいけないことを check が証明し、状態は呼び出す側が持ちます)。いくつものステートマシンを同時に動かすこと、呼び出しをまたいで数を持ち越すこと、段階のあいだの時間は書けません
  • 並びの平均 — 件数で割ることになり、変数で割る式は書けません。呼び出す手前で出して、値として渡します。合計と件数は書けます(sum。「明細の合計が 1 万円を超える」は表の行になります。count なら「冷蔵品が 3 件以上」)し、並びから一件を選ぶことも書けます(fold。「どれかが冷蔵品なら受け付けない」「いちばん高い行を採る」)
  • 文字列を丸ごと分岐に使うこと — string の列に書けるのは前方一致だけです(starts_with "CH-"。それ以外は E110)。SKU やカテゴリの頭で分けるならそれで足ります。値が数えられるなら enum のほうが向いています — 閉じた集合なので、どの値にも行があるかまで検査できます。部分一致と正規表現はありません
  • 入力そのものを決めること — 「この問い合わせは請求か技術か」「この傷は軽微か」。曖昧な状態から値を決めるのは、人かモデルの仕事です。決めた値を引数として渡してください。そうすれば規則は純関数のままで、過去再生も差分もそのまま効きます。確信度で分けるかどうかは、それ自体が業務の判断なので表に書けます(確信度 : rate[step 0.1%] の列を持つ表にすれば、閾値の穴と重なりは検査に掛かります)。誰がその値を決めたかは記録に残せます(形式の by)
  • 重みや閾値そのものを決めること — 最適化や機械学習の領分です。決まった重みで点数を合算してランクを付けるのは書けます(例で見るの「評価ランク」がそれで、金額はどこにも出てきません)。ただし証明できるのは「どの行に当てはまるか」であって、その重みが妥当かどうかではありません

なぜこの形を狙うのか

次の五つが同時に成り立つ領域だからです。ツールの機能はどれも、この五つのどれかが理由になっています。

ルールの性質 それに対してツールがしていること
間違えると実害が出る 近似して通すことをしない。証明できなければ止まる。実害がお金なら単位と税区分の型、丸めの宣言が効き、実害が「通らない/通ってしまう」なら抜けと重なりの検査が効く
正しい形が別のところに書いてある 規約、運賃表、約款。仕事はゼロから設計することではなく転記することです。だから最初の価値が「転記した瞬間に抜けが出る」になる
条件が絡み合う あて先 × サイズ × 重量、種別 × 期間 × 会員区分。if 一本では書けず、組み合わせを網羅できたかは目で確かめられない
期日で改定される 運賃改定、キャンペーン期間、規約変更。だから版を指して比べられ、変更が何件をいくら動かすかを、入れる前に出せる
書く人と決める人が違う 金額も、丸めの向きも、区分の切れ目も業務の判断。だから人が読む資料を書き出せて、検査は「決められないこと」を具体例つきの質問に変える

言語としての狙い

一つのファイルに三役を兼ねさせることです。同じ .rule が、人が読んで確かめる仕様であり、検査器が証明する対象であり、生成コードのもと。この三つが別々のファイルに分かれた瞬間、どれかが必ず腐ります。そして腐るのは、たいてい仕様のほうです。

似たものは何があるか

決定表は新しくありませんし、表を検査することも新しくありません。 表に抜けと重なりが無いことを証明する仕事は 1990 年代の形式手法からあり、ルールエンジンの多くは、編集しているそばから表を検査します。隣にあるものを、正直に並べます(どれも実際に使ってはいないので、公開情報から読んだ範囲です)。

それは何か rulec との違い
形式手法の表(SCR(米海軍研究所)、PVS の表、Tabular Expression Toolbox) 仕様を表で書き、抜けと重なりが無いことを証明する。成り立たなければ、それを起こす場合を返す(SCR、PVS) 二つの性質も、それを欠く表を受け付けないという考え方も、こちらが先です。対象は制御ソフトウェアで、使うのは定理証明器でした。rulec はそれを料金表や規約や法令に向け、金額、単位、丸めを持たせ、アプリケーションを書く言語のコードとして出します
DMN(OMG の標準)と、その実装(Apache KIE / Drools、Camunda、jDMN、Kogito、Trisotech ほか) 決定表の業界標準。ヒットポリシーがあり、抜けと重なりの静的解析を持つ実装がいくつもある(Drools DMN、dmn-check)。jDMN は Java を生成する セルに書ける式が広い(FEEL は式言語)ぶん、完全性の判定は一般には難しく、解析はその部分集合が相手になります。rulec は一つのセルが自分の列しか見ないこと(二つの列をまたぐ条件は書けない)にして、抜けと重なりを厳密に決められる側に置いています。単位や税区分の型、丸めの必須宣言、int64 の証明、複数言語への生成、いま動いている実装との突き合わせは DMN の範囲外です
ルールエンジン(Drools DRL、IBM ODM、Pega、SAP BRFplus、GoRules / ZEN、OpenRules、OpenL Tablets ほか) 規則を実行時にライブラリかサービスで評価する。多くは、編集中の決定表の抜けと重なりも検査する(IBM ODM)。SAP BRFplus は、検査を通らない表を有効にさせない設定もできる。IBM ODM は列の定義域を XML Schema から取れるので、そこに足された値は表の抜けとして見える rulec はエンジンを配りません。出るのは依存ゼロの普通の関数で、実行時に rulec は存在しません
Corticon(Progress、商用) rulesheet に矛盾チェッカと完全性チェッカがある。狙いとしてはいちばん近い 商用・専用ランタイム。rulec はふつうのソースコードを渡して終わりで、差分の突き合わせ(verify / replay / diff)までツール側に持っています
LF-ET(Lohrfink、商用) 決定表の完全性、冗長、矛盾を検査し、そこから多くの言語のコード(COBOL や ABAP も含む)を出す。実行時にエンジンは要らない 出てくるものの形がいちばん近い相手です。LF-ET の条件は、対象の言語で書いた式に名前を付けたもので、検査はその真偽の組み合わせについてです。rulec のセルは自分の列の範囲か集合なので、検査が値そのものに届き、単位、丸め、int64 も一緒に見ます
Catala(INRIA) 法令をそのままプログラムにする言語。正しさを最優先に設計され、複数言語へコンパイルされる。証明のプラグインが、定義が空になる場合と、二つの例外が同時に当てはまる場合があるかを Z3 に訊き、その場合を返す。結果は警告で、ビルドは止めない 精神としてはいちばん近い親戚です。形は違って、Catala は法文の構造(既定と例外)をそのままなぞる言語で、決定表ではありません。rulec も本則に優先する特例、ただし書、準用を持ちますが、単位はあくまで表で、抜けと重なりは区画の計算で決めます。Catala は任意精度で計算し、金額は自動でセントに丸めます。rulec はどの値も int64 に収まることを示し、丸めは宣言させるので、生成したどの言語も同じ答えを返します。単位と税区分の型や、いま動いている実装との照合は Catala にはありません
API の契約の検査(buf breaking、oasdiff) .proto や OpenAPI の変更が、ワイヤの上で互換かを見る 契約が値を渡した先で何が決まるかは、意図して見ません。buf breaking は Protovalidate の注釈のようなカスタムオプションを読みませんし、リクエストの列挙に値が増えることは、どちらにとっても壊れる変更ではありません。rulec は契約を規則の入力と突き合わせるので(E032、E033、E122)、ワイヤの上では互換でも、どの行にも当てはまらない値を通す変更は CI で落ちます
Morphir(FINOS) 業務ロジックを一つの中間表現でモデル化し、複数の対象へ出す 対象が広いぶん、決定表そのものの完全性検査は目的ではありません

一つひとつは、どこかに既にあります。 抜けと重なりの証明は SCR のころから、編集中の検査は多くのルールエンジンに、それを起こす場合を返す静的な検査は Catala に、検査した表から多くの言語のコードを出すのは LF-ET に、改定が過去の記録をどう動かすかはルールエンジンのシミュレーションにあります。ほかで見つからなかったものは二つです。一つは、検査が証拠を渡すこと。rulec certificate が証明の根拠を出し、依存の無い Python のファイル一つと、Lean で書いた検査と証明から作ったプログラムがそれを再検査します。Lean の側では、検査が通れば主張が成り立つことを証明しています。結果と一緒に証拠を出すアルゴリズム(certifying algorithm)の形を、業務の表に当てたものです。もう一つは、API の契約を、その検証が書く範囲、件数、必須、列挙の値まで含めて、契約の側の CI で規則の入力と突き合わせること。変更がワイヤに何をするかだけでなく、判断に何をするかまで見ます。列の定義域を XML Schema から取るルールエンジンでも、列挙に足された値は抜けとして見えますが、それはエンジンの中での話です。それ以外は組み合わせです。セルを絞って抜けと重なりを厳密に決め、それを起こす入力そのものを必ず返し、単位と丸めを型と宣言で縛り、12 の言語へ依存ゼロで出し、いま動いている実装や過去のデータと突き合わせ、その全部を AI エージェントが --format json だけで回せる。まとまった形と、エージェントを第一の利用者に据えた設計も、このツールの立ち位置です。


ルール(.rule)を書く 例で見る