Following the ongoing discussion on “static dicts”, aka Ciao argnames or ECLiPSe “structs”, I have implemented most of it in a branch GitHub - SWI-Prolog/swipl-devel at argnames Here are some experience that is hopefully useful input for the discussion.
Implementation
Given built-in support for dicts based on the same syntax, that was fairly trivial. As read/1
is in C in SWI-Prolog, this also means adding the basic structures for storing, importing and exporting argnames to modules, which is a bit more work.
Declaration and reflexion
(as examples we use a 3d point)
:- argnames(point(x,y,z)).
Module-local declaration.:- export(argnames(point(x,y,z)).
Exported version of the same. Importing this module imports thepoint
argnames. Introspection is possible usingmodule_property(Module, exported_argnames(List))
as well asargnames_property(point, exported(true))
and for the importing moduleargnames_property(point, imported_from(the_point_module))
discussion
Should we (also) allow for:- module(m, [p1, argnames(point(x,y,z))]).
?current_argnames(?Name, :Term).
can be used with any instantiation.
discussion
Should the meta argument be on theName
orTerm
? The choice follows oldcurrent_predicate/2
as found in e.g. Quintus AFAIK.argnames_property(:Name, ?Property)
Can be used with any instantiation. Defined properties arearity(Int)
,functor(Name/Arity)
,exported(Bool)
andimported_from(Module)
Basic use
point{x:1}
reads aspoint(1,_,_)
, etc.x of point
is macro-expanded to1
, following ECliPSe. Also the ECLiPSeproperty(P) of point
are implemented.
Experimental
named_arg(x, point{x:1}, X)
→X = 1
.
Signaturenames_arg(?Name, :Term, ?Value)
.
Performance comparison toarg(x of point, point{x:1}, X)
is barely noticeable, unless cases where arg/3 is mapped to a VM instruction in SWI-Prolog.
Integration with SWI-Prolog dicts
point{x:1}.x
Is evaluated as for dynamic dicts. All functional evaluation is supported, in part directly and in part after automatic type translation. For example:?- X = point{x:1}.put(type, '3d')
→X = #{x:1, y:_, z:_, type:'3d'}
argnames_to_dict(point{x:1}, Dict, [])
→Dict = #{x:1, y:_, z:_}
.
The option list allows fornonvar
to suppress they
andz
andtag(Tag)
to modify the#
dict_to_argnames(#{x:1,type:'3d'}, point, Pt)
→Pt = point(1,_,_)
I.e., keys from the argnames that appear in the dict are used and the others are ignored.
Naming
I used argnames throughout because it is easy to replace. After this work, I think the name is acceptable for Ciao’s scope. If, however, you want to consider point(,,_) as something different from an ordinary compound this name is problematic. You can’t call the thing an “argnames” (at least, it sounds silly to me). For now I did If you want to have these “things” cooperating with dicts, you have to consider them separate entities, be it “a compound with additional functionality”
Relation to dynamic dicts
This is a bit hard. “Argnames” are great for predicate heads for which you often only want to specify a few of the arguments. I start having my doubt on their usefulness as data structure. Dynamic dicts are about as fast, more flexible and easier to manage. Yes, argnames use less space, but only if more than half of the arguments are used. For _sparse _ argnames, dicts are cheaper. I think things would change if we combine them with module-local functors, such that the term can efficiently be protected.