Add equivalent to `\providecommand` from LaTeX
robertbachmann opened this issue · 5 comments
LaTeX has \providecommand{\NAME}{...}
, that is a shorthand for:
\unless\ifcsname NAME\endcsname \newcommand{\NAME}{...} \fi
(see https://latexref.xyz/_005cprovidecommand.html)
In the past I've used this pattern in OpTeX:
\isdefined{foo}\iffalse
\def\foo{...}
\fi
However since I define multiple macros that way (use case explained below), it got way to verbose
and I decided to write my own macro \softdef
and \softsdef
:
\def\softdef#1{\ea\ifcsname\csstring #1\endcsname%
\afterfi{\ea\def\csname softdef:dummy\endcsname}%
\else\afterfi{\def#1}\fi}
\def\softsdef#1{\ifcsname #1\endcsname%
\afterfi{\ea\def\csname softdef:dummy\endcsname}%
\else\afterfi{\ea\def\csname #1\endcsname}\fi}
It's written so that for example \long\protected\softdef\foo
would also do the right thing.
Notes:
- I never had a concrete use case for a "soft" version of
\let
so I never looked
into that. - I experimented with different names. Initially I had
\providedef
and\providesdef
.
I'm currently prefering "soft" since it makes more sense to me, but don't care
strongly about the name of the macros.
My proposal:
- a) Include the macros in OpTeX's base format
- b) Or alternatively, add them as loadable tricks?
I think I would prefer base since I think autoloading might get confusing:
This case would work:
\softdef\foo{...}
This case would most likely not work without explicit '\loadtrick\softdef' beforehand:
\long\softdef\qux{...}
However if you don't want to have these macros in the base format, it's also
fine. I can do '\loadtrick\softdef' beforehand.
Please let me know, so I can prepare a pull-request for option a or b.
Side note: My use case
For the pandoc optex writer I'm using some helper macros.
Backgroun: When converting **xy**
, pandoc does
- uses logical
<strong>xy</strong>
for HTML - uses physical formatting
\textbf{xy}
for LaTeX (because the maintainers don't want to use custom macros)
So for HTML the user can easily customize with CSS:
strong { font-weight: normal; color: red; }
For LaTeX he would need to overwrite \textbf
which might cause some unwanted side effects.
For the OpTeX writer I decided¹ against {\bf xy}
instead I produce \strong{xy}
and provide a default
defintion for \strong
at the top of the generated document/document fragment².
\softdef\strong#1{\begingroup\bf #1\endgroup}
\regmacro{}{}{\let\strong=\useit}
If the user wants a different style he can simply:
\def \strong#1{\begingroup\Red\it #1\endgroup}
\strong
is just one example. Another example would be [Some marked text]{.mark}
(<mark>
html element) or * * *
(<hr>
)
¹ can be added as toggle-able option if someone really needs it
² fragment: Pandoc can generate standalone documents or partial documents (fragments). For TeX these fragments would then be usually included via \input
.
Maybe, it should be better to create a "prefix" for more general usage, like \newpublic
is. For example its name can be \newonly
and we can use \newonly\def\foo
, \newonly\edef\foo
, \newonly\mathchardef\foo
, \newonly\newcount\foo
and we can use \newonly{\long\def}\foo
. This prefix can be created as loadable trick.
Yes, that would work as well!
The only thing I would need that \newonly
would work with both \def
and \sdef
.
I think the following should work:
<h2><a name="newonly"></a>Only define a macro if it is not already defined</h2>
<p CLASS="rmarg">
autoload:<br>\newonly
</p>
<p>
LaTeX has <code>\providecommand{\NAME}{...}</code>
that only defines <code>\NAME</code> if it was not already defined.
We can achieve this in OpTeX by wrapping <code>\def\NAME{...}</code> inside
<code>\isdefined{\NAME}\iffalse ...\fi</code>.
However, this will get verbose if we need to define many macros in this manner.
Therefore, we implement a similar functionality as a prefix macro <code>\newonly</code>.
</p>
<p>Usage examples:</p>
<pre>
\newonly\def\mymacro#1{...}
\newonly\let\myfont=\bf \newonly\let\myfont\bf
\newonly\slet{myfont}{bf}
\newonly{\protected\long\def}\foo#1{...}
\newonly\newtoks\mytoks
</pre>
<p>The implementation:</p>
<pre>
\def\newonly#1#2{\begingroup%
\edef\tmpA{\_noprefix{#2}}%
\edef\tmpB{\string #2}%
\ea\ifcsname\tmpA\endcsname%
\ifx\tmpA\tmpB% #2 has no backslash
\gdef\newonlyA{#1{newonlyB}}\else%
\gdef\newonlyA{#1\newonlyB}\fi%
\else%
\ifx\tmpA\tmpB%
\gdef\newonlyA{#1{#2}}\else%
\gdef\newonlyA{#1#2}\fi%
\fi\endgroup\newonlyA}
</pre>
<p CLASS=datum>(0129) -- Robert Bachmann 2024-02-11<p>
edit: I can prepare a PR in the evening or on monday
- A minor improvements:
\ea\endgroup\newonlyA
and\def
instead\gdef
(4x). - Why use
\_noprefix
and no single\csstring
? I mean that nobody needs to use\newonly\def\_foo
. Moreower it is bad because\foo
is checked but\_foo
is redefined. - The name
\newonly
for this prefix was my first idea. If you can find better name, use it. - Maybe , abbreviations
\def\softdef{\newonly\def}
,\def\softsdef{\newonly\sdef}
should be mentioned in the OpTeX trick too.
Thanks for your feedback.
It's a bit hard to find a good name: things I like so far \onlynew
and \onlyifnew
:
LaTeX has \providecommand{\NAME}{...}
that only defines \NAME
if it
was not already defined. We can achieve this in OpTeX by wrapping
\def\NAME{...}
inside \isdefined{\NAME}\iffalse ...\fi
. However,
this will get verbose if we need to define many macros in this manner.
Therefore, we implement a similar functionality as a prefix macro
\onlyifnew
.
Usage examples:
\onlyifnew\def\mymacro#1{...}
\onlyifnew\let\myfont=\bf \onlyifnew\let\myfont\bf
\onlyifnew\slet{myfont}{bf}
\onlyifnew\newtoks\mytoks
\onlyifnew{\protected\long\def}\foo#1{...}
In the last case, it could be useful to define a shortcut such as:
\def\mylpdef{\onlyifnew{\protected\long\def}}
\mylpdef\foo#1{...}
The implementation:
\def\onlyifnew#1#2{\begingroup%
\edef\tmpA{\csstring #2}%
\edef\tmpB{\string #2}%
\ea\ifcsname\tmpA\endcsname%
\ifx\tmpA\tmpB% #2 has no backslash
\def\onlyifnewA{#1{onlyifnewB}}\else%
\def\onlyifnewA{#1\onlyifnewB}\fi%
\else%
\ifx\tmpA\tmpB%
\def\onlyifnewA{#1{#2}}\else%
\def\onlyifnewA{#1#2}\fi%
\fi\ea\endgroup\onlyifnewA}
OK, would you prepare the pull request?