# How do you signal an error from within an expandable function?

I would like to do some error checking from within an expandable function. But my attempts at signalling an error are not processed correctly, but rather returned to the input stream---contrary to my expectations.

Here's the code:

\documentclass{article}
\usepackage{pstricks,pst-eucl}
\psset{unit=0.25cm}
\usepackage{xparse}
\ExplSyntaxOn
\msg_new:nnnn {testing}{testing}{illegal~property}{}
%% property list
\prop_new:N \g__ace_all_properties_plist
%% setter
\cs_new_protected:Npn \__ace_set_property:nn #1#2
{
\prop_gput:Nnn \g__ace_all_properties_plist { #1 } { #2 }
}
%% getter
\cs_new:Npn \__ace_get_property:n #1
{
\prop_get:Nn \g__ace_all_properties_plist { #1 }
}
%%--- User interface for settr and gettr
\DeclareDocumentCommand{\settr}{ m m }
{
\__ace_set_property:nn { #1 }{ #2 }
}
\DeclareExpandableDocumentCommand{\gettr}{ m }
{
\prop_if_in:NnTF \g__ace_all_properties_plist { #1 }
{
\__ace_get_property:n { #1 }
}
{
%% this next line does not get executed
%% even when there #1 is not in the list
%<var 1>%\msg_info_text:n { #1 ~ is ~ not ~ defined }
\msg_fatal_text:n { testing}
}

}
\ExplSyntaxOff
\pagestyle{empty}
\begin{document}
\settr{displacement}{10}%

This picture gets processed correctly\par
\begin{pspicture}(0,0)(10,10)
\pstGeonode[PointName=default,PointSymbol=default,PosAngle=0](0,0){A}(0,1){B}
\pstTranslation[DistCoef=\gettr{displacement}]{A}{B}{A}[C]
\end{pspicture}

\vspace{1cm}

No error signaled here $\rightarrow$
\gettr{unknownproperty}

\vspace{1cm}

This picture should signal an error when commented line uncommented\par
\begin{pspicture}(0,0)(10,10)
\pstGeonode[PointName=default,PointSymbol=default,PosAngle=0](0,0){A}(0,1){B}
%\pstTranslation[DistCoef=\gettr{unknown-property}]{A}{B}{A}[C]
\end{pspicture}

\end{document}


Initially, I thought this was an issue of calling a protect function \msg_term:x from within an expandable function. But, my errors are not properly triggered even when using using \mgs_fatal_text:n. This later approach is not really what I want. But signalling an error in any way would be nice.

EDIT

Following @DavidCarlisle's suggestion, I tried replacing

\msg_fatal_text:n{ testing }


with

\ERROR


So, the compilation stops, but since the wrong type of error message is signaled, I can imagine wasting a lot of time looking for the wrong error. So, instead of \ERROR I tried

\use:c { Attempting ~ to ~ call ~ undefined ~ property -> {#1} }


I figured at least with this, I'd get an error that made it clearer what the real issue was. But this doesn't signal an error at all---contrary to my expectations.

-
\use:c is just \csname ...\endcsname hidden. This also doesn't trigger an error when the csname doesn't exist but defines it to be equal to \relax – clemens Feb 10 '13 at 16:19
@cgnieder, so how do I signal an error message that will better info me about what the real issue is? – A.Ellett Feb 10 '13 at 16:21
(In fact that is a way to safely call a command that does nothing if it doesn't exist. E.g. \csname phantomsection\endcsname won't do harm if hyperref is not loaded.) How about an undefined \undefinedproperty? – clemens Feb 10 '13 at 16:23
The kernel has the internal functions \__msg_kernel_expandable_error:nnn and \__msg_expandable_error:n that don't have, at least for now, a "programmer's level" interface. – egreg Feb 10 '13 at 17:02
@egreg. What does it mean to say there is not "programmer's level interface"? If they're documented and arguments not marked as ":D", doesn't that mean I can use them? – A.Ellett Feb 10 '13 at 17:12

If you actually want to generate the error in an expansion only context such as a \write or a PS \special the usual way is just to use an undefined command such as \ERROR You can not trigger any other error from there (and LaTeX3 can't really change that).

! Undefined control sequence.
l.1 ...ptingToCallUndefinedPropertyunknownproperty

\documentclass{article}
\usepackage{pstricks,pst-eucl}
\psset{unit=0.25cm}
\usepackage{xparse}
\ExplSyntaxOn
\msg_new:nnnn {testing}{testing}{illegal~property}{}
%% property list
\prop_new:N \g__ace_all_properties_plist
%% setter
\cs_new_protected:Npn \__ace_set_property:nn #1#2
{
\prop_gput:Nnn \g__ace_all_properties_plist { #1 } { #2 }
}
%% getter
\cs_new:Npn \__ace_get_property:n #1
{
\prop_get:Nn \g__ace_all_properties_plist { #1 }
}
%%--- User interface for settr and gettr
\DeclareDocumentCommand{\settr}{ m m }
{
\__ace_set_property:nn { #1 }{ #2 }
}
\DeclareExpandableDocumentCommand{\gettr}{ m }
{
\prop_if_in:NnTF \g__ace_all_properties_plist { #1 }
{
\__ace_get_property:n { #1 }
}
{
%% this next line does not get executed
%% even when there #1 is not in the list
%<var 1>%\msg_info_text:n { #1 ~ is ~ not ~ defined }
\foo{ Attempting  To  Call  Undefined  Property  #1 }
}

}

\ExplSyntaxOff

{\catcode\@=0
@catcode\\=12
@gdef@foo#1{@scantokens{\#1}}}

\pagestyle{empty}
\begin{document}
\settr{displacement}{10}%

This picture gets processed correctly\par
\begin{pspicture}(0,0)(10,10)
\pstGeonode[PointName=default,PointSymbol=default,PosAngle=0](0,0){A}(0,1){B}
\pstTranslation[DistCoef=\gettr{displacement}]{A}{B}{A}[C]
\end{pspicture}

\vspace{1cm}

No error signaled here $\rightarrow$
\gettr{unknownproperty}

\vspace{1cm}

This picture should signal an error when commented line uncommented\par
\begin{pspicture}(0,0)(10,10)
\pstGeonode[PointName=default,PointSymbol=default,PosAngle=0](0,0){A}(0,1){B}
%\pstTranslation[DistCoef=\gettr{unknown-property}]{A}{B}{A}[C]
\end{pspicture}

\end{document}


(AM) David's trick is interesting:

{
\catcode\|=0
|catcode\\=12
|gdef|throwundefined#1{|scantokens{\#1}}
}
\throwundefined{x}

-
Following your suggestion, I've attempted modifying my code. But, I'm still not getting what I want. How is my modification (see edit above) not doing what you were suggesting? – A.Ellett Feb 10 '13 at 16:17
you are nearly right: but \csname this is not defined\endscsname defines the new csname to be \relax so you can \let that to be \undefined before entering your expansion only code. This may mean you want a fixed command name not one using #1 so you can globally have a useful undefined command name. – David Carlisle Feb 10 '13 at 16:20
I added some code that makes a csname on the fly using scantokens (I leave you to translate to idiomatic L3:-) – David Carlisle Feb 10 '13 at 16:40
@DavidCarlisle: But I saw somewhere that you contributed to phrasing the idioms. – Ahmed Musa Feb 10 '13 at 23:00
@AhmedMusa last century sometime... – David Carlisle Feb 10 '13 at 23:14

Translaton of @DavidCarlisle's clever slight of hand:

\cs_new:Npn \foo #1
{
\tl_rescan:nn
{
\char_set_catcode_escape:N \@
}{
@#1
}
}

-
No: \tl_rescan:nn is not expandable. There is no expandable wrapper for \scantokens in LaTeX3. – Bruno Le Floch May 8 '13 at 10:15