Your test doesn't work as expected because \if
doesn't work the way you think. \if
expands the tokens in front of it until it has two unexpandable ones, then it compares their character codes. More precisely, let's quote the TeXbook (p. 209):
TeX will expand macros following \if
until two unexpandable tokens are found. If either token is a control sequence, TeX considers it to have character code 256 and category code 16, unless the current equivalent of that control sequence has been \let
equal to a non-active character token. In this way, each token specifies a (character code, category code) pair. The condition is true if the character codes are equal, independent of the category codes. For example, after \def\a{*}
and \let\b=*
and \def\c{/}
, the tests \if*\a
and \if\a\b
will be true, but \if\a\c
will be false. Also \if\a\par
will be false, but \if\par\let
will be true.
Now, let's analyse how it works in your macro (Steven B. Segletes and egreg already pointed out that you don't use proper syntax for the optional argument [namely, square brackets when the macro is used], I won't comment on that further):
\newcommand{\reason}[1][]{\if\#1\ \ \else \textnormal{(#1)}\fi}
When this definition is read by TeX, it will store as the replacement text:
the control sequence tokens \if
and \#
;
a character token with character code 49 (the TeX-internal code for 1
) and category code 12 (other);
two control sequence tokens \<space>
(backslash followed by a space), i.e. control spaces;
the control sequence token \else
;
the control sequence token \textnormal
;
etc.
This is very important because you probably believed that the first #1
would be replaced by the first argument when \reason
is expanded, however it won't. The first #
has been tokenized differently, as we just saw (it is the name of the control sequence token \#
, and as such is “embedded” in this control sequence token, so to speak).
Now, how does it behave according to the rules given in the TeXbook excerpt quoted above for \if
?
\if
expands tokens until it has two unexpandable ones. It starts with \#
, which is defined as \chardef\#=`\#
in the LaTeX format (latex.ltx
line 610 here). So, \#
is a \chardef
token, and as such is an unexpandable control sequence token. For the sake of \if
, it has character code 256 (in traditional TeX) according to the rules quoted above, because a \chardef
token is not \let
-equivalent to a character token (these are just different kinds of beasts).
\if
needs another unexpandable token to decide. What comes next? The character token 1
(character code 49, category code 12). This character token is non-active (its catcode is different from 13), thus it is unexpandable. So, there we are, we now have two unexpandable tokens for \if
.
The first of these tokens is considered for \if
to have character code 256, and the second one has character code 49 (this is the TeX internal code for 1
, which normally coincides with ASCII).
256 is different from 49, therefore the \if
test is false. As you can see, the true-or-false result of this test doesn't depend at all on the arguments passed to the \reason
macro! (the expansion of the \if ... \fi
construct depends on the first argument because of the other #1
, but that is another thing).
Here are two ways to implement your macro, one using \if
and \detokenize
, the other using etoolbox
's \ifstempty
macro (the \detokenize
e-TeX primitive expands to character tokens of category code 12, except spaces which come out with category code 10; the expansion of \detokenize{...}
is empty if the ...
“argument” is empty).
\documentclass{article}
\newcommand{\reason}[1]{%
\if\relax\detokenize{#1}\relax
% nothing
\else
\textnormal{(#1)}%
\fi
}
\begin{document}
\begin{enumerate}
\item \reason{text $x$}
\item \reason{ }
\item \reason{\ }
\item \reason{}
\end{enumerate}
\end{document}

\documentclass{article}
\usepackage{etoolbox}
\newcommand{\reason}[1]{%
\ifstrempty{#1}{}{\textnormal{(#1)}}%
}
\begin{document}
\begin{enumerate}
\item \reason{text $x$}
\item \reason{ }
\item \reason{\ }
\item \reason{}
\end{enumerate}
\end{document}
Same output.
Note that in case you want to consider “only-spaces” the same way as “empty,” etoolbox
has the \ifblank
macro readily available.
If you want to obtain an error when the argument of your \reason
macro contains blank lines (or, equivalently, \par
tokens), use \newcommand*
instead of \newcommand
.