Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

How does one make custom \if variants in core TeX?

I would like to write:

\ifshape{it} ... \else ... \fi

as in:

\textit{Italic? \ifshape{it}Yes\else No\fi}

Problem is, I get the error "Too many }'s" when I define \ifshape as

\makeatletter
\newcommand{\ifshape}[1]{%
  \def\ifshape@test{#1}%
  \ifx\f@shape\ifshape@test%
}
\makeatother

Is the \if...\else...\fi construct actually lexically scoped? I didn't think (La)TeX had lexical scoping. I was expecting the above (definition plus invocation) to expand to:

\def\ifshape@test{it}\ifx\f@shape\ifshape@test Yes\else No\fi

Of course, it works fine if I say:

\makeatletter
\newcommand{\ifshapethenelse}[3]{%
  \def\ifshape@test{#1}%
  \ifx\f@shape\ifshape@test{#2}\else{#3}\fi%
}
\makeatother

but I'd like to write using infix notation rather than prefix notation, now that @DavidCarlisle has pointed out to me that \ifthenelse (from the package ifthen) is an infix monster and doesn't really do anything magical anyway.

Is there some combination of \relax and \noexpand that I need to insert somewhere to make this work?

share|improve this question

2 Answers 2

up vote 13 down vote accepted

The problem is that the expansion of \textit begins with a conditional:

\ifmmode
  \nfss@text {\itshape #1}%
\else
  \hmode@bgroup\text@command{#1}\itshape\check@icl
  #1%
  \check@icr\expandafter\egroup
\fi

Let's see what happens when you put in place of #1 your argument:

\ifmmode
  \nfss@text {\itshape Italic? \ifshape{it}Yes\else No\fi}%
\else
  \hmode@bgroup\text@command{Italic? \ifshape{it}Yes\else No\fi}\itshape\check@icl
  Italic? \ifshape{it}Yes\else No\fi
\check@icr\expandafter\egroup
\fi

Here's the problem: the \else and \fi of your argument match \ifmmode: you're not in math mode, so the "false" path is followed and then the stray closing brace is found. The macro \ifshape is not expanded, because it's in the "true" path.

The usual way to solve this problem is

\makeatletter
\newcommand{\ifshape}[1]{%
  \def\f@shapetest{#1}%
  \ifx\f@shape\f@shapetest
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}
\makeatother

and calling it as

\textit{Italic? \ifshape{it}{Yes}{No}}

In this case no \else or \fi will be seen until expanding \ifshape and they will properly refer to the \ifx.

An alternative way is to say

\makeatletter
\newcommand\isshape[1]{%
  TT\fi
  \def\f@shapetest{#1}%
  \ifx\f@shape\f@shapetest}
\makeatother

that can be called as

\textit{Italic? \if\isshape{it}Yes\else No\fi}

A trick which is worthy a close look (it was devised by the grand Wizard himself).

What's this trick about? It's based on the fact that \if wants to find two unexpandable tokens after it. When \if is found in an expansion context, it expands \isshape and immediately finds TT that are equal, so TeX follows the "true" branch , which is empty, and the following \fi disappears. If one had used TL, instead, TeX would have followed the "false" branch, which is empty either!

Next comes, possibly after other tokens, the conditional which really concerns us, which is evaluated as usual.

In case the \if\isshape is found in a non-expansion context, for example in the "false" branch of another conditional, the \else and \fi that we intend to do the work of the inner conditional keep TeX happy as they are paired with the "bogus" \if.

The only important thing is that the two tokens are unexpandable, so \relax\relax or \hfuzz\uchyph would work as well.

share|improve this answer
    
That's diabolical cleverness if I've ever seen it! Cool, that works for me. I'm selecting this as the accepted answer because it suggests the conditional \isshape rather than a customized \if variant. Question: Why TT? The TT can be any pair of tokens. It took me a minute to realize that this has nothing to do with the teletype font but that it stood for "token token". If I use \relax\relax instead of TT, that works just as well, as does any unequal token pair. What would be the clearest token pair to use? (The most confusing might be \fi\if. :) –  Todd Lehman Feb 7 '12 at 19:40
    
The important thing is that they are unexpandable, so \fi\if are not recommendable. But the merit goes to the grand Wizard, of course. You might use TL, for example. –  egreg Feb 7 '12 at 20:26
    
I see you added some more stuff to your answer. Cool. And I just did a spit-take with my coffee when I read \hfuzz\uchyph. I'll go with TT if that's what the Grand Wizard himself used. It needs a comment anyway no matter what is used. –  Todd Lehman Feb 7 '12 at 20:58

If you're willing to change a little the syntax of your test to \ifshape{it}\then Yes\else No\fi, you can also use the following trick by Donald Arseneau (from the \ifnum for real numbers thread on comp.text.tex) :

\documentclass{article}

\makeatletter
\let\then\iffalse
\def\ifshape#1\then{%
  \def\ifshape@test{#1}%
  \ifx\f@shape\ifshape@test
}
\makeatother

\begin{document}

\textit{Italic? \ifshape{it}\then Yes\else No\fi}

\textbf{Italic? \ifshape{it}\then Yes\else No\fi}

\end{document}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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