Data conversion in janus

Contents

Bi-translation between Prolog Terms and Python Data Structures

janus takes advantage of a C-level bi-translation of a large portion of Prolog terms and Python data structures: i.e., Python lists, tuples, dictionaries, sets and other data types are translated to their Prolog term forms, and Prolog terms of restricted but large syntax are translated to lists, tuples, dictionaries, sets and so on. Bi-translation is recursive in that any of these data structures can be nested in any other data structures (subject to limitations on the occurrence of mutables in Python data structures).

Due to syntactic similarities between Prolog terms and Python data structures, the Prolog term forms are easy to translate and sometimes appear syntactically identical.

As terminology, when a Python data structure D is translated into a Prolog term T, T is called a (Janus) D term e.g., a dictionary term or a set term. The type representing any Python structure that can be translated to Prolog is called jns_struct while jns_term is the pseudo-type representing all Prolog terms that can be translated into a Python data structure.

We first describe the bi-translation in detail, and summarize it in a table at the end of this section.

Bi-translation Specification of Python Types

Bi-translation between Prolog and Python can be described from the viewpoint of Python types as follows:

  • Numeric Types: Python integers and floats are bi-translated to Prolog integers and floats. For Prologs that have a finite min_integer or max_integer a representation error should be thrown if the number returned from Python is out of bounds.

v - Fractional numbers (created by fractions.Fraction()) are translateed into Prolog rationals in Prologs like SWI that support rationals.

  • Boolean Numbers in Python are translated to the special Prolog structures @(true) and @(false).

  • Complex numbers are not translated to Prolog; rather their translation to Prolog returns a Python Object Reference, whose real and imaginary parts can be accessed as attributes.

String Types: Python string types are bi-translated to Prolog atoms by default. There are two reasons for this. First, not all Prologs support an efficient representation of strings, so translating as atoms improves compatibility. A pragmatic second reason is that Python uses strings both for identifiers and arbitrary text, and it is not generally possible to determine the role played by a specific Python string in a given case.

  • The Prolog representation #(Term) allows passing any Prolog term as a Python string. If Term is an atom or string, this is the same as passing the atom or string. Any other Prolog term is written canonically to a Prolog atom (or string) that is then passed to Python.

For Prolog systems that offer an efficient representation of strings, Prolog strings can automatically be translated to Python strings, while Janus function calls provide the option py_string_as(string) to translate Python strings to Prolog strings.

Note that a Python string can be enclosed in either double quotes ('') or single quotes ('). In translating from Python to Prolog, the outer enclosure is ignored, so Python "’Hello’" is translated to the Prolog ’\’Hello\’’, while the Python ’"Goodbye"’ is translated to the Prolog ’"Goodbye"’.

  • Sequence Types:

    • Python lists are bi-translated as Prolog lists and the two forms are syntactically identical. An exception is for Janus calls that use the py_object(true) option. This option returns a list as an iterable sequence object that can be backtracked through using py_iter() or examined in other ways.

    • A Python tuple of arity N >= 0 is bi-translated to a compound Prolog term -/N (i.e., the functor is a hyphen).

      • Note that as a specia case a Python empty tuple () bitranslates to -().

      • For those Prologs with a maximum arity Maxa for compound terms, passing a Python term with arity greater than Maxa should throw a representation error.

  • Mapping Types: The translation of Python dictionaries takes advantage of the syntax of braces, which is supported by all Prologs that support DCGs. The term form of a dictionary is;

    { DictList}

    where DictList is a comma list of ’:’/2 terms that use infix notation. I.e., {k1:v1, k2:v2} is syntactic sugar for {}(','(:(k1,v1), :(k2,v2)))

    Key:Value

    Key and Value are the translations of any Python data structures that are both allowable as a dictionary key or value, and supported by janus. For instance, Value can be (the term form of) a list, a set, a tuple or another dictionary as with

    {’K1’:[1,2,3], ’k2’:(4,5,6)]}

    which has a nearly identical term form as

    {’K1’:[1,2,3], k2: -(4,5,6)]}

    For compatibility with Prologs that support dicts or that do not support DCGs, systems should allow the representation of a dictionary term Term as above as py(Term).

    • We note that if py/1 is defined as prefix operator, (I.e, via the declaration op(600,fx,py), py{k1:v1, k2:v2} is a ISO Prolog compliant term and is also readabls as a named dict.

    • Also note that {} translates to a Python string, while py({}) translates into an empty Python dict.

  • Set Types: A Python set S is translated to the term form

    py_set(SetList)

    where SetList is the list containing exactly the translated elements of S. Due to Python’s implementation of sets, there is no guarantee that the order of elements will be the same in S and SetList.

  • None Types. The Python keyword None is translated to the Prolog term @(none).

  • Binary Types: are not yet supported.

  • Any Python object Obj of a type that is not translated to a Prolog term as indicated above, and that does not have an associated iterator is translated to the Python object reference, which can be passed back to Python for an object call or other purposes.

    • The representation of a Python object reference is system-dependent, and should be tested via py_is_object/1.

    • The translaton of arbitrary Prolog terms to Python is system-dependent. See e.g., the SWI or XSB manuals for different ways in which this is done.

Tabular Summary

The bi-directional conversion between Prolog and Python terms is summarized in the table below.

Prolog Python Notes
Variable - (instantiation error)
Integer int Supports big integers
Rational fractions.Fraction()
Float float
@(none) None
@(true) True
@(false) False
Atom String
String String Via py_string_as(string) option (Optional)
#(Term) String stringify using write_canonical/1 if not atomic
List List
List Sequence
List Iterator Note that a Python Generator is an Iterator
py_set(List) Set
-() () Python empty Tuple
-(a,b, … ) (a,b, … ) Python Tuples. Note that a Prolog pair A-B maps to a Python (binary) tuple.
{k:v, …} Dict
py({k:v, …}) Dict
py({}) {} Python empty dictionary
Python Object Reference Object Used for any Python object not above
Compound Domain Error For any term not designated above.


The Prolog Implementers Forum is a part of the "All Things Prolog" online Prolog community, an initiative of the Association for Logic Programming stemming from the Year of Prolog activities.