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
          • Predicates for managing dicts
            • is_dict/1
            • is_dict/2
            • get_dict/3
            • get_dict/5
            • dict_create/3
            • dict_pairs/3
            • dict_same_keys/2
            • put_dict/3
            • put_dict/4
            • del_dict/4
            • :</2
            • select_dict/3
            • >:</2
            • Destructive assignment in dicts
          • When to use dicts?
          • A motivation for dicts as primary citizens
          • Implementation notes about dicts
    • Packages

5.4.2 Predicates for managing dicts

This section documents the predicates that are defined on dicts. We use the naming and argument conventions of the traditional library(assoc).

is_dict(@Term)
True if Term is a dict. This is the same as is_dict(Term,_).
is_dict(@Term, -Tag)
True if Term is a dict of Tag.
get_dict(?Key, +Dict, -Value)
Unify the value associated with Key in dict with Value. If Key is unbound, all associations in Dict are returned on backtracking. The order in which the associations are returned is undefined. This predicate is normally accessed using the functional notation Dict.Key. See section 5.4.1.

Fails silently if Key does not appear in Dict. This is different from the behavior of the functional‘.`-notation, which throws an existence error in that case.

[semidet]get_dict(+Key, +Dict, -Value, -NewDict, +NewValue)
Create a new dict after updating the value for Key. Fails if Value does not unify with the current value associated with Key. Dict is either a dict or a list the can be converted into a dict.

Has the behavior as if defined in the following way:

get_dict(Key, Dict, Value, NewDict, NewValue) :-
        get_dict(Key, Dict, Value),
        put_dict(Key, Dict, NewValue, NewDict).
dict_create(-Dict, +Tag, +Data)
Create a dict in Tag from Data. Data is a list of attribute-value pairs using the syntax Key:Value, Key=Value, Key-Value or Key(Value). An exception is raised if Data is not a proper list, one of the elements is not of the shape above, a key is neither an atom nor a small integer or there is a duplicate key.
dict_pairs(?Dict, ?Tag, ?Pairs)
Bi-directional mapping between a dict and an ordered list of pairs (see section A.34).
dict_same_keys(?Dict1, ?Dict2)
True when Dict1 and Dict2 have the same set of keys. The tag is not considered. This predicate is semidet if both arguments are instantiated to a dict. If one is instantiated to a dict and the other is unbound, it generates a dict with the same keys and unbound tag and values. The predicate is_dict/2 may be used test tag equivalence or unify the tags. This predicate raises an instantiation_error if both argument are unbound or a type_error if one of the arguments is neither a dict nor a variable.
put_dict(+New, +DictIn, -DictOut)
DictOut is a new dict created by replacing or adding key-value pairs from New to Dict. New is either a dict or a valid input for dict_create/3. This predicate is normally accessed using the functional notation. Below are some examples:
?- A = point{x:1, y:2}.put(_{x:3}).
A = point{x:3, y:2}.

?- A = point{x:1, y:2}.put([x=3]).
A = point{x:3, y:2}.

?- A = point{x:1, y:2}.put([x=3,z=0]).
A = point{x:3, y:2, z:0}.
put_dict(+Key, +DictIn, +Value, -DictOut)
DictOut is a new dict created by replacing or adding Key-Value to DictIn. For example:
?- A = point{x:1, y:2}.put(x, 3).
A = point{x:3, y:2}.

This predicate can also be accessed by using the functional notation, in which case Key can also be a *path* of keys. For example:

?- Dict = _{}.put(a/b, c).
Dict = _6096{a:_6200{b:c}}.
del_dict(+Key, +DictIn, ?Value, -DictOut)
True when Key-Value is in DictIn and DictOut contains all associations of DictIn except for Key.
[semidet]+Select :< +From
True when Select is a‘sub dict’of From: the tags must unify and all keys in Select must appear with unifying values in From. From may contain keys that are not in Select. This operation is frequently used to match a dict and at the same time extract relevant values from it. For example:
plot(Dict, On) :-
        _{x:X, y:Y, z:Z} :< Dict, !,
        plot_xyz(X, Y, Z, On).
plot(Dict, On) :-
        _{x:X, y:Y} :< Dict, !,
        plot_xy(X, Y, On).

The goal Select :< From is equivalent to select_dict(Select, From, _).

[semidet]select_dict(+Select, +From, -Rest)
True when the tags of Select and From have been unified, all keys in Select appear in From and the corresponding values have been unified. The key-value pairs of From that do not appear in Select are used to form an anonymous dict, which is unified with Rest. For example:
?- select_dict(P{x:0, y:Y}, point{x:0, y:1, z:2}, R).
P = point,
Y = 1,
R = _{z:2}.

See also :</2 to ignore Rest and >:</2 for a symmetric partial unification of two dicts.

+Dict1 >:< +Dict2
This operator specifies a partial unification between Dict1 and Dict2. It is true when the tags and the values associated with all common keys have been unified. The values associated to keys that do not appear in the other dict are ignored. Partial unification is symmetric. For example, given a list of dicts, find dicts that represent a point with X equal to zero:
    member(Dict, List),
    Dict >:< point{x:0, y:Y}.

See also :</2 and select_dict/3.

5.4.2.1 Destructive assignment in dicts

This section describes the destructive update operations defined on dicts. These actions can only update keys and not add or remove keys. If the requested key does not exist the predicate raises existence_error(key, Key, Dict). Note the additional argument.

Destructive assignment is a non-logical operation and should be used with care because the system may copy or share identical Prolog terms at any time. Some of this behaviour can be avoided by adding an additional unbound value to the dict. This prevents unwanted sharing and ensures that copy_term/2 actually copies the dict. This pitfall is demonstrated in the example below:

?- A = a{a:1}, copy_term(A,B), b_set_dict(a, A, 2).
A = B, B = a{a:2}.

?- A = a{a:1,dummy:_}, copy_term(A,B), b_set_dict(a, A, 2).
A = a{a:2, dummy:_G3195},
B = a{a:1, dummy:_G3391}.
[det]b_set_dict(+Key, !Dict, +Value)
Destructively update the value associated with Key in Dict to Value. The update is trailed and undone on backtracking. This predicate raises an existence error if Key does not appear in Dict. The update semantics are equivalent to setarg/3 and b_setval/2.
[det]nb_set_dict(+Key, !Dict, +Value)
Destructively update the value associated with Key in Dict to a copy of Value. The update is not undone on backtracking. This predicate raises an existence error if Key does not appear in Dict. The update semantics are equivalent to nb_setarg/3 and nb_setval/2.
[det]nb_link_dict(+Key, !Dict, +Value)
Destructively update the value associated with Key in Dict to Value. The update is not undone on backtracking. This predicate raises an existence error if Key does not appear in Dict. The update semantics are equivalent to nb_linkarg/3 and nb_linkval/2. Use with extreme care and consult the documentation of nb_linkval/2 before use.