11

What determines whether I should use a property list or a key-value list when implementing some interface in expl3 syntax?

The LaTeX3 documentation says

Property lists are intended for storing key-based information for use within code. This is in contrast to key–value lists, which are a form of input parsed by the keys module.

(interface3.pdf, p. 129)

This suggests to me that, if I'm providing a document-level interface, I should be using key-value lists rather than property lists, and that the latter should be reserved for more internal data handling.

However, property lists often feature in answers which seem prima facie to involve providing a document-level interface for handling user input. For example, egreg posted code of this kind yesterday.

This makes me think that I have just misunderstood the distinction which the L3 docs are getting at in the above quotation.

How is this distinction to be understood? That is, how do I decide whether to use property lists or key-value lists in a particular case?

EDIT

So what I have is roughly a somewhat more complicated version of the following

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \l_test_res_prop
\NewDocumentCommand \res { m }
{
  \group_begin:
  \seq_set_split:Nnn \l_tmpa_seq { , } { #1 }
  \seq_map_inline:Nn \l_tmpa_seq
  {
    \prop_item:Nn \l_test_res_prop { ##1 }
  }
  \group_end:
}
\cs_new_protected:Nn \test_res_set:nn
{
  \prop_if_exist:NF \l_test_res_prop
  {
    \prop_new:N \l_test_res_prop
  }
  \prop_put:Nnn \l_test_res_prop { #1 } { #2 }
}
\cs_generate_variant:Nn \test_res_set:nn { VV }
\keys_define:nn { test }
{
  res / unknown .code:n = {
    \test_res_set:VV \l_keys_key_tl \l_keys_value_tl
  },
}
\NewDocumentCommand\testset { +m }{
  \keys_set:nn { test } { #1 }
}
\ExplSyntaxOff
\testset{
  res/something={\begin{center}\Huge\bfseries\itshape Something \dots\end{center}},
  res/this way comes={\begin{center}\Huge\bfseries\itshape \includegraphics[width=.5\linewidth]{cauldron}\par this way comes \dots\end{center}},
  res/wicked={\begin{center}\Huge\bfseries\itshape wicked \dots\end{center}},
}
\usepackage{graphicx}
\begin{document}
  \res{something}
  \res{wicked,this way comes}
\end{document}

Something wicked this way comes ...

which is, indeed, using both key-value and property lists. But is this really legitimate usage? Don't get me wrong: it works very nicely, thank you. But it seems to rely on dubious sleight of hand ....

  • Just yesterday, Bruno posted something on the LaTeX team list about setting property lists with a key-value interface. – egreg Feb 25 '18 at 21:24
  • Well, key-value lists in the sense of \keys_define:nn provide more conveniency about checking what to do with the values, where in property lists values are stored only without much processing (which would be possible by applying other code on the property values and then storing them) -- In fact, using both of them is what I do in my (yet) private packages. For me, it's not vs, it's both ... and, whereas the key-value interface is the path to save data into property lists – user31729 Feb 25 '18 at 21:28
  • @egreg I'm already completely confused and you tell me worse is to come! – cfr Feb 25 '18 at 21:32
  • @ChristianHupfer How does that fit with the distinction in the documentation, though? I understand that they are processed differently, but I'm not sure how that exactly maps onto the internal/input distinction and that's what confuses me in the examples I've seen. Every time I decide to learn property lists, I read this bit in the manual, get confused and give up. I thought this time I'd try asking first and give up a little later ;). – cfr Feb 25 '18 at 21:34
  • 2
    You're mixing different concepts. Property lists are a data structure, key-value lists are a method to parse input. – Henri Menke Feb 25 '18 at 21:36
8

tl;dr

The two concepts are not really linked to each other, other than by the fact that both are based on functions (in the mathematical sense): one associates a value to the elements of the domain. The domain is predetermined in the case of key-value interface (but there's the unknown key that can extend it), it isn't for property lists. However, using a key-value interface for populating a property list is not at all a bizarre idea.

Extended answer

There is nothing wrong at all in using a key-value interface for storing data in a property list and indeed the answer you're referring to just does that.

The problem was to fill a template with some data that the user specifies in a key-value fashion. How this data is stored depends on what the macros are supposed to do with it.

If the data is to be used sequentially, then using a sequence is better, see https://tex.stackexchange.com/a/352436/4427 where data already available in key-value format is converted in a slightly different format good for BibTeX.

If the data is to be used by key, then a property list is the natural data type to employ, instead of setting up several token list variables; of course it's also a matter of personal preferences and efficiency. Token lists perform faster than property lists.

There's not much difference between

\keys_define:nn { cfr/foo }
 {
  a .tl_set:N = \l_cfr_foo_a_tl,
  b .tl_set:N = \l_cfr_foo_b_tl,
  c .tl_set:N = \l_cfr_foo_c_tl,
 }

and a hypothetical

\keys_define:nn { cfr/foo }
 {
  a .prop_put:Nn = \l_cfr_foo_prop { a },
  b .prop_put:Nn = \l_cfr_foo_prop { b },
  c .prop_put:Nn = \l_cfr_foo_prop { c },
 }

which is currently not available, but could be in the future. One can do it by

\prop_new:N \l_cfr_foo_prop
\keys_define:nn { cfr/foo }
 {
  a .code:n = \prop_put:Nnn \l_cfr_foo_prop { a } { #1 },
  b .code:n = \prop_put:Nnn \l_cfr_foo_prop { b } { #1 },
  c .code:n = \prop_put:Nnn \l_cfr_foo_prop { c } { #1 },
 }

The main difference in the two approaches, apart from efficiency, is that the second approach may allow for somewhat cleaner code when the data is used.

  • I guess if the token lists are named coherently, the former is as clear as the latter approach and then efficiency beats it, imho. – Skillmon likes topanswers.xyz Feb 25 '18 at 22:39
  • 1
    @Skillmon Yes, that's the idea I wanted to convey; however, I see slight advantages in coding \prop_item:Nn \l_cfr_foo_prop {#1} over \tl_use:c { l_cfr_foo_#1_tl }. Not a big deal, though, but the former is by default passed as \unexpanded, the latter needs \exp_not:v { l_cfr_foo_#1_tl } for the same. – egreg Feb 25 '18 at 22:44
  • OK. But it isn't equivalent if you don't want to specify the keys you pass values to in advance. Then I can do it easily with a property list, it seems, but not so easily with token lists. I mean I could do it with token lists, I guess, but it would require quite a bit more :c stuff later on, which is perfectly doable, but rather more complicated. On the other hand, my current approach seems like sleight of hand to me. Not actually wicked, perhaps, but at least rather imprudent. (But 'Something rather imprudent this way comes ...' doesn't have quite the same ring to it.) – cfr Mar 1 '18 at 2:23
  • @cfr — the only thing that looks strange to me in your question is the use of \seq_set_split with commas and then \seq_map; why not just \clist_map_inline? – Will Robertson Mar 1 '18 at 3:46
  • @egreg \prop_set_from_keyval:Nn? Could you add that to your answer? – Manuel Mar 1 '18 at 19:59
3

I've just seen \prop_set_from_keyval:Nn in interface3.pdf, so that might be what you want to use.

Regarding the question “property lists vs. key-value lists” as many have answered I think that the fact is that “key-value lists” do not exist, they are a form of input, but it seems convenient to do \setup{foo1=bar,foo2=baz} rather than \setup{foo1}{bar}\setup{foo2}{baz}.

This command seems to be the answer from expl3 to that:

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\prop_new:N \l_test_res_prop

\NewDocumentCommand \res { m }
 {
  \clist_map_inline:nn { #1 }
   {
    \prop_item:Nn \l_test_res_prop { ##1 }
   }
 }
\NewDocumentCommand \testset { +m }
 {
  \prop_set_from_keyval:Nn \l_test_res_prop { #1 }
 }

\ExplSyntaxOff

\testset{
  something={\begin{center}\Huge\bfseries\itshape Something \dots\end{center}},
  this way comes={\begin{center}\Huge\bfseries\itshape \includegraphics[width=.5\linewidth]{cauldron}\par this way comes \dots\end{center}},
  wicked={\begin{center}\Huge\bfseries\itshape wicked \dots\end{center}}
}

\usepackage{graphicx}

\begin{document}

\res{something}
\res{wicked, this way comes}

\end{document}

If you don't want to rely on this function because of its experimentalness, you could define your own, and then update if it gets moved to stable:

\cs_new_protected:Npn \cfr_prop_put_from_keyval:Nn #1 #2
 {
  \cs_set:Npn \__cfr_prop_put:nn ##1 ##2 { \prop_put:Nnn #1 { ##1 } { ##2 } }  
  \keyval_parse:NNn \use_none:n \__cfr_prop_put:nn { #2 }
 }
  • I have not tested this because I don't have TeX up to date. But I do think that this should work, and the command being in interface3.pdf means that it's stable. But I might be wrong. – Manuel Mar 1 '18 at 22:06
  • I cannot check if this lets you set the prop more than once and each time the command puts new items, or if the prop is cleared and reset to have only those items. May be in that case something like \prop_put_from_keyval:Nn would be nice. – Manuel Mar 1 '18 at 22:08
  • This does indeed work. I won't use this at the moment because I'd rather not rely on experimental functions if I can possibly avoid it. (I'm just too dependent on this code.) However, this is certainly a good thing to know if it makes it into the stable part of expl3. – cfr Mar 2 '18 at 0:52
  • I added another version that relies on \keyval_parse:NNn. I didn't see it was under experimental, thanks for the note. – Manuel Mar 2 '18 at 10:49
  • Thanks again. This is interesting, although I think it would make things overly complicated in my real code. However, my brain isn't working right now, so this might be why. – cfr Mar 3 '18 at 2:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.