Tablex
An implementation of Decision Table in Elixir.
Install / Use
/learn @elixir-tablex/TablexREADME
Tablex - Decision Tables in Elixir Code
Tablex is an implementation of the [Decision Table][] in Elixir. Its goal is to make maitaining domain rules easy.
Quick demo
Let's assume we decide what to do everyday based on day of week and the weather, as the following table indicates:
<table class="tablex horizontal"><colgroup> <col span="1" class="rule-number"> <col span="2" class="input"> <col span="1" class="output"> </colgroup> <thead><tr><th class="hit-policy hit-policy-F"></th><th class="input">day (string)</th><th class="input">weather (string)</th><th class="output">activity</th></tr></thead><tbody><tr><td class="rule-number">1</td><td rowspan="2" class="input">Monday, Tuesday, Wednesday, Thursday</td><td class="input">rainy</td><td class="output">read</td></tr><tr><td class="rule-number">2</td><td class="input">-</td><td class="output">read, walk</td></tr><tr><td class="rule-number">3</td><td rowspan="2" class="input">Friday</td><td class="input">sunny</td><td class="output">soccer</td></tr><tr><td class="rule-number">4</td><td class="input">-</td><td class="output">swim</td></tr><tr><td class="rule-number">5</td><td class="input">Saturday</td><td class="input">-</td><td class="output">watch movie, games</td></tr><tr><td class="rule-number">6</td><td class="input">Sunday</td><td class="input">-</td><td class="output">null</td></tr></tbody></table>We can use a similar tabular form of the code in an Elixir program:
...> plans = Tablex.new("""
...> F day (string) weather (string) || activity
...> 1 Monday,Tuesday,Wednesday,Thursday rainy || read
...> 2 Monday,Tuesday,Wednesday,Thursday - || read,walk
...> 3 Friday sunny || soccer
...> 4 Friday - || swim
...> 5 Saturday - || "watch movie",games
...> 6 Sunday - || null
...> """)
...>
...> Tablex.decide(plans, day: "Monday")
%{activity: ["read", "walk"]}
...>
...> Tablex.decide(plans, day: "Friday", weather: "sunny")
%{activity: "soccer"}
...>
...> Tablex.decide(plans, day: "Sunday")
%{activity: nil}
The above code demonstrates how we can determine what to do based on a set of rules which are represented in a decision table on day and weather condition.
Inside the table, we defined the decision logic with:
- An indicator of hit policy,
Fin this case meaning the first rule matched will be applied. SeeHit Policiessection for more information. - Two input stubs,
dayandweatherwhich are both strings. SeeInput Stubssection - An output stub,
activityin this case. SeeOutput Stubssection - Six rules which take inputs and determine the activity output. See
Rulessection - A friendly expression in each cell of the rules. See
Expressionsection
Vertical Table
Vertical tables are the same as horizontal ones. It's just a matter of direction. The following tables are the same:
F product_category competitor_pricing product_features || launch_decision reasoning
1 Electronics "Higher than Competitor" "More Features" || Launch "Competitive Advantage"
2 Electronics "Lower than Competitor" "Same Features" || Launch "Price Advantage"
3 Fashion "Same as Competitor" "New Features" || "Do Not Launch" "Lack of Differentiation"
<table class="tablex horizontal"><colgroup><col span="1" class="rule-number"><col span="3" class="input"><col span="2" class="output"></colgroup><thead><tr><th class="hit-policy hit-policy-F">F</th><th class="input">Product Category</th><th class="input">Competitor Pricing</th><th class="input">Product Features</th><th class="output">Launch Decision</th><th class="output">Reasoning</th></tr></thead><tbody><tr><td class="rule-number">1</td><td rowspan="2" class="input"><span class="tbx-exp-string">Electronics</span></td><td class="input"><span class="tbx-exp-string">Higher than Competitor</span></td><td class="input"><span class="tbx-exp-string">More Features</span></td><td class="output"><span class="tbx-exp-string">Launch</span></td><td class="output"><span class="tbx-exp-string">Competitive Advantage</span></td></tr><tr><td class="rule-number">2</td><td class="input"><span class="tbx-exp-string">Lower than Competitor</span></td><td class="input"><span class="tbx-exp-string">Same Features</span></td><td class="output"><span class="tbx-exp-string">Launch</span></td><td class="output"><span class="tbx-exp-string">Price Advantage</span></td></tr><tr><td class="rule-number">3</td><td class="input"><span class="tbx-exp-string">Fashion</span></td><td class="input"><span class="tbx-exp-string">Same as Competitor</span></td><td class="input"><span class="tbx-exp-string">New Features</span></td><td class="output"><span class="tbx-exp-string">Do Not Launch</span></td><td class="output"><span class="tbx-exp-string">Lack of Differentiation</span></td></tr></tbody></table>
====
F || 1 2 3
product_category || Electronics Electronics Fashion
competitor_pricing || "Higher than Competitor" "Lower than Competitor" "Same as Competitor"
product_features || "More Features" "Same Features" "New Features"
====
launch_decision || Launch Launch "Do Not Launch"
reasoning || "Competitive Advantage" "Price Advantage" "Lack of Differentiation"
<table class="tablex vertical"><tbody><tr><th class="hit-policy hit-policy-F">F</th><th class="rule-number">1</th><th class="rule-number">2</th><th class="rule-number">3</th></tr></tbody><tbody><tr><th class="input">Product Category</th><td colspan="2"><span class="tbx-exp-string">Electronics</span></td><td><span class="tbx-exp-string">Fashion</span></td></tr><tr><th class="input">Competitor Pricing</th><td><span class="tbx-exp-string">Higher than Competitor</span></td><td><span class="tbx-exp-string">Lower than Competitor</span></td><td><span class="tbx-exp-string">Same as Competitor</span></td></tr><tr><th class="input">Product Features</th><td><span class="tbx-exp-string">More Features</span></td><td><span class="tbx-exp-string">Same Features</span></td><td><span class="tbx-exp-string">New Features</span></td></tr></tbody><tfoot><tr><th class="output">Launch Decision</th><td><span class="tbx-exp-string">Launch</span></td><td><span class="tbx-exp-string">Launch</span></td><td><span class="tbx-exp-string">Do Not Launch</span></td></tr><tr><th class="output">Reasoning</th><td><span class="tbx-exp-string">Competitive Advantage</span></td><td><span class="tbx-exp-string">Price Advantage</span></td><td><span class="tbx-exp-string">Lack of Differentiation</span></td></tr></tfoot></table>
## Input Stubs
Inputs can be defined with a set of name (type[, description]) pairs. For example:
Age (integer)defines an input field whose name is "age" and type is integer.DOB (date, date of birth)defines a date input field with a description label.
Name
Names can contain spaces in them if they are quoted. The following names are valid:
year_month_dayyearMonthDay"year month day"
They will all be converted to year_month_day.
Type
Currently the following types are supported:
- integer
- float
- number
- string
- bool
When types are specified, the input value shall be of the same type as specified.
Output Stubs
Output stubs are defined as name (type[, description]) where
- name can be a string which will be converted to an underscored atom;
- type can be either of the supported types (the same as inputs, see above section);
- description is optional and is currently ignored.
Rules
After output stub definitions, each of the following rows defines a rule entry, with the format:
rule_number input_exp_1 input_exp_2 ... input_exp_m || output_exp_1 output_exp_2 ... output_exp_n
Rule number is primarily used for ordering. The rule with the lowest rule number has the highest priority. Input expressions and output expressions are separated by "||".
Expression
Currently only these types are supported:
- literal numeric value: integer and float (without scientific notation)
- literal quoted string in
" - boolean
- comparison:
>,>=,<,<= - range, e.g.
5..10 - nil ("null")
- list of numeric, string, range, bool, nil or comparison; can be mixed
- any ("-")
The following types of expressions are planned:
- date
- time
- datetime
- function
Hit policies
There are several hit policies to indicate how matched rules are applied.
F (First matched)- the first matched rule will be applied.C (Collect)- all matched rules will be collected into result list.M (Merge)- all matched rules will be reduced (merged) into a single return entry, until there's no-in the output.R (Reverse Merge)- similar tomergebut in a reversed order.
Examples:
First Hit
iex> table = Tablex.new("""
...> F age (integer) || f (float)
...> 1 > 60 || 3.0
...> 2 50..60 || 2.5
...> 3 31..49 || 2.0
...> 4 15..18,20..30 || 1.0
...> 5 - || 0
...> """
...> )
...>
...> Tablex.decide(table, age: 30)
%{f: 1.0}
iex> Tablex.decide(table, age: 55)
%{f: 2.5}
iex> Tablex.decide(table, age: 22)
%{f: 1.0}
iex> Tablex.decide(table, age: 17)
%{f: 1.0}
iex> Tablex.decide(table, age: 1)
%{f: 0}
iex> table = Tablex.new("""
...> F age (integer) years_of_service || holidays (integer)
...> 1 >=60 - || 3
...> 2 45..59 <30 || 2
...> 3 - >=30 || 22
...> 4 <18 - || 5
...
Related Skills
node-connect
345.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
106.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
345.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
345.9kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
