TransWikia.com

Error with edef and inner def,

TeX - LaTeX Asked by Patrick Häcker on March 18, 2021

I seem to lack understanding the combination of an edef and the definition of a macro inside another definition. This is probably best shown with an example.

The following MWE (I know it is not really useful so minimal) does not work:

documentclass{article}
defmymacro#1{%
    defdo##1{##1}%
    #1%
}

begin{document}
    edefsavedValue{mymacro{argument}}%
    savedValue: savedValue
end{document}

The error is in the edef line and states Illegal parameter number in definition of savedValue.

I thought that the edef would expand mymacro{argument}, which first expands defdo##1{##1} to nothing leading to the output argument, which is then saved into savedValue. Without the line defdo##1{##1} this seems to work as explained, but with that line I get the mentioned error.

What is the problem here?

Using xparse‘s command does work

documentclass{article}
usepackage{xparse}
NewDocumentCommand{mymacro}{m}{%
    defdo##1{##1}%
    #1%
}

begin{document}
    edefsavedValue{mymacro{argument}}%
    savedValue: savedValue
end{document}

and gives the expected output: result

What is the actual difference to using def?

3 Answers

the def operation is not expandable. In an edef def is simply a non-expandable token that stays unchanged. Similarly mymacro ends up being defined by protecteddef so does not expand in an edef.

showmymacro

shows

> mymacro=protected macro:
#1->def do ##1{##1}#1.
documentclass{article}
usepackage{xparse}
NewDocumentCommand{mymacro}{m}{%
    defdo##1{##1}%
    #1%
}

begin{document}
    edefsavedValue{mymacro{argument}}%
    savedValue: savedValue
end{document}

So there are no expandable tokens in the definition of savedValue so the edef is equivalent to def in this case. showsavedValue shows

   > savedValue=macro:
->mymacro {argument}.

Then when savedValue is expanded it is equivalent to

defdo#1{#1}argument

If you use def rather than protecteddef then when you get to the edef mymacro expands (but def doesn't so it is the same as

edefsavedValue{mymacro{argument}}%

is

edefsavedValue{defdo##1{##1}argument}%

then it tries to expand def but that is not expandable so it is left, then it tries to expand do so you get the expansion of whatever random definition do has at this point which happens to be

 do=noexpand.

and things go wrong....

Correct answer by David Carlisle on March 18, 2021

Another possibility to avoid the problems explained by David Carlisle is to completely avoid the edef. The trick is to put the saving inside of the "called function". This is a bit similar to C, where a pointer to the allocated memory is given as the first argument to a function and is normally an input-output-parameter. This pattern can probably save a lot of trouble with LaTeX.

documentclass{article}
usepackage{etoolbox}
defmymacro#1#2{%
    defdo##1{##1}%
    csdef{#1}{#2}%
}

begin{document}
    mymacro{savedValue}{argument}%
    savedValue: savedValue
end{document}

Answered by Patrick Häcker on March 18, 2021

Your code is:

documentclass{article}
defmymacro#1{%
    defdo##1{##1}%
    #1%
}

begin{document}
    edefsavedValue{mymacro{argument}}%
    savedValue: savedValue
end{document}

Two facts are relevant for the explanation:

  1. When a macro is expanded whose ⟨definition text⟩ contains sequences of hashes, then the amount of hashes in these sequences will be halved in the result of expanding the macro.
    With the code of your example mymacro{⟨argument⟩} yields defdo#1{#1}⟨argument⟩. The amount of hashes in all sequences ## of the ⟨definition text⟩ of mymacro is halved to # in the result of expanding mymacro.

  2. def is not expandable, therefore will not be removed from the token stream but will be left untouched during edef-expansion. (Unless an expandable control sequence is expanded during edef-expansion whose expansion yields removal of the def-token as is the case with gobble in the following: defgobble#1{} ... edeffoo{gobbledef whatsoever}.)

With edefsavedValue{mymacro{argument}} expandable tokens of the sequence mymacro{argument} are expanded until no expandable tokens are left before the assignment takes place.

def is not expandable. Therefore just expanding all expandable tokens in/coming from mymacro{argument} yields:

def⟨total expansion of all expandable tokens in the sequence "do#1{#1}argument" ⟩

If do is defined as something unexpandable, then just expanding all expandable tokens in/coming from mymacro{argument} yields the token-sequence:
defdo#1{#1}argument.

The likelihood is very high that the token-sequence that comes into being during edef-expansion as the expanded ⟨definition text⟩ contains #1 although the ⟨parameter text⟩ of the edef-definition of savedValue is empty/does not introduce an argument #1.

That's why you get the error Illegal parameter number in definition of savedValue.

In case do is undefined, you will get the error

! Undefined control sequence.
mymacro #1->def do

prior to the error Illegal parameter number in definition of savedValue.



The following example might provide better understanding of how edef works:

documentclass{article}
defmymacro#1{%
  defdo{#1}%
}
begin{document}
letdo=relax 
edefmacro{mymacro{bar}}
showmacro
letfoo=relax
defdo{foo}%
edefmacro{mymacro{bar}}
showmacro
deffoo{hoo}%
lethoo=relax
edefmacro{mymacro{bar}}
showmacro
end{document}    

Console output:

[...]
> macro=macro:
->def do {bar}.
l.8 showmacro
               
? 
> macro=macro:
->def foo {bar}.
l.12 showmacro
                
? 
> macro=macro:
->def hoo {bar}.
l.16 showmacro
                
? 
[...]

The following example might provide better understanding of the treatment of sequences of hashes during expansion:

documentclass{article}

makeatletter
newcommandstringifytillrelax[1]{%
  ifxrelax#1expandafter@firstoftwoelseexpandafter@secondoftwofi
  {}{string#1stringifytillrelax}
}%
makeatother

begin{document}
defmymacro{##}%
message{^^JSee the amount of hashes that comes from expanding stringmymacro: expandafterstringifytillrelaxmymacrorelax}%
defmymacro{####}%
message{^^JSee the amount of hashes that comes from expanding stringmymacro: expandafterstringifytillrelaxmymacrorelax}%
defmymacro{######}%
message{^^JSee the amount of hashes that comes from expanding stringmymacro: expandafterstringifytillrelaxmymacrorelax}%
defmymacro{########}%
message{^^JSee the amount of hashes that comes from expanding stringmymacro: expandafterstringifytillrelaxmymacrorelax}%
edefmymacrob{mymacro}
message{^^JSee the amount of hashes that comes from expanding stringmymacrob: expandafterstringifytillrelaxmymacrobrelax}%
edefmymacroc{mymacrob}
message{^^JSee the amount of hashes that comes from expanding stringmymacroc: expandafterstringifytillrelaxmymacrocrelax}%
end{document}    

Console output:

[...]
See the amount of hashes that comes from expanding mymacro: # 

See the amount of hashes that comes from expanding mymacro: ## 

See the amount of hashes that comes from expanding mymacro: ### 

See the amount of hashes that comes from expanding mymacro: #### 

See the amount of hashes that comes from expanding mymacrob: ## 

See the amount of hashes that comes from expanding mymacroc: #
[...]

Answered by Ulrich Diez on March 18, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP