porolog

<img src=“repository-images.githubusercontent.com/131847563/b3754100-636a-11e9-995b-20d409b992c9” width=“240” height=“120” align=“right” />

Plain Old Ruby Objects Prolog

Introduction

porolog is a Prolog implementation using plain old Ruby objects with the aim that logic queries can be called within a regular Ruby program. The goal was not to implement a Prolog interpreter that is just implement in Ruby, but rather that a logic engine could be embedded in a larger program.

The need that this gem aims to meet is to have a Ruby program interact with a Prolog program using native Ruby objects (POROs); hence the name Porolog. The goal was to implement a minimal logic engine in the style of Prolog where Ruby objects could be passed in and Ruby objects were passed back.

This version completes the minimal/generic logic engine along with some standard builtin predicates. Custom builtin predicates can be easily added.

Dependencies

The aim of porolog is to provide a logic engine with a minimal footprint. The only extra dependency is Yard for documentation.

Installation

gem install porolog

Usage

porolog is used by:

  • requiring the library within a Ruby program

  • defining facts and rules

  • solving goals

It is entirely possible to create a Ruby program that is effectively just a Prolog program. The main purpose of Porolog though is to add declarative logic programming to Ruby and allow hybrid programming, in the same way that Ruby allows hybrid programming in the functional programming paradigm.

This then Ruby programs to be written spanning all the major programming paradigms: - Imperative - Functional - Object Oriented - Declarative Logic

Basic Usage

Using porolog involves creating logic from facts and rules. An example, of the most basic usage uses just facts.

require 'porolog'

prime = Porolog::Predicate.new :prime

prime.(2).fact!
prime.(3).fact!
prime.(5).fact!
prime.(7).fact!

solutions = prime.(:X).solve

solutions.each do |solution|
  puts "#{solution[:X]} is prime"
end

Common Usage

Common usage is expected to be including Porolog in a class and encapsulating the engine defined.

require 'porolog'

include Porolog

class Numbers

  Predicate.scope self
  predicate :prime

  prime(2).fact!
  prime(3).fact!
  prime(5).fact!
  prime(7).fact!

  def show_primes
    solutions = prime(:X).solve

    solutions.each do |solution|
      puts "#{solution[:X]} is prime"
    end
  end

  def primes
    prime(:X).solve_for(:X)
  end

end


numbers = Numbers.new
numbers.show_primes
puts numbers.primes.inspect

Scope and Predicate Usage

A Predicate represents a Prolog predicate. They form the basis for rules and queries.

The Scope class enables you to have multiple logic programs embedded in the same Ruby program. A Scope object defines a scope for the predicates of a logic programme.

require 'porolog'

# -- Prime Numbers Predicate --
prime = prime1 = Porolog::Predicate.new :prime, :numbers

prime.(2).fact!
prime.(3).fact!
prime.(5).fact!
prime.(7).fact!
prime.(11).fact!

# -- Pump Predicate --
prime = prime2 = Porolog::Predicate.new :prime, :pumps

prime.('pump A').fact!
prime.('pump B').fact!
prime.('pump C').fact!
prime.('pump D').fact!

# -- Assertions --
assert_equal        [:default,:numbers,:pumps],     Scope.scopes

assert_scope        Porolog::Scope[:default],  :default, []
assert_scope        Porolog::Scope[:numbers],  :first,   [prime1]
assert_scope        Porolog::Scope[:pumps],    :second,  [prime2]

assert_equal        :prime,     prime1.name
assert_equal        :prime,     prime2.name

solutions = [
  { X:  2 },
  { X:  3 },
  { X:  5 },
  { X:  7 },
  { X: 11 },
]
assert_equal        solutions,                prime1.(:X).solve

solutions = [
  { X: 'pump A' },
  { X: 'pump B' },
  { X: 'pump C' },
  { X: 'pump D' },
]
assert_equal        solutions,                prime2.(:X).solve

Testing

rake test

or

rake core_ext_test
rake porolog_test
rake scope_test
rake predicate_test
rake arguments_test
rake rule_test
rake goal_test
rake variable_test
rake value_test
rake tail_test
rake instantiation_test

Author

Luis Esteban MSc MTeach