Option vs pairs: when to use which

Hi there,
While learning Prolog, I find 2 “idioms” for key-value associations; one is

option(key(Value), OptionList)

the other variant being the “pairs-notation”

memberchk(key-Value, OptionList)

.
It seems both are not interopable (e.g.

option(key(Option), [key-value])
false

Question: when to use the “option variant” and when the Key-Value variant?
Thanks for any hints

1 Like

Since no one answered, I can start and then people can correct me when I say something wrong.

If you at need any of the predicates in library(pairs), then you should probably use pairs. Since Jan made sort/4 available, pairs are no longer necessary for sorting by a key – before that, you had to do the little dance described in the keysort/2 docs. But pairs are still useful. Read the docs to library(pairs) for ideas.

Then, if you compare foo(bar) to foo-bar, you are actually comparing foo(bar) to -(foo, bar). They are conceptually the same, which is why some predicates would accept either. I am struggling to find a good general rule, but in some cases it is easier/cleaner to use the one, and in some cases, the other. foo(bar) is slightly smaller, so if you have many, that could make a difference. If you use the term for indexing, foo(bar) should in theory be a bit more efficient than foo-bar (but please don’t believe me and do your own profiling).

One last thing: if you actually need to “unpack” the key and the value at run time, I have always assumed that foo-bar is the better choice. If you have foo(bar) and you need the foo, than you’d have to do Term =.. [Key, Value] which seems a bit unnecessary?

1 Like

If you don’t mind restricting yourself to SWI-Prolog, dicts are a handy way of handling options. I use library(optparse) and then the following code to convert between the options list from opt_arguments/3 and a dict – this avoids using memberchk(key(Value), Opts) and instead directly using Opts.key.

opts_dict(Opts, OptsDict) :- opts_dict(Opts, opts, OptsDict).

%! opts_dict(+Opts:list, ?DictTag:atom, -OptsDict:dict) is det.
%! opts_dict(-Opts:list, ?DictTag:atom, +OptsDict:dict) is det.
opts_dict(Opts, DictTag, OptsDict) :-
    (  var(Opts)
    -> dict_pairs(OptsDict, DictTag, OptsPairs),
       maplist(dict_pair_to_opt, OptsPairs, Opts)
    ;  var(OptsDict)
    -> maplist(opt_to_dict_pair, Opts, OptsPairs),
       dict_pairs(OptsDict, DictTag, OptsPairs)
    ;  instantiation_error(opts_dict(Opts, OptsDict))
    ).

dict_pair_to_opt(K-V, KV) :- KV =.. [K, V].

opt_to_dict_pair(KV, K-V) :- KV =.. [K, V].
2 Likes

Note that you can also use dict_create/3, which is more relaxed on the input, e.g.,

dict_create(D, #, [a(b), c(d)]).
D = #{a:b, c:d}.
2 Likes

Thank-you – I can delete a dozen lines of code!

1 Like

Actually this is a good “trick” because dict_create reconciles both the option-notation “a(b)” and the pairs-notation. “a-b”.
Nice…