wizardofosmium / porolog

Plain Old Ruby Objects Prolog
4 stars 0 forks source link

question: how to refute, or delete, a fact #19

Open abargnesi opened 1 year ago

abargnesi commented 1 year ago

Hello,

Thanks for creating a great prolog library for Ruby.

I have asserted a fact that I would like to refute afterward. Calling fact.(42).fallacy! adds a goal that results in false, but does not remove the true goal, therefore fact(42) can still be solved.

require 'porolog'

fact = Porolog::Predicate.new :fact, self

fact.(42).fact!
fact.(42).fallacy!

puts fact.(42).valid?
# => true

puts fact.rules.inspect
# => [  fact(42):- true,   fact(42):- false]

puts fact.(42).solve.inspect
# => [{:n=>42}]

You can delete the rule directly which has the desired behavior.

fact.rules.delete(fact.rules[0])
puts fact.(42).valid?
# => false
puts fact.rules.inspect
# => [  fact(42):- false]
puts fact.(:n).solve.inspect
# => []

Are there more natual ways to refute facts such that they can't be solved after?

wizardofosmium commented 1 year ago

Hi @abargnesi,

Thanks for your kind words and good question.

Yes, I would interpret

fact.(42).fact!
fact.(42).fallacy!

towards fact.(42) being both true and false.

So, we would need a retract method,

fact.(42).retract!(...)

which would delete the rule as you described above and where ... identifies which rule you want deleted (rather than by its index). I'll have to think about the cleanest Ruby way to seem like Prolog.

Note that my intention for this gem was not to implement Prolog but rather to provide a logic engine that can integrate with Ruby. I have a few more nice ideas to implement, that would make Porolog more powerful from a Ruby perspective yet not compromise the logic engine-ness. Other Prolog gems often implement Prolog in Ruby but then the data is hard to pass to Ruby for Ruby to then process it. This was my goal, to make Ruby seem as though it was a bit Prolog-ish in the same way that Ruby is already a bit LISP-ish.

Retraction was not really in my thinking; but it is now.

Are there more natural ways to refute facts such that they can't be solved after?

I would keep track of the rule so that it can be deleted later. Something like this.

require 'porolog'

fact = Porolog::Predicate.new :fact, self

fact.(42).fact!
fact_42 = fact.rules.last  # Remember this rule

puts fact.(42).valid?
# => true

puts fact.rules.inspect
# => [  fact(42):- true]

puts fact.(42).solve.inspect
# => [{}]

puts fact.(:n).solve.inspect
# => [{:n=>42}]

fact.rules.delete(fact_42)

puts fact.(42).valid?
# => false

puts fact.rules.inspect
# => []

puts fact.(42).solve.inspect
# => []

puts fact.(:n).solve.inspect
# => []

Unfortunately, you can't remember the rule at creation, like this:

fact_42 = fact.(42).fact!  # Can't do this

This is because the library was designed around making predicate << rule work. Also, there's the convention of making bang(!) methods return self so that methods can be chained on the object. I will reconsider this though. But, for now, just use fact.rules.delete(fact_42).

I'll leave this issue open while I think about making this more Prolog-like. Let me know what you think and how the above goes.