1

I am wondering if you can "reopen" an environment to extend its definition, similar to how in programming languages like Ruby you can reopen a class/module anywhere in your code and extend its definition.

So for example, say you have this in one part of your document:

\newenvironment{myenv}{
  \newcommand\a{}
}{}

Then later on in the document you do this:

\reopenenvironment{myenv}{
  \newcommand\b{}
}{}

That way the end result is this:

\newenvironment{myenv}{
  \newcommand\a{}
}{}

\reopenenvironment{myenv}{
  \newcommand\b{}
}{}

Which should be functionally equivalent to:

\newenvironment{myenv}{
  \newcommand\a{}
  \newcommand\b{}
}{}

Wondering if anything like that exists in Latex. The reasoning is say you have a class with some generic definitions in it. You may want to reuse a specific diagram in various formats a few times, but it doesn't warrant adding it to the class definition. Instead you would just define it inline in your document and use it where right there where needed.

Another variation of this question (or another way this could potentially be accomplished) is by adding a new conditional block to an existing command. So say we had \a as in:

\newenvironment{myenv}{
  \newcommand\a[1]{
    \ifthenelse{\equal{##1}{\string foo}}
      {\typeout{foo}}
      {}
  }
}{}

Then you could extend the definition like this at runtime perhaps:

\reopenenvironment{myenv}{
  \reopencommand\a[1]{
    \ifthenelse{\equal{##1}{\string bar}}
      {\typeout{bar}}
      {}
    \super{}
  }
}{}

That way you can extend it at runtime so you can keep the API:

\begin{myenv}
  \a{foo}
  \a{bar}
\end{myenv}

Wondering if anything like this exists or if there is a normal way to accomplish this with latex.

I would like to be able to do the following:

\begin{athing}{label=baz}
  \stuff
  \stuff
  \stuff
  \stuff
\end{athing}

And then have it usable as:

\begin{myenv}
  \a{baz}
\end{myenv}

Maybe this could somehow be done with a local key/value store. That is,

kvstore = {
  foo: athing
  bar: athing
  baz: athing
}

\newcommand\a[1]{
  kvstore[##1]
}

Maybe that sort of thing is possible, that would be another solution if it was possible.

So to summarize, I would like to be able to have an API:

myapi{somelabela}
myapi{somelabelb}
...

Such that it can be extended at runtime with more definitions. The 3 approaches outlined here are just initial thoughts, I am new to latex. Thank you so much for your help.

  • please always provide a complete small test document. For example your initial environment has \newcommand\a which will already generate errors as \a is a predefined command in latex, so the initial environment does not work so it's hard to test extending it. – David Carlisle Mar 23 '18 at 12:56
  • It sounds like you want to use LaTeX in a not-so-LaTeX-y way - which is not going to be very pleasant :) For your first suggestion: this is easy with \AtEndEnvironment from the etoolbox package. – Marijn Mar 23 '18 at 14:12
1

LaTeX3 (expl3) actually has a nice interface for working with token lists, which would have more or less the functionality you are looking for.

Edit: Realized that my (previously) first approach, wasn't really canonical. This is much better and less hacky:

In LaTeX I suspect that the canonical approach would be using macro hooks, or more specifically \g@addto@macro. This is how \AtBeginDocument works!

\makeatletter
\def\somehook{hello}
\g@addto@macro\somehook{ world}
\somehook %<- prints hello world
\makeatother

The \begin{myenv} command actually triggers the macro \myenv that is defined when you do \newenvironment. Thus:

\makeatletter
\newenvironment{myenv}{hello}{}
\g@addto@macro\myenv{ world}
\begin{myenv}
  !
\end{myenv}
\makeatother

Which prints hello world!


Here is my less canonical approach:

However you can, do it more or less yourself in plain TeX: One "hack" you can do then is

\newenvironment{myenv}{Hello}{}
\let\oldMyenv = \myenv
\def\myenv{\oldMyenv world}
\begin{myenv}
!
\end{myenv}

Which prints Hello world !. This could then be generalized to

\makeatletter
\def\removebs#1{\if#1\@backslashchar\else#1\fi}
\def\macname#1{\expandafter\removebs\string#1}
\def\reopencommand#1#2{
  \def\mname{\macname{#1}}
  \let\oldCommand#1
  \expandafter\gdef\csname \mname\endcsname{\oldCommand #2}
}
\def\hello{hello}
\reopencommand{\hello}{ world}
\hello
\makeatother
  • Cool! Would love to see how expl3 does it too :) – Lance Pollard Mar 23 '18 at 19:05
  • Take a look here: texdev.net/2012/01/22/… – Andreas Storvik Strauman Mar 23 '18 at 19:30
  • Just made a huge update with \g@addto@macro, so you might want to look through it again. Don't for get to accept answer if it satisfied your question :) – Andreas Storvik Strauman Mar 23 '18 at 19:37
  • I would like to see the latex3 way of accomplishing this, with \NewDocumentEnvironment and such if possible :) – Lance Pollard Mar 25 '18 at 22:37
  • What I was thinking about was not a built in function to do this, but the way they have builtins to work with token-lists: \tl_put_right:Nn and \tl_put_left:Nn, where \tl_put_right:Nn is basically defined as \protected\long\def\tl_put_right:Nn#1#2{\edef#1{\unexpanded\expandafter #1\unexpanded{#2}}. I can expand on it a little further a little later :) – Andreas Storvik Strauman Mar 26 '18 at 8:28

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.