Skip to content
Pierre-Antoine Champin edited this page Jan 11, 2021 · 10 revisions

Introduction

An N3 builtin is identified by an IRI, and occurs in the predicate position of a statement.

Arguments

An N3 builtin operates on its arguments. These arguments are often the subject and object of the builtin statement. For instance (arguments are prefixed with $a):

  • math:lessThan results in a true statement if the subject is less than the object. Hence, its arguments, $a_1 and $a_2, are derived from the builtin statement as follows: $a_1 math:lessThan $a_2.

Arguments can also be a more complex "deconstruction" of the subject and/or object in case lists or cited formulas are expected. For instance:

  • math:sum calculates the sum of a list of numbers. In this case, the subject is deconstructed into its constituent elements, which serve as arguments together with the object. In particular, its arguments, $a_1 .. $a_n and $a_n, are defined from the builtin statement as follows: ($a_1 .. $a_n) math:sum $a_s

If an argument is concrete (i.e., not an unbound variable), it serves as an input argument. Else, i.e., if an argument is an unbound variable, then it serves an output argument. From now on, we will refer to an N3 builtin's input arguments as inputs, and output arguments as outputs.

An N3 builtin does not necessarily have a single, fixed output ; it depends on which argument is given as an unbound variable. For instance, both 35 math:cos ?x and ?x math:cos 0.57 are perfectly valid builtin statements; in the first case, the math:cos of 35 will be calculated; in the second case, the inverse operation (math:acos) for 0.57 will be calculated.

Input and output domains

Each N3 builtin has an expected datatype for each of its arguments, called the argument domain (or simply domain). If the datatype of an argument value, called the value datatype, does not match the argument domain, it may be possible to cast the value's datatype to, or substitute it for, the domain datatype.

If the value datatype and domain datatype do not match, and no casting or substitution is possible, the builtin statement will be considered false (see Builtin processes).

Numeric datatype promotion and substitution

If the numeric value datatype does not match the argument domain, it may be possible to promote or substitute the numeric value datatype:

Numeric type promotion: A numeric value datatype that is derived from the domain datatype can be promoted to the latter (e.g., xs:integer is derived from xs:decimal). This is done by casting the original value to the required datatype. This kind of promotion may cause loss of precision [can it? if the required type has a higher precision?].

Even if there is no direct derivation relation between the value and domain datatype, the following numeric type promotions can take place:

  • A value of type xs:float (or any type derived from xs:float) can be promoted to type xs:double. The result is an xs:double value that is the same as the original value.
  • A value of type xs:decimal (or any type derived from xs:decimal) can be promoted to either of the types xs:float or xs:double.

Numeric type substitution: If all values have the same numeric datatype, and this datatype is derived from the domain datatype (e.g., xs:integer is derived from xs:decimal), then the values can be used without any casting. For example, if two xs:integer values are used for inputs where xs:decimal domains are expected, then the values retain their datatype as xs:integer. The substituted numeric datatype will also apply to the builtin's output, if any. In the above example, in case there is a builtin output with xs:decimal datatype, any calculated value will also be assigned datatype xs:integer.

Builtins operating on any numeric type: some N3 builtins (e.g., math:sum) operate on values of any numeric type (i.e., xs:numeric, the domain of xs:double, xs:float, and xs:decimal), i.e., their concrete input values may present any combination of numeric types. In that case, the builtin can only be applied if all value datatypes can be promoted into a common numeric datatype in the ordered list (xs:integer, xs:decimal, xs:float, xs:double). At this point, we rely on numeric type substitution. For instance:

  • Given two value datatypes xs:integer and xs:decimal, the xs:integer value will be promoted to xs:decimal as described above. At that point, the two xs:decimal datatypes can be substituted for xs:numeric, as above. If the builtin has an output, then the calculated value for this output will also have datatype xs:decimal.

  • Given two values with type xs:integer, this datatype will simply be substituted for xs:numeric. If the builtin has an output, then the calculated value for the output will also have datatype xs:integer.

Other kinds of datatype casting.

If the non-numeric value datatype does not match the argument domain, it may be possible to cast the value datatype to the domain datatype.

String

A literal will be considered a "string" when it has an xs:string datatype, due to the absence of a datatype, an explicit xs:string datatype; or a rdf:langString datatype, due to the presence of a language tag.

Casting from string: if an input value has an xs:string datatype that does not match the domain, it may be possible to cast the string to the domain datatype, as defined in XPath. The given string will be mapped to a value of the domain datatype as defined in XML Schema 2. The resulting value representation must be a valid lexical form for the domain datatype.

Casting to string: if an input value is an IRI, or any kind of literal (incl. type xs:anyUri or its derivations), and the domain is xs:string, then the value will be cast to a string as defined in XPath.

Other datatypes

Other types of datatype casting will take place as defined in XPath. [Speaking for myself here; supporting all these casting operations would really increases the complexity of implementing builtins. But I suppose it's unavoidable.]

[[gk – This is what I have defined in a PR for string:concatenation:

Resources are turned into strings using by casting to xsd:string as defined in XPath along with additional rules defined for SPARQL 1.1.

]]

Comments from the google doc:

(gregg) There's a useful chart in https://www.w3.org/TR/xpath-functions/#casting-from-primitive-to-primitive, a subset of which is in SPARQL (https://www.w3.org/TR/sparql11-query/#FunctionMapping), also describing IRIs.

The following processes take place for builtin statements:

  • If an N3 builtin only has inputs then the builtin will check the truthfulness of the statement. For instance, in the statement (2 1) math:sum 3, the builtin math:sum will check whether 2 + 1 = 3.

  • If an N3 builtin has one or more outputs, then the builtin will either

    • check the truthfulness of the statement, or
    • calculate one or more values for the output arguments so as to ensure the truthfulness.

For example:

  • Given (2 1) math:sum ?s, the system will calculate value 3 for output ?s
  • Given (?a 1) math:sum ?s, the system returns true - since there are infinite numbers of sums that match this pattern
  • Given (1 "a" 3) list:member ?m, the system will calculate three values for output ?m, namely 1, "a" and 3

Builtin theory box

These cases can be more conveniently be seen as a restrained basic graph pattern search on the builtin’s "theory box", i.e., set of all truthful builtin statements.

In the first example, the theory box includes (2 1) math:sum 3 that clearly matches the concrete graph pattern (2 1) math:sum 3 as well as the parametrized pattern (2 1) math:sum ?s. The same theory box includes an infinite number of statements matching the pattern (?a 1) math:sum ?s. Instead of calculating these infinite numbers of values, the builtin will simply acknowledge the truthfulness of the statement (i.e., results exist but we're not returning them!).

This "theory box search" is restrained in several ways. Firstly, for all builtins, it will never calculate an infinite amount of values for output arguments. Secondly, there are builtin-specific constraints that avoid intractable calculations as well as some common-sense constraints that weigh utility vs. implementation difficulty.

Clone this wiki locally