# Defining a macro with three optional arguments in the form \newmacro{a}{b}[c]{d}[e][f] and \newmacro{a}{b}[c]{d}*[f]

I am defining a macro in LaTeX2e for writing function declarations.

I want that the macro has tree compulsory arguments: the name of the function (v.g f), a depiction of the type of variable it takes (v.g. x), and the space in which the function is defined (v.g. \mathbb R), as well as three optional arguments: the domain (if the domain is not the same as the codomain), the writing of the function applied to its variables, and the asignment/meaning of the function.

For the sake of clarity when using the macro I had decided for the following structure:

\funcdef{functionName}{variables}[domain]{codomain}[notation][definition]


To avoid ambiguity in the last two optional elements, when notation is given but no definition it takes the form:

\funcdef{functionName}{variables}[domain]{codomain}[notation]


And when definition is given but no notation:

\funcdef{functionName}{variables}[domain]{codomain}*[definition]


I came with the following code (both declaration and example), which is working but seems a little too complicated:

\documentclass{article}
\makeatletter
\newcommand\funcdef[2]{\def\func@name{#1}\def\func@var{#2}%
\begin{array}{r@{\ }c@{\,}c@{\,}l}#1:\func@dom}
\newcommand\func@dom[2][\@empty]{&\ifx#1\@empty#2\else#1\fi%
&\to&#2\\&\func@var&\mapsto&\func@use}
\newcommand\func@use{\@ifstar\func@use@\func@usei}
\newcommand\func@use@{\func@name(\func@var)\func@def}
\newcommand\func@usei[1][\@empty]{\ifx#1\@empty\func@name(\func@var)%
\else#1\fi\func@def}
\newcommand\func@def[1][\@empty]{\ifx#1\@empty\relax\else%
\mathrel{:=}#1\fi\end{array}}
\makeatother
\begin{document}
Simple function: $\funcdef fx{\mathbf R}$

Simple function with declaration: $\funcdef fx{\mathbf R}*[x^2]$

Function with alternative writing: $\funcdef{\textrm{exp}}x{\mathbf R}[e^x]$textrm{exp}

Function with alternative writing and declaration: $\funcdef\exp x{\mathbf R}[e^x][\lim\limits_{n\to\infty}% \left(1+\frac xn\right)^n]$

Function with different domain and codomain: $\funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}$

Function with different domain and codomain, and alternative
writing: $\funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}[\sqrt n]$

Function with different domain and codomain, alternative writing
and declaration: $\funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}[\sqrt n][\exp(\frac12\ln n)]$
\end{document}


Is there any other way to define in LaTeX2e these kind of macros that does not require too many supporting macros (5 in this sample) and temporal definitions (2 in this sample)?

How would it work in pain-TeX or LaTeX3? (answers to this will be upvoted but not accepted)

-
Xparse, job done. How ever I do not think this leaves the source particular readable –  daleif Dec 12 '13 at 19:47
@daleif would my declaration be simplified to \DeclareDocumentCommand\funcdef{mmomsoo}{...} ? –  Carlos Eugenio Thompson Pinzón Dec 12 '13 at 20:21
Yes. Then use \IfValueTF{#3}{<true code>}{<false code>} to test whether the optional argument #3 has a value, and \IfBooleanTF{#5}{<star there>}{<star absent>} for the star. I agree with daleif that the source is not readable. Perhaps a key--value syntax would be clearer? –  Bruno Le Floch Dec 12 '13 at 20:27

I would use a completely different approach, with a key-value syntax. Six arguments, some of them optional, with a * to denote a missing one are error prone.

\documentclass{article}
\usepackage{amsmath,keyval}

\makeatletter
\newcommand{\funcdef@key}[1]{%
\define@key{funcdef}{#1}{\@namedef{cet@#1}{##1}}%
\expandafter\let\csname cet@#1\endcsname\@empty
}
\funcdef@key{name}
\funcdef@key{domain}
\funcdef@key{codomain}
\funcdef@key{variable}
\funcdef@key{notation}
\funcdef@key{definition}
\newcommand{\funcdef@check}[1]{%
\expandafter\ifx\csname cet@#1\endcsname\@empty
\@latex@error{Missing #1'}{Provide #1'}%
\fi
}

\newcommand{\funcdef}[1]{%
\begingroup
\setkeys{funcdef}{#1}%
\ifx\cet@codomain\@empty\let\cet@codomain\cet@domain\fi
\funcdef@check{name}%
\funcdef@check{domain}%
\funcdef@check{variable}%
\begin{array}{l@{}r@{}l@{}l}
\cet@name\colon{} &
\cet@domain &
{}\to \cet@codomain \\
&
\cet@variable &
{}\mapsto
\ifx\cet@notation\@empty
\cet@name(\cet@variable)
\else
\cet@notation
\fi
\ifx\cet@definition\@empty
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{& {}\mathrel{:}=\cet@definition}
\\
\end{array}
\endgroup
}
\makeatletter

\begin{document}

\noindent
Simple function:
$\funcdef{name=f,variable=x,domain=\mathbf{R}}$
Simple function with declaration:
$\funcdef{ name=f, variable=x, domain=\mathbf{R}, notation=x^2 }$
Function with alternative writing:
$\funcdef{ name=\exp, variable=x, domain=\mathbf{R}, notation=e^x }$
Function with alternative writing and declaration:
$\funcdef{ name=\exp, variable=x, domain=\mathbf{R}, notation=e^x, definition=\lim\limits_{n\to\infty}\left(1+\frac xn\right)^n }$
Function with different domain and codomain:
$\funcdef{ name=\operatorname{sqrt}, variable=n, domain=\mathbf{N}, codomain=\mathbf{R} }$
Function with different domain and codomain, and alternative
writing:
$\funcdef{ name=\operatorname{sqrt}, variable=n, domain=\mathbf{N}, codomain=\mathbf{R}, definition=\sqrt{n} }$
Function with different domain and codomain, alternative writing
and declaration:
$\funcdef{ name=\operatorname{sqrt}, variable=n, domain=\mathbf{N}, codomain=\mathbf{R}, notation=\sqrt{n}, definition=\exp\bigl(\frac{1}{2}\ln n\bigr) }$
\end{document}


Here's an implementation in LaTeX3, with also an “inline” version.

\documentclass{article}
\usepackage{amsmath,xparse}

\ExplSyntaxOn
\keys_define:nn { funcdef }
{
name       .tl_set:N   = \l_funcdef_name_tl,
name       .initial:n  = {},
domain     .tl_set:N   = \l_funcdef_domain_tl,
domain     .initial:n  = {},
codomain   .tl_set:N   = \l_funcdef_codomain_tl,
codomain   .initial:n  = {},
variable   .tl_set:N   = \l_funcdef_variable_tl,
variable   .initial:n  = {},
variables  .tl_set:N   = \l_funcdef_variables_tl,
variables  .initial:n  = {},
notation   .tl_set:N   = \l_funcdef_notation_tl,
notation   .initial:n  = {},
definition .tl_set:N   = \l_funcdef_definition_tl,
definition .initial:n  = {},
inline     .bool_set:N = \l_funcdef_inline_bool,
}

\msg_new:nnnn { funcdef } { missing }
{
Missing~#1'
}
{
You~have~to~specify~a~value~for~#1';~%
I~have~substituted~??~for~it
}

\NewDocumentCommand{\funcdef}{ m }
{
\group_begin:
\funcdef_print:n { #1 }
\group_end:
}

\cs_new_protected:Npn \funcdef_print:n #1
{
\keys_set:nn { funcdef } { #1 }
\funcdef_check:n { name }
\funcdef_check:n { domain }
\tl_if_empty:NT \l_funcdef_variables_tl
{
\funcdef_check:n { variable }
}
\tl_if_empty:NT \l_funcdef_codomain_tl
{
\tl_set_eq:NN \l_funcdef_codomain_tl \l_funcdef_domain_tl
}
\bool_if:NTF \l_funcdef_inline_bool
{
\funcdef_print_inline:
}
{
\funcdef_print_array:
}
}

\cs_new_protected:Npn \funcdef_print_array:
{
\begin{array}{ l @{} r @{} l }
% first row
\l_funcdef_name_tl \colon {}
&
\l_funcdef_domain_tl
&
{}\to \l_funcdef_codomain_tl
\\
% second row
&
\tl_if_empty:NTF \l_funcdef_variable_tl
{
(\l_funcdef_variables_tl)
}
{
\l_funcdef_variable_tl
}
&
{}\mapsto
\tl_if_empty:NTF \l_funcdef_notation_tl
{
\l_funcdef_name_tl (
\tl_if_empty:NTF \l_funcdef_variable_tl
{
\l_funcdef_variables_tl
}
{
\l_funcdef_variable_tl
}
)
}
{
\l_funcdef_notation_tl
}
\tl_if_empty:NF \l_funcdef_definition_tl
{ \mathrel{:}= \l_funcdef_definition_tl }
\\
\end{array}
}

\cs_new:Npn \funcdef_print_inline:
{
\l_funcdef_name_tl \colon
\l_funcdef_domain_tl
\to \l_funcdef_codomain_tl
\tl_if_empty:NTF \l_funcdef_variable_tl
{
(\l_funcdef_variables_tl)
}
{
\l_funcdef_variable_tl
}
\mapsto
\tl_if_empty:NTF \l_funcdef_notation_tl
{
\l_funcdef_name_tl (
\tl_if_empty:NTF \l_funcdef_variable_tl
{
\l_funcdef_variables_tl
}
{
\l_funcdef_variable_tl
}
)
}
{
\l_funcdef_notation_tl
}
\tl_if_empty:NF \l_funcdef_definition_tl
{ \mathrel{:}= \l_funcdef_definition_tl }
}

\cs_new_protected:Npn \funcdef_check:n #1
{
\tl_if_empty:cT { l_funcdef_#1_tl }
{
\msg_error:nnn { funcdef } { missing } { #1 }
\tl_set:cn { l_funcdef_#1_tl } { ?? }
}
}

\ExplSyntaxOff

\begin{document}

\noindent
Simple function inline:
$\funcdef{inline,name=f,variable=x,domain=\mathbf{R}}$
Simple function:
$\funcdef{name=f,variable=x,domain=\mathbf{R}}$
Simple function with declaration:
$\funcdef{ name=f, variable=x, domain=\mathbf{R}, definition=x^2 }$
Function with alternative writing:
$\funcdef{ name=\exp, variable=x, domain=\mathbf{R}, notation=e^x }$
Function with alternative writing and declaration:
$\funcdef{ name=\exp, variable=x, domain=\mathbf{R}, notation=e^x, definition=\lim\limits_{n\to\infty}\left(1+\frac xn\right)^n }$
Function with different domain and codomain:
$\funcdef{ name=\operatorname{sqrt}, variable=n, domain=\mathbf{N}, codomain=\mathbf{R} }$
Function with different domain and codomain, and alternative
writing:
$\funcdef{ name=\operatorname{sqrt}, variable=n, domain=\mathbf{N}, codomain=\mathbf{R}, definition=\sqrt{n} }$
Function with different domain and codomain, alternative writing
and declaration:
$\funcdef{ name=\operatorname{sqrt}, variable=n, domain=\mathbf{N}, codomain=\mathbf{R}, notation=\sqrt{n}, definition=\exp\bigl(\frac{1}{2}\ln n\bigr) }$
Function of two variables:
$\funcdef{ name=f, variables={a,b}, domain=A\times B, codomain=C, }$
\end{document}


-
I would also prefer key-value syntax, if it's not too much effort switching to that. In @egreg's result, there seems to be a small deviation in the second alternative, which misses the f(x) := part; it should be easy to fix. –  nickie Dec 12 '13 at 21:03
@nickie Hawk eye! Just replace, in the second display, notation with definition. –  egreg Dec 12 '13 at 21:09
I'm not sure if I like the key-value approach for this particular project. I rather go for typing shortcuts than for readability. But I appreciate the beauty (and the example) of the key-value. A mixed approach such as \funcdef[codomain=\mathbb N,definition=\rfloor{x}\lfloor]{f}{x}{\mathbb R} might be what I'll end up to. –  Carlos Eugenio Thompson Pinzón Dec 12 '13 at 21:41
@CarlosEugenioThompsonPinzón I see no gain in this. In a couple of months you won't remember the precise syntax of the command. With keys, you can't miss them and the order doesn't matter. –  egreg Dec 12 '13 at 21:57
In a couple of months I will not remember the name of the keys or its exact orthography (sometimes). And having to type name= for something that always be there..., but this is a matter of personal preferences. –  Carlos Eugenio Thompson Pinzón Dec 12 '13 at 22:05

This seems to be very easy with xparse. The only thing that seems hard to do is to enforce the exact syntax that you want for differentiating the two last optional arguments. With the solution below, the following are allowed (as you want them):

\funcdef{f}{x}{R}
\funcdef{f}{x}{R}[2x]
\funcdef{f}{x}{R}[2x][x+x]
\funcdef{f}{x}{R}*[x+x]


but the following is allowed too:

\funcdef{f}{x}{R}[2x]*[x+x]


and is equivalent to the third one (of the first four). I do not consider this to be much of a problem; if you do, it will not be easy to fix.

\documentclass{article}
\usepackage{xparse}

\DeclareDocumentCommand{\funcdef}{m m o m o s o}{%
\begin{array}{r@{\ }c@{\,}c@{\,}l}
#1:
& \IfNoValueTF{#3}{#4}{#3}
& \to & #4 \\
& #2
& \mapsto
& \IfNoValueTF{#5}{#1(#2)}{#5}
\IfNoValueTF{#7}{}{\mathrel{:=}#7}
\end{array}
}

\begin{document}
Simple function: $\funcdef fx{\mathbf R}$

Simple function with declaration: $\funcdef fx{\mathbf R}*[x^2]$

Function with alternative writing: $\funcdef{\textrm{exp}}x{\mathbf R}[e^x]$

Function with alternative writing and declaration: $\funcdef\exp x{\mathbf R}[e^x][\lim\limits_{n\to\infty}% \left(1+\frac xn\right)^n]$

Function with different domain and codomain: $\funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}$

Function with different domain and codomain, and alternative
writing: $\funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}[\sqrt n]$

Function with different domain and codomain, alternative writing
and declaration: $\funcdef{\textrm{sqrt}}n[\mathbf N]{\mathbf R}[\sqrt n][\exp(\frac12\ln n)]$
\end{document}


-
I'd probably go with soo at the end, but ... –  Joseph Wright Dec 12 '13 at 20:59
@JosephWright, using soo will make *[LAST] bind LAST to the second last argument, instead of the last one. Then you would have to take into account the presence (or no presence) of the star, which I can completely disregard in my answer. I think that the two are equivalent. –  nickie Dec 12 '13 at 21:08
I can actually see one use for the extra starred version: when you want to allow both the standard functional notation and the alternative notation: f(x)=2x:=x+x –  Carlos Eugenio Thompson Pinzón Dec 12 '13 at 21:50