Skip to content

Instantly share code, notes, and snippets.

@vermiculus
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vermiculus/d8ac080f3f8c7ec2bed6 to your computer and use it in GitHub Desktop.
Save vermiculus/d8ac080f3f8c7ec2bed6 to your computer and use it in GitHub Desktop.
\DeclareObjectType { name }
{
first : tokenlist ,
middle : tokenlist ,
last : tokenlist ,
first : .required ,
last : .required ,
}
\DeclareTemplateInterface { name } { full }
{
reversed : boolean = false ,
use-first-name : boolean = true ,
use-middle-name : boolean = true ,
use-last-name : boolean = true ,
first-name-format : tokenlist ,
middle-name-format : tokenlist ,
last-name-format : tokenlist ,
}
\tl_new:N \l_names_first_name_tl
\tl_new:N \l_names_middle_name_tl
\tl_new:N \l_names_last_name_tl
\tl_new:N \l_names_first_format_tl
\tl_new:N \l_names_middle_format_tl
\tl_new:N \l_names_last_format_tl
\bool_new:N \l_names_use_first_bool
\bool_new:N \l_names_use_middle_bool
\bool_new:N \l_names_use_last_bool
\bool_new:N \l_names_reversed_bool
\DeclareTemplateCode { name } { full }
{
first = \l_names_first_name_tl ,
middle = \l_names_middle_name_tl ,
last = \l_names_last_name_tl ,
}
{
reversed = \l_names_reversed_bool ,
use-first-name = \l_names_use_first_bool ,
use-middle-name = \l_names_use_middle_bool ,
use-last-name = \l_names_use_last_bool ,
first-name-format = \l_names_first_format_tl ,
middle-name-format = \l_names_middle_format_tl ,
last-name-format = \l_names_last_format_tl ,
}
{
\AssignTemplateKeys
\bool_if:nT
{
\tl_empty_p:N \l_names_middle_name_tl &&
\l_names_use_middle_bool
}
{
\typeout{! This is an error and I'm far too lazy to create a ^^J
proper l3msg. (In practice, probably better to be ^^J
a warning.) We were provided no middle name to use ^^J
with the use-middle-name directive.}
}
\bool_if:NTF \l_names_reversed_bool
{
\bool_if:NT \l_names_use_last_bool
{{\tl_use:N \l_names_last_format_tl \l_names_last_name_tl}}
\bool_if:nT
{
\l_names_use_first_bool &&
\l_names_use_last_bool
}
{ , ~ }
\bool_if:NT \l_names_use_first_bool
{{\tl_use:N \l_names_first_format_tl \l_names_first_name_tl}}
\bool_if:NT \l_names_use_middle_bool
{
\bool_if:NT \l_names_use_first_bool
{ ~ }
{\tl_use:N \l_names_middle_format_tl \l_names_middle_name_tl}
}
}
{
\bool_if:NT \l_names_use_first_bool
{
{\tl_use:N \l_names_first_format_tl \l_names_first_name_tl}
\bool_if:nT
{
\l_names_use_middle_bool ||
\l_names_use_last_bool
}
{ ~ }
}
\bool_if:NT \l_names_use_middle_bool
{
{\tl_use:N \l_names_middle_format_tl \l_names_middle_name_tl}
\bool_if:NT \l_names_use_last_bool
{ ~ }
}
\bool_if:NT \l_names_use_last_bool
{
{\tl_use:N \l_names_last_format_tl \l_names_last_name_tl}
}
}
}
\DeclareInstance { name } { standard } { full } { }
\DeclareDocumentCommand \Name { >{\SplitArgument{1}{~}}m }
{
\UseInstance { name } { standard }
{
first = \use_i:nn #2,
last = \use_ii:nn #2,
}
}
\DeclareDocumentCommand \SpecialName { m m m m }
{
\UseTemplate { name } { full } { #1 }
{
first = #2,
last = #3,
middle = #4,
}
}

Thoughts on xtemplate

I want to start all of this out by saying that I have no idea where to begin actually implementing all of this in the code-behind and that I marvel at the reality of xtemplate as it stands implemented in expl3. That said, interface design is nothing to scoff at (as I’m sure members of this list are aware), so I’m continuing under the assumption that this kind of proposal is of substance.

This is an opinion piece meant for constructive purposes :)

Introduction

This file is best viewed within emacs under org-mode. Sorry, vi (et al.).

Please see the Org source for the original question and Joseph Wright’s answer, but to keep this file short (as viewed on GitHub), I’ve commented it out here.

In brief, the discussion revolves around the current limitation of xtemplate’s object declaration syntax. Since TeX restricts the number of arguments a single macro can take, an object is limited to nine qualities. This is unacceptably restrictive, especially when considering things such as ‘optional’ pieces of data in an object.

Workarounds such as including the optional data in a template declaration is a violation of the separation between content and presentation. Objects should be data and only data; templates should be presentation and only presentation.

I know of no other solutions to the problem of optional data.

Design Thoughts

I believe Joseph is Wright on several points:

  • this is potentially an edge case,
  • long argument listings is unreasonable on the author level,
  • the code is experimental, and that
  • these are hard questions.

He gives two suggestions that I’ll flesh out here momentarily. First, I want to make sure that we are all on the same page about the roles involved here. The role that xtemplate gets involved in is every role except the author level:

objects
logic (document designer)
instances
design (typographer)
templates
programming (programmer)

We should not be overly-concerned with verbosity on these levels. Purity and consistency should, in my honest opinion, be top goals. (Consider this the spirit of Guy Steele speaking.)

Parameter-Based

Right now, we have a parameter-based system. Objects (\DeclareObjectType) are given a set of required attributes as \def-style parameters and template code (\DeclareTemplateCode) uses these parameters along with key–value input targeted specifically for layout parameters (such as a font or length). This paradigm is possibly the simplest to understand when the user (in this case, the document designer) already has a background in TeX.

Unfortunately, this style has the inherent limitation set out in the introduction: an object can have no more than nine parameters. This is caused by TeX’s own (more or less arbitrary) limitation in the primitive \def. There is no clean way of getting around this.

KeyVal-Based

Another option is to use a purely key–value syntax for this.

Object Creation

\DeclareObjectType { name }
  {
    first  : tokenlist                   ,
    middle : tokenlist                   ,
    last   : tokenlist                   ,
    first  : .required                   ,
    last   : .required                   ,
  }

Note that everything is given as an xtemplate-style key listing. This maintains consistency. Note also that the keys first and last are given the .required property; I opted against the possibly more consistent (for varying definitions of ‘consistent’) .value_required: (reminiscent of expl3) because expl3 is on an entirely different design level from xtemplate. I opted to keep the . because it well conveys the ‘meta’ quality of being a ‘required argument’. If it makes more sense to do so, I’m not against incorporating the required-ness into the type (in this case, tokenlist). Perhaps something like tokenlist* or tokenlist!.

This is more-or-less stream-of-consciousness as far as specific input syntax ideas go, but the idea of notating this (and potentially other) meta-information into this listing is the main idea I’d like to propose (again, partly from Joseph’s own prompting).

Template Creation

Templates would be created as normal:

\DeclareTemplateInterface { name } { full }
  {
    reversed           : boolean = false ,
    use-first-name     : boolean = true  ,
    use-middle-name    : boolean = true  ,
    use-last-name      : boolean = true  ,
    first-name-format  : tokenlist       ,
    middle-name-format : tokenlist       ,
    last-name-format   : tokenlist       ,
  }

This is included only to make a complete example use.

Template Code

This is another proposed change in the interface.

<<declare-template-code:variables>>

\DeclareTemplateCode { name } { full }
  {
    first              = \l_names_first_name_tl    ,
    middle             = \l_names_middle_name_tl   ,
    last               = \l_names_last_name_tl     ,
  }
  {
    reversed           = \l_names_reversed_bool    ,
    use-first-name     = \l_names_use_first_bool   ,
    use-middle-name    = \l_names_use_middle_bool  ,
    use-last-name      = \l_names_use_last_bool    ,
    first-name-format  = \l_names_first_format_tl  ,
    middle-name-format = \l_names_middle_format_tl ,
    last-name-format   = \l_names_last_format_tl   ,
  }
  {
    <<declare-template-code:code>>
  }

(adapted from Clemens Niederberger’s tutorial.)

Instances

It is only on this level do we see the re-introduction of LaTeX syntax. We marry the key–value interface with traditional 2e syntax as one would naturally expect.

\DeclareInstance { name } { standard } { full } { }

\DeclareDocumentCommand \Name { >{\SplitArgument{1}{~}}m }
  {
    \UseInstance { name } { standard }
      {
        first = \use_i:nn  #2,
        last  = \use_ii:nn #2,
      }
  }

\DeclareDocumentCommand \SpecialName { m m m m }
  {
    \UseTemplate { name } { full } { #1 }
      {
        first  = #2,
        last   = #3,
        middle = #4,
      }
  }

Hybrid

Given that the option .value_required: pattern exists (from l3keys), I’m not a big fan of this option. Still, it is conceivable that absolutely required arguments are given as TeX-style parameters where optional ones are given in key–value format. I include this possibility for completion’s sake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment