Module: UnificationAssertion
- Includes:
- MiniTest::Assertions
- Defined in:
- lib/unification_assertion.rb,
lib/unification_assertion/version.rb
Constant Summary collapse
- VERSION =
"0.0.1"
- @@comparators =
{}
- @@substituters =
{}
Class Method Summary collapse
-
.assert_unifiable(a, b, original_message = "", options = {}, &block) ⇒ Object
Run unification between |a| and |b|, and fails if they are not unifiable.
-
.comparators ⇒ Object
Comparators are hash from a class to its comparator.
- .substitute(unifier, a) ⇒ Object
- .substitutions ⇒ Object
-
.unify(eqs, unifier = {}, options = {}, &block) ⇒ Object
Run unification algorithm for given equations.
Class Method Details
.assert_unifiable(a, b, original_message = "", options = {}, &block) ⇒ Object
Run unification between |a| and |b|, and fails if they are not unifiable. |assert_unifiable| can have block, which yields the unifier for |a| and |b| if exists.
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/unification_assertion.rb', line 143 def assert_unifiable(a, b, = "", = {}, &block) msg = proc {|eq, path| header = if == nil or .length == 0 else "No unification" end = "\nCould not find a solution of equation at it#{path}.\n=> #{mu_pp(eq[0])} == #{mu_pp(eq[1])}" (header, ) { a_pp = mu_pp(a) b_pp = mu_pp(b) "=> #{a_pp}\n=> #{b_pp}" } } unifier = unify([[a, b, ""]], {}, ) do |x, y, path| assert(x==y, msg.call([x, y], path)) end if block yield(unifier) end end |
.comparators ⇒ Object
Comparators are hash from a class to its comparator. It is used to check unifiability of two object of the given hash.
There are two comparators defined by default; for |Array| and |Hash|.
Example ==
UnificationAssertion.comparators[Array] = lambda do |a, b, , eqs, unifier, &block|
block.call(a.length, bl.ength, + " (Array length mismatch)")
eqs.concat(a.zip(b))
end
This is our comparator for |Array|. It first tests if the length of the two arrays are equal. And then, it pushes the equations of each components of the arrays. The components of the arrays will be tested unifiability later.
Comparator ==
Comparator is a lambda which takes 5 arguments and block and checks the unifiability of the first two arguments.
-
|a|, |b| Objects they are compared.
-
|message| Message given to |unify|.
-
|eqs| Equations array. This is for output.
-
|unifier| Current unifier. This is just for reference.
-
|&block| Block given to |unify|, which can be used to check the equality of two values.
52 53 54 |
# File 'lib/unification_assertion.rb', line 52 def comparators @@comparators end |
.substitute(unifier, a) ⇒ Object
131 132 133 134 135 136 137 138 |
# File 'lib/unification_assertion.rb', line 131 def substitute(unifier, a) subst = @@substituters[a.class] if subst subst.call(unifier, a) else a end end |
.substitutions ⇒ Object
127 128 129 |
# File 'lib/unification_assertion.rb', line 127 def substitutions @@substituters end |
.unify(eqs, unifier = {}, options = {}, &block) ⇒ Object
Run unification algorithm for given equations. If all equations in |eqs| are unifiable, |unify| returns (the most-general) unifier.
Which identifies an symbol as a meta variable if the name matches with |options|. The default of the meta variable pattern is |/^_/|. For example, |:_a| and |:_xyz| are meta variable, but |:a| and |:hello_world| are not.
It also accepts wildcard variable. Which matches with any value, but does not introduce new equational constraints. The default wildcard is |:_|.
|unify| takes a block to test equation of two values. The simplest form should be using |assert_equal|, however it can be customized as you like.
Example ==
unify([[:_a, 1], [{ :x => :_b, :y => 1 }, { :x => 3, :y => 1 }]], "Example!!") do |x, y, |
assert_equal(x,y,)
end
The |unify| call will return an hash |{ :_a => 1, :_b => 3 }|. The block will be used to test equality between 1 and 1 (it will pass.)
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/unification_assertion.rb', line 77 def unify(eqs, unifier = {}, = {}, &block) = { :meta_pattern => /^_/, :wildcard => :_ }.merge!() pattern = [:meta_pattern] wildcard = [:wildcard] while eq = eqs.shift a,b,path = eq case when (Symbol === a and a.to_s =~ pattern) unless a == wildcard eqs = substitute({ a => b }, eqs) unifier = substitute({ a => b }, unifier).merge!(a => b) end when (Symbol === b and b.to_s =~ pattern) unless b == wildcard eqs = substitute({ b => a }, eqs) unifier = substitute({ b => a }, unifier).merge!(b => a) end when (a.class == b.class and @@comparators[a.class]) @@comparators[a.class].call(a, b, eqs, unifier, path, block) else yield(a, b, path) end end unifier.inject({}) {|acc, (key, value)| if key == value acc else acc.merge!(key => value) end } end |