Term types in Integraal are inconsistent
There are several problems when loading terms with Integraal parsers. There are already two other issues in this subject:
There is another issue not listed here: there are cases where an IRI term is parsed as a functional term:
@prefix ex:<http://example.com>
p(X) :- r(X, ex:test)
In this example, ex:test
is parsed as the functional term http://example.com/test()
.
There is an issue when loading a timestamp in RDF: currently, the timestamp is converted to a string representing a date. We obtain something like 1970-01-01 00:00:40.066669
.
Finally, there is the issue with how the prefixes are managed for the terms (Currently, a prefix declared with @prefixed
is just replaced by its value and a prefix declared with @computed
just disappears).
These issues makes Integraal very difficult to use in practice as it can be very difficult to find the errors they cause. For example, it is hard to see that a rule is not applied because it tries to make a join between a Float
and a Double
. Or join between a number that was parsed as a Float
(as in Dlgp) and another one parsed as a String
(as in RDF). When we print the atoms, in appearance, they all appear to be the same value as printing an atom doesn't show the type of the terms.
I propose several default behaviours:
- By default, a literal should be parsed as
Literal<?>
, whatever the type of the file we parse; - We should always select a specific type when possible, parsing everything as
Literal<String>
like in the RDF parsers is not a good behaviour; - The parsing should be consistent between parsers: notably, an integer should have a unique type, the same for floating/decimal numbers;
- An IRI term should never be parsed as a functional term and should parsed the same way both in Dlgp and RDF ;
- When parsing types in RDF, we should register this type for a predicate position so that we build the good type when doing the parsing.
I have several ideas:
- Integers should be parsed as a unique type. For example, it could be a Python-like Integer, that is optimized when the integer is less than 64-bits in size but can grow bigger than that - it would give a great flexibility for the users. I put an implementation at the end of this issue.
- Decimal/Floating numbers should always be parsed as
Double
- for greater flexibility, we could implement a classIntegraalFloat
that extendsDouble
, so it will be easier to implement other functionalities later. - Booleans should be, obviously, always be parsed as
Boolean
.
Implementation of IntegraalInteger
:
import java.math.BigInteger;
public class IntegraalInteger extends Number implements Comparable<IntegraalInteger> {
private boolean isBig;
private long smallValue;
private BigInteger bigValue;
// Constructor from long
public IntegraalInteger(long value) {
this.smallValue = value;
this.isBig = false;
}
// Constructor from BigInteger
public IntegraalInteger(BigInteger value) {
if (value.bitLength() <= 63) { // Fits in long
this.smallValue = value.longValue();
this.isBig = false;
} else {
this.bigValue = value;
this.isBig = true;
}
}
// Private helper to get BigInteger representation
private BigInteger toBigInteger() {
return isBig ? bigValue : BigInteger.valueOf(smallValue);
}
// Addition
public IntegraalInteger add(IntegraalInteger other) {
if (this.isBig || other.isBig) {
return new IntegraalInteger(this.toBigInteger().add(other.toBigInteger()));
}
long result = this.smallValue + other.smallValue;
if (((this.smallValue ^ result) & (other.smallValue ^ result)) < 0) { // Overflow detection
return new IntegraalInteger(BigInteger.valueOf(this.smallValue).add(BigInteger.valueOf(other.smallValue)));
}
return new IntegraalInteger(result);
}
// Subtraction
public IntegraalInteger subtract(IntegraalInteger other) {
if (this.isBig || other.isBig) {
return new IntegraalInteger(this.toBigInteger().subtract(other.toBigInteger()));
}
long result = this.smallValue - other.smallValue;
if (((this.smallValue ^ other.smallValue) & (this.smallValue ^ result)) < 0) { // Overflow detection
return new IntegraalInteger(BigInteger.valueOf(this.smallValue).subtract(BigInteger.valueOf(other.smallValue)));
}
return new IntegraalInteger(result);
}
// Multiplication
public IntegraalInteger multiply(IntegraalInteger other) {
if (this.isBig || other.isBig) {
return new IntegraalInteger(this.toBigInteger().multiply(other.toBigInteger()));
}
long result = this.smallValue * other.smallValue;
if (other.smallValue != 0 && result / other.smallValue != this.smallValue) { // Overflow detection
return new IntegraalInteger(BigInteger.valueOf(this.smallValue).multiply(BigInteger.valueOf(other.smallValue)));
}
return new IntegraalInteger(result);
}
// Division
public IntegraalInteger divide(IntegraalInteger other) {
if (other.equals(new IntegraalInteger(0))) {
throw new ArithmeticException("Division by zero");
}
return new IntegraalInteger(this.toBigInteger().divide(other.toBigInteger()));
}
// Modulo
public IntegraalInteger mod(IntegraalInteger other) {
if (other.equals(new IntegraalInteger(0))) {
throw new ArithmeticException("Modulo by zero");
}
return new IntegraalInteger(this.toBigInteger().mod(other.toBigInteger()));
}
// Comparisons
@Override
public int compareTo(IntegraalInteger other) {
return this.toBigInteger().compareTo(other.toBigInteger());
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof IntegraalInteger)) return false;
IntegraalInteger other = (IntegraalInteger) obj;
return this.toBigInteger().equals(other.toBigInteger());
}
@Override
public int hashCode() {
return this.toBigInteger().hashCode();
}
// Implementing Number abstract methods
@Override
public int intValue() {
return isBig ? bigValue.intValue() : (int) smallValue;
}
@Override
public long longValue() {
return isBig ? bigValue.longValue() : smallValue;
}
@Override
public float floatValue() {
return isBig ? bigValue.floatValue() : smallValue;
}
@Override
public double doubleValue() {
return isBig ? bigValue.doubleValue() : smallValue;
}
@Override
public String toString() {
return isBig ? bigValue.toString() : Long.toString(smallValue);
}
}