5.4 Dicts: structures with named arguments
All Application Manual Name SummaryHelp

  • Documentation
    • Reference manual
      • SWI-Prolog extensions
        • Dicts: structures with named arguments
          • Functions on dicts
            • ./3
            • User defined functions on dicts
            • Predefined functions on dicts
          • Predicates for managing dicts
          • When to use dicts?
          • A motivation for dicts as primary citizens
          • Implementation notes about dicts
    • Packages

5.4.1 Functions on dicts

The infix operator dot (op(100, yfx, .) is used to extract values and evaluate functions on dicts. Functions are recognised if they appear in the argument of a goal in the source text, possibly nested in a term. The keys act as field selector, which is illustrated in this example.

?- X = point{x:1,y:2}.x.
X = 1.

?- Pt = point{x:1,y:2}, write(Pt.y).
2
Pt = point{x:1,y:2}.

?- X = point{x:1,y:2}.C.
X = 1,
C = x ;
X = 2,
C = y.

The compiler translates a goal that contains ./2 terms in its arguments into a conjunction of calls to ./3 defined in the system module. Terms functor.2 that appears in the head are replaced with a variable and calls to ./3 are inserted at the start of the body. Below are two examples, where the first extracts the x key from a dict and the second extends a dict containing an address with the postal code, given a find_postal_code/4 predicate.

dict_x(X, X.x).

add_postal_code(Dict, Dict.put(postal_code, Code)) :-
        find_postal_code(Dict.city,
                         Dict.street,
                         Dict.house_number,
                         Code).

Note that expansion of ./2 terms implies that such terms cannot be created by writing them explicitly in your source code. Such terms can still be created with functor/3, =../2, compound_name_arity/3 and compound_name_arguments/3.181Traditional code is unlikely to use ./2 terms because they were practically reserved for usage in lists. We do not provide a quoting mechanism as found in functional languages because it would only be needed to quote ./2 terms, such terms are rare and term manipulation provides an escape route.

.(+Dict, +Function, -Result)
This predicate is called to evaluate ./2 terms found in the arguments of a goal. This predicate evaluates the field extraction described above, raising an exception if Function is an atom (key) and Dict does not contain the requested key. If Function is a compound term, it checks for the predefined functions on dicts described in section 5.4.1.2 or executes a user defined function as described in section 5.4.1.1.

5.4.1.1 User defined functions on dicts

The tag of a dict associates the dict to a module. If the dot notation uses a compound term, this calls the goal below.

<module>:<name>(Arg1, ..., +Dict, -Value)

Functions are normal Prolog predicates. The dict infrastructure provides a more convenient syntax for representing the head of such predicates without worrying about the argument calling conventions. The code below defines a function multiply(Times) on a point that creates a new point by multiplying both coordinates. and len182as length would result in a predicate length/2, this name cannot be used. This might change in future versions. to compute the length from the origin. The . and := operators are used to abstract the location of the predicate arguments. It is allowed to define multiple a function with multiple clauses, providing overloading and non-determinism.

:- module(point, []).

M.multiply(F) := point{x:X, y:Y} :-
        X is M.x*F,
        Y is M.y*F.

M.len() := Len :-
        Len is sqrt(M.x**2 + M.y**2).

After these definitions, we can evaluate the following functions:

?- X = point{x:1, y:2}.multiply(2).
X = point{x:2, y:4}.

?- X = point{x:1, y:2}.multiply(2).len().
X = 4.47213595499958.

5.4.1.2 Predefined functions on dicts

Dicts currently define the following reserved functions:

get(?KeyPath)
Return the value associates with KeyPath. KeyPath is either a single key or a term Key1/Key2/.... Each key is either an atom, small integer or a variable. While Dict.Key throws an existence error, this function fails silently if a key does not exist in the target dict. See also :</2, which can be used to test for existence and unify multiple key values from a dict. For example:
?- write(t{a:x}.get(a)).
x
?- write(t{a:x}.get(b)).
false.
?- write(t{a:t{b:x}}.get(a/b)).
x
put(+New)
Evaluates to a new dict where the key-values in New replace or extend the key-values in the original dict. See put_dict/3.
get(?KeyPath, +Default)
Same as get/1 , but if no match is found the function evaluates to Default. If KeyPath contains variables possible choice points are respected and the function only evaluates to Default if the pattern has no matches.
put(+KeyPath, +Value)
Evaluates to a new dict where the KeyPath-Value replaces or extends the key-values in the original dict. KeyPath is either a key or a term KeyPath/Key,183Note that we do not use the’.’functor here, because the ./2 would evaluate. replacing the value associated with Key in a sub-dict of the dict on which the function operates. See put_dict/4. Below are some examples:
?- A = _{}.put(a, 1).
A = _G7359{a:1}.

?- A = _{a:1}.put(a, 2).
A = _G7377{a:2}.

?- A = _{a:1}.put(b/c, 2).
A = _G1395{a:1, b:_G1584{c:2}}.

?- A = _{a:_{b:1}}.put(a/b, 2).
A = _G1429{a:_G1425{b:2}}.

?- A = _{a:1}.put(a/b, 2).
A = _G1395{a:_G1578{b:2}}.