Prolog vs Java Comparison

These are my notes and insights while extending the VisB Java code for new features (optional, ignore, repeat, for attributes, inclusion, and better error reporting) and then rewriting the core of VisB in Prolog

Note: still under construction !

Prolog's Fact Database

This is very convenient. With a simple declaration I can now store VisB items as Prolog facts:

:- dynamic visb_svg_file/3,visb_item/4, visb_event/4.

Adding a fact is simply calling assert(visb_item(ID,Attr,TypedExpr,Desc) and looking up facts using arbitrary patterns is just calling visb_item(SvgID,SvgAttribute,Formula,_Desc). This call can serve as an enumerator/iterator over all VisB items (if all variables unbound), or to look up items for particular SVG Ids or particular attributes.

In Java, a VisB item is declared in this class (VisBItem.java), with setters and getters (which I feel are complete overkill here). Items are stored in a list

  List<VisBItem> visBItems = new ArrayList<>();

where we add using

  visBItems.add(new VisBItem(id, attribute, value, optional))

Note: we have no convenient way to look up arbitrary patterns. Here are the contents of VisBItem.java, which also includes one parsing method:

package de.prob2.ui.visb.visbobjects;

import de.prob.animator.domainobjects.IEvalElement;
import de.prob2.ui.visb.VisBParser;
import de.prob2.ui.visb.exceptions.VisBNestedException;
import de.prob.model.classicalb.ClassicalBModel;
import de.prob.exception.ProBError;
import de.prob.statespace.Trace;
import de.prob.animator.domainobjects.EvaluationException;
import de.prob.animator.domainobjects.ClassicalB;
import de.prob2.ui.prob2fx.CurrentTrace;
import de.prob.animator.domainobjects.FormulaExpand;

import java.util.Objects;

/**
 * The VisBItem is designed for the JSON / VisB file
 */
public class VisBItem {
	private String id;
	private String attribute;
	private String value; // B Formula to compute value of attribute for SVG object id
	private Boolean optional; // if true then we ignore identifier not found errors and simply disable this item
	private IEvalElement parsedFormula; // if different from null the formula has already been parsed

	/**
	 *
	 * @param id this has to be the id used in the svg file to correspond with that svg element
	 * @param attribute this has to be an actual svg attribute, that can be handled via {@link VisBParser}
	 * @param value this formula has to provide a valid value usable with the given attribute
	 * @param optional true if this item is optional, i.e., will be ignored if the value formula contains unknown ids
	 */
	public VisBItem(String id, String attribute, String value, Boolean optional) {
		this.id = id;
		this.attribute = attribute.toLowerCase();
		this.value = value;
		this.optional = optional;
	}

	public String getId() {
		return id;
	}

	public String getAttribute() {
		return attribute;
	}

	public String getValue() {
		return value;
	}

	public Boolean itemIsOptional() {
		return optional;
	}
	public IEvalElement getParsedFormula() {
	 // getter which does not require CurrentTrace; but cannot parse on demand
		return parsedFormula;
	}
	
	/**
	 * parse the formula of VisBItem and store parsed formula in parsedFormula attribute
	 */
	public IEvalElement getParsedValueFormula(CurrentTrace currentTrace) throws VisBNestedException, ProBError {
		String formulaToEval = this.getValue();
		try {
			if (this.parsedFormula != null) {
			   return this.parsedFormula; // is already parsed
			} else if(currentTrace.getModel() instanceof ClassicalBModel) {
			   this.parsedFormula = currentTrace.getModel().parseFormula(formulaToEval, FormulaExpand.EXPAND);
			  // use parser associated with the current model, DEFINITIONS are accessible
			} else {
			   this.parsedFormula = new ClassicalB(formulaToEval, FormulaExpand.EXPAND); // use classicalB parser
			   // Note: Rodin parser does not have IF-THEN-ELSE nor STRING manipulation, cumbersome for VisB
			}
		} catch (EvaluationException e){
			System.out.println("\nException for "+ this.getId() + "."+ this.getAttribute() + " : " + e);
		    throw(new VisBNestedException("Exception parsing B formula for VisB item "+ this.getId() + "."+ this.getAttribute() + " : ",e));
		}
	    return this.parsedFormula;
	}
	
	
	@Override
	public String toString(){
		return "{ID: " + this.id +", ATTRIBUTE: "+this.attribute+", VALUE: "+this.value+"} ";
	}

	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof VisBItem)) {
			return false;
		}
		VisBItem other = (VisBItem) obj;
		return this.id.equals(other.id) && this.attribute.equals(other.attribute) && this.value.equals(other.value);
	}

	@Override
	public int hashCode() {
		return Objects.hash(id, attribute, value);
	}
}

Sample Querys of the Fact Database

The existing Java code held the above VisB items in a list: List<VisBItem>. Let us examine a few Prolog queries, to show the versatility and conciseness:

  • is the attributed visibility set for the SVG identifier train1:
visb_item(train1,visibility,_,_)
  • retrieve the formula for the given identifier and attribute:
visb_item(train,visibility,Formula,_)
  • Iterate over all VisB items:
visb_item(ID,Attr,Formula,Desc), DoSomething, fail
  • Find a list of identifiers and attributes where there are conflicting formulas:
findall(conflict(ID,Attr), (visb_item(ID,Attr,F1,_),visb_item(ID,Attr,F2), F1 @< F2), Cs)

To implement the latter in Java you need to:

  • define a new class to hold the pairs (or implement/use a generic tuple type and instantiate it)
  • create a new collection object to hold the resulting list Cs
  • iterate over the VisB item list
  • write a function to find a VisB item with the given ID and attribute in the list (or refactor the list into a hash map for the right keys, but which may not be using the right keys for other queries)
  • calls this function to look for another entry (an alternative for this particular query would be to sort the list and look for a successor element with the same identifier and attribute)
  • create a new pair conflict object and add it to the result list