You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by "Geir Magnusson Jr." <ge...@earthlink.net> on 2002/03/26 09:42:12 UTC

Re: macro arguments may change implicitely - why not store arguments in local context ?

On 3/26/02 1:21 AM, "Claude Brisson" <cl...@savoirweb.com> wrote:

> 
> I'd like to discuss a strange behaviour (already mentionned sometimes, but
> without any concluding point, imho), and to make a
> proposal.
> 
> When velocimacro.context.localscope is false, a macro may implicitely change
> the actual value of its arguments by changing some
> external variable implied in their calculation.

Yep.

> 
> This is due to the fact that each time a non-constant argument is evaluated in
> a macro, it is re-evaluated with the global context
> that may have been changed by the same macro.

Yep.
 
> For instance, as shown in example 1, if a #foreach or a #set directive inside
> a macro uses a reference name similar to the name of a
> reference passsed to the macro, then the actual value of the parameters may
> also change in the same time.
> 
> ---------------------
> 
> ## Example 1 :
> 
> ## outputs "1 ! a b c" instead of expected "1 1 1 1 1"
> 
> #macro (test $arg)
> $arg ## -> 1
> #set($i = '!')
> $arg ## -> !
> #foreach ($i in ['a','b','c'])
> $arg ## -> a b c
> #end
> #end
> 
> #set($i = 1)
> #test($i) ## outputs "1 ! a b c" instead of expected "1 1 1 1 1"
> 
> ---------------------
> 
> This may be seen as a small side effect, but it can lead to very ankward
> behaviours in some situations... especially when dealing
> with macro recursions.

Yep.

It was hard work to make this behave this way :)

Originally, you could originally imagine that a VM invocation was simply an
inline replacement with it's definition, with the corresponding references
replaced with the passed in args.

When the VM system was redone, that behavior had to be mimiced w/o breaking
things, so there is a 'pass by name' system - where the local reference in
the VM is really like a reference to the outer reference that it was invoked
with.

I say 'pass by name' rather than 'pass by reference' because you can do
weird things like

#macro(foo $colortool)
   <tr bgcolor=$colortool><td>...</td></tr>
   <tr bgcolor=$colortool><td>...</td></tr>
   <tr bgcolor=$colortool><td>...</td></tr>
   <tr bgcolor=$colortool><td>...</td></tr>
#end

#foo( $tool.nextRowColor() )

And the method nextRowColor() will get invoked each time (i.e. 4 times in
the VM 'foo').


> ---------------------
> 
> ## Example 2 : (partial)
> 
> #macro (disp_node $node)
> #foreach ($child in $node.children())
> 
> $child.name is a child of $node.name
> ##                        ^^^^^^^^^^      (I hate proportional fonts)
> ## "$node.name" evaluate to (external) "$child.name", which equals (internal)
> "$child.name"
> ## so for a basic tree (grandparent-parent-grandson) we get the output
> "grandson is a child of grandson"
> 
> #disp_node($child)
> #end
> #end
> 
> ---------------------
> 
> This recursion problem should also be solved if #foreach directives avoided to
> store their working variables in the global context,
> but it's another point.

Indeed :)  That's possible, but it involves some kind of inverse wrapping
such that that val is protected.

Also, someone may expect to look at the last val in the context :

#foreach($item in $items)
...
#end

#if($item == 'whatever')
  Last was 'whatever'
#end


> 
> I finally made it out for my personal stuff, by the mean of two or three
> dreadful workarounds, but here is my point :
> 
>     Why couldn't macro directives only evaluate once their arguments at the
> moment they are called and save them in their local
> context ?
> 
> This sounds too simple... do I miss something ?

The thing we would lose is that method invocation feature, and people may be
expecting that they can use a VM to alter the global context.

Why not make context.localscope true?  What's the problem there?

> 
> As shown in example 3, only vicious minds (like mine here, for a didactic
> purpose) may try to exploit constructively this behaviour.
> Otherwise, it's rather strange (and time consuming) to re-evaluate arguments
> each time.
> 
> ---------------------
> 
> ## Example 3 : outputs "hello folks"
> 
> #macro (print_twice $value)
> $value $value
> #end
> 
> #set ($myarray = ["hello","folks"])
> 
> #print_twice($myarray.remove(0))
> 
> ---------------------
>

Yes, that's a bit wacked.  However, you can imagine that the color tool
thing is something people might use.

I

> Depending on the followup, I'd be glad to try to contribute attenant patches.
> 
> Thanx for reading up to that point.
> And last but not least, thanx a lot to the support team.
> 
> Sincerely,
> 
> CloD
> 
> 
> 
> --
> To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
> For additional commands, e-mail: <ma...@jakarta.apache.org>
> 

-- 
Geir Magnusson Jr.                                     geirm@optonline.net
System and Software Consulting
"They that can give up essential liberty to obtain a little temporary safety
deserve neither liberty nor safety." - Benjamin Franklin



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - whynotstorearguments in local context ?

Posted by Claude Brisson <cl...@savoirweb.com>.
> You should be able to read a 'global' value, just not set it.  Isn't that
> the case?

I re-tested to be sure...

Actually, no :

when velocimacro.context.localscope = true,

VMContext.get() only looks in localcontext.

Thanx,

CloD


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - why notstorearguments in local context ?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
On 3/28/02 11:21 AM, "Claude Brisson" <cl...@savoirweb.com> wrote:

> Ok, Geir, thanks a lot, your implementation works fine for my needs.
> 
>> We need to decide if it masks existing globals
>> completely - you can't read the values of those already existing. I guess it
>> should, however .... Dunno.  I can see that one either way.
> 
> Not sure the mask would be a so good idea : for the recursion case, for
> instance, the usage would not be as straightforward as it is
> now, as in :
> 
> #macro recurse($item)
>   #local($item)
>       ...
>       #recurse($item.child)
>       ...
>   #end
> #end
> 
> (there would be an extra reference, and an extra #set directive).
> 
I agree with you - I don't think it should mask either - when I said "I
guess it should" I should have completed my thought, which was "to be
consistent" - so you could use it to make it really a local.  Of course,
then someone would ask for some kind of 'get from global' element in VTL

  $:foo

Or something :)

The conclusion is that there are some subtle issues :)

> ---
> 
> Btw, Geir, some mails ago in this tread, you wrote :
> 
>>>> Why not make context.localscope true?  What's the problem there?
>>> - all needed global stuff must be passed as arguments to macros
>> That shouldn't be true.
> 
> Did you mean that the actual behaviour should be changed, or that it should
> not be the actual behaviour ?

You should be able to read a 'global' value, just not set it.  Isn't that
the case?

-- 
Geir Magnusson Jr.                       geirm@optonline.net
System and Software Consulting
You're going to end up getting pissed at your software
anyway, so you might as well not pay for it. Try Open Source.



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - why notstorearguments in local context ?

Posted by Claude Brisson <cl...@savoirweb.com>.
Ok, Geir, thanks a lot, your implementation works fine for my needs.

> We need to decide if it masks existing globals
> completely - you can't read the values of those already existing. I guess it
> should, however .... Dunno.  I can see that one either way.

Not sure the mask would be a so good idea : for the recursion case, for instance, the usage would not be as straightforward as it is
now, as in :

#macro recurse($item)
    #local($item)
        ...
        #recurse($item.child)
        ...
    #end
#end

(there would be an extra reference, and an extra #set directive).

---

Btw, Geir, some mails ago in this tread, you wrote :

>>> Why not make context.localscope true?  What's the problem there?
>> - all needed global stuff must be passed as arguments to macros
> That shouldn't be true.

Did you mean that the actual behaviour should be changed, or that it should not be the actual behaviour ?
'cause it IS the actual behaviour, cf. VMContext.java get method,#223, where in fact 'get' should always behave the same way and
ignore localcontextscope.

(hum... the comments reveal a cut&past from the 'put' method ;-) )

            if(localcontextscope)
            {
                /*
                 * if we have localcontextscope mode, then just
                 * put in the local context
                 */

                o =  localcontext.get( key );
            }
            else
            {
                /*
                 *  try the local context
                 */

                o = localcontext.get( key );

                if ( o == null)
                {
                    /*
                     * last chance
                     */

                    o = innerContext.get( key );
                }
            }


Thanx,

CloD




--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - why notstorearguments in local context ?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
On 3/27/02 12:12 PM, "Christoph Reck" <Ch...@dlr.de> wrote:

> Thanks Geir. It will sure be useful; but for the moment I'm stuck with
> debugging/enhancing/extending a swing application - no chance now to
> setup and test the new #local directive.

It may be useful, and it needs more work :)
> 
> Looking over the code I saw that it takes the current values of a given
> list of references and restores them later. It does not handle the case
> where listed local references are not available globally - null's are
> then restored?!

That¹s a valid issue - I think that if $foo isn't a valid reference before
the call, then it shouldn't be after.

> 
> So it acts a bit different than the #foreach loop variable. The way its
> implemented in Local.java might be ok - more to this below.
> 
> 
> I thought that the contract of the #local directive should be allowing
> to create context variables without affecting the callers (of macros
> and parsed documents). It sould also enable one to have some local
> declared variables dissapear after the #end.

Yes - that it should be.  We need to decide if it masks existing globals
completely - you can't read the values of those already existing. I guess it
should, however .... Dunno.  I can see that one either way.
 
> 
> If the #set( $foo = null ) handling is implemented as proposed in another
> thread (to remove the reference from the context) then the current Local.java
> will probably work:
> * references without dots will be saved and restoring them will
> remove local ones that did not exists before.

Because the real user context is wrapped, anything that didn't exist before
shouldn't be added - the outer context is removed, leaving the inner.  You
can't add 'null' anyway to the context, so this shouldn't be a problem.  If
it is (i.e. If the internals aren't forcing the 'no null' semantics) then
it's easy to check and not add it.

> * references with dots will restore values within references (it might
> call the setter with a null value when no proper get was available - but
> whoever does #local($foo.bar $woogie.bang)...#end should know what he
> is doing).

This actually is very problematic, as $foo.bar can imply $foo.setBar(value),
which has every chance of modifying state of the reference.  This isn't good
- there is no way to really protect that.  Copying a reference to the outer
protecting context obviously won't work, and doing a clone of it is just too
scary for my tastes.

We could just restrict the use of #local() to simple references rather than
the identifier/property idiom...

> This latter one sounds a bit like magic, but it is orthogonal to simple
> references so it should be OK.

So maybe we restrict to that...
 
> some more inline...
> "Geir Magnusson Jr." wrote:
>> 
>> On 3/27/02 8:42 AM, "Christoph Reck" <Ch...@dlr.de> wrote:
>> 
>>> Claude Brisson wrote:
>>>> 
>>>> Thank you for your responses, Christopher & Geir.
>>>> [snip]
>>>> Chirstopher's #local directive proposal looks quite right to me, and I
>>>> volunteers to write it.
>>>> But (supposing my code is ok), will it be incorporated as a new
>>>> functionnality ? A patch has a quite short lifetime...
>>>> Time for what, now, votes, or validation, or something ?
>>>> 
>>>> Thanx,
>>>> 
>>>> CloD
>>> 
>>> If you looked the ForeachDirective source and understood how parameters
>>> are passed to directives, then you can go ahead and implement it.
>>> 
>>> Geir, would a LocalDirective make it into the core or into the contribution
>>> sections?
>>> 
>> 
>> I did one yesterday in a boring session here at J1, and just put it into
>> whiteboard/geir
>> 
>> Give it a look.
>> 
>> I really don't think we should jump ahead and make this core - we can
>> support it in a contrib section, and if people really, really find it
>> useful, we should talk about adding it, but I don't think it's really a core
>> function...  Just my 0.02
> 
> I believe this is a candidate for core since it greatly enhances the
> usability/reliability of a (well written) generic macro library.

People believe all sorts of things :)

That is a valid point though.
 
>> 
>>> I you do not get flamed on cons about implementing a local directive, and
>>> Geir states he can put it into CVS, then you can read it as a "go ahead".
>> 
>> With pluggable directives, anyone can implement anything.  If there are good
>> contribs, we keep them.  But we don't have to add them to the core if they
>> are not obvious or in widespread use.
> 
> OK
> 
>> Having 'local variables' is something
>> that makes a lot of sense to us programmers, but I would bet means very
>> little to designers.
> 
> The 'local variables' feature is addressing programmers of #macro libraries
> and reusable template fragments (#parsed).

Don't forget that #macros() are designed for designers - "...they are just
bits of VTL so designers can take advantage of the concept of reuse...."

But yes, it is useful.
 
> Others don't have use them or even know about it.
> 
> In a previous project with another template language I used included/parsed
> templates to create complex form fields to handle data structures (e.g.
> geographical coverage, time ranges, etc.)
> 
> Also note that a #macro with a #local inside makes recursions a
> straightforward thing to follow. No magic on how to ensure the recursion
> will work.

Yep
 
>> 
>> I personally have a use for this, but I don't think many people do.
> 
> Dito.
> 
>> 
>> Geir

-- 
Geir Magnusson Jr.                       geirm@optonline.net
System and Software Consulting
You're going to end up getting pissed at your software
anyway, so you might as well not pay for it. Try Open Source.



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - why notstorearguments in local context ?

Posted by Christoph Reck <Ch...@dlr.de>.
Thanks Geir. It will sure be useful; but for the moment I'm stuck with
debugging/enhancing/extending a swing application - no chance now to 
setup and test the new #local directive.

Looking over the code I saw that it takes the current values of a given
list of references and restores them later. It does not handle the case
where listed local references are not available globally - null's are
then restored?!

So it acts a bit different than the #foreach loop variable. The way its
implemented in Local.java might be ok - more to this below.


I thought that the contract of the #local directive should be allowing 
to create context variables without affecting the callers (of macros
and parsed documents). It sould also enable one to have some local 
declared variables dissapear after the #end.


If the #set( $foo = null ) handling is implemented as proposed in another
thread (to remove the reference from the context) then the current Local.java 
will probably work: 
* references without dots will be saved and restoring them will 
  remove local ones that did not exists before.
* references with dots will restore values within references (it might
  call the setter with a null value when no proper get was available - but 
  whoever does #local($foo.bar $woogie.bang)...#end should know what he 
  is doing).
This latter one sounds a bit like magic, but it is orthogonal to simple
references so it should be OK.

some more inline...
"Geir Magnusson Jr." wrote:
> 
> On 3/27/02 8:42 AM, "Christoph Reck" <Ch...@dlr.de> wrote:
> 
> > Claude Brisson wrote:
> >>
> >> Thank you for your responses, Christopher & Geir.
> >> [snip]
> >> Chirstopher's #local directive proposal looks quite right to me, and I
> >> volunteers to write it.
> >> But (supposing my code is ok), will it be incorporated as a new
> >> functionnality ? A patch has a quite short lifetime...
> >> Time for what, now, votes, or validation, or something ?
> >>
> >> Thanx,
> >>
> >> CloD
> >
> > If you looked the ForeachDirective source and understood how parameters
> > are passed to directives, then you can go ahead and implement it.
> >
> > Geir, would a LocalDirective make it into the core or into the contribution
> > sections?
> >
> 
> I did one yesterday in a boring session here at J1, and just put it into
> whiteboard/geir
> 
> Give it a look.
> 
> I really don't think we should jump ahead and make this core - we can
> support it in a contrib section, and if people really, really find it
> useful, we should talk about adding it, but I don't think it's really a core
> function...  Just my 0.02

I believe this is a candidate for core since it greatly enhances the
usability/reliability of a (well written) generic macro library.

> 
> > I you do not get flamed on cons about implementing a local directive, and
> > Geir states he can put it into CVS, then you can read it as a "go ahead".
> 
> With pluggable directives, anyone can implement anything.  If there are good
> contribs, we keep them.  But we don't have to add them to the core if they
> are not obvious or in widespread use.  

OK

> Having 'local variables' is something
> that makes a lot of sense to us programmers, but I would bet means very
> little to designers.

The 'local variables' feature is addressing programmers of #macro libraries
and reusable template fragments (#parsed).

Others don't have use them or even know about it.

In a previous project with another template language I used included/parsed 
templates to create complex form fields to handle data structures (e.g. 
geographical coverage, time ranges, etc.)

Also note that a #macro with a #local inside makes recursions a 
straightforward thing to follow. No magic on how to ensure the recursion
will work.

> 
> I personally have a use for this, but I don't think many people do.

Dito.

> 
> Geir

-- 
:) Christoph Reck

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - why not storearguments in local context ?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
On 3/27/02 8:42 AM, "Christoph Reck" <Ch...@dlr.de> wrote:

> Claude Brisson wrote:
>> 
>> Thank you for your responses, Christopher & Geir.
>> [snip]
>> Chirstopher's #local directive proposal looks quite right to me, and I
>> volunteers to write it.
>> But (supposing my code is ok), will it be incorporated as a new
>> functionnality ? A patch has a quite short lifetime...
>> Time for what, now, votes, or validation, or something ?
>> 
>> Thanx,
>> 
>> CloD
> 
> If you looked the ForeachDirective source and understood how parameters
> are passed to directives, then you can go ahead and implement it.
> 
> Geir, would a LocalDirective make it into the core or into the contribution
> sections?
> 

I did one yesterday in a boring session here at J1, and just put it into
whiteboard/geir

Give it a look.

I really don't think we should jump ahead and make this core - we can
support it in a contrib section, and if people really, really find it
useful, we should talk about adding it, but I don't think it's really a core
function...  Just my 0.02

> I you do not get flamed on cons about implementing a local directive, and
> Geir states he can put it into CVS, then you can read it as a "go ahead".

With pluggable directives, anyone can implement anything.  If there are good
contribs, we keep them.  But we don't have to add them to the core if they
are not obvious or in widespread use.  Having 'local variables' is something
that makes a lot of sense to us programmers, but I would bet means very
little to designers.

I personally have a use for this, but I don't think many people do.

Geir

-- 
Geir Magnusson Jr.                                     geirm@optonline.net
System and Software Consulting
POC lives!


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - why not storearguments in local context ?

Posted by Christoph Reck <Ch...@dlr.de>.
Claude Brisson wrote:
> 
> Thank you for your responses, Christopher & Geir.
>[snip]
> Chirstopher's #local directive proposal looks quite right to me, and I volunteers to write it.
> But (supposing my code is ok), will it be incorporated as a new functionnality ? A patch has a quite short lifetime...
> Time for what, now, votes, or validation, or something ?
> 
> Thanx,
> 
> CloD

If you looked the ForeachDirective source and understood how parameters 
are passed to directives, then you can go ahead and implement it.

Geir, would a LocalDirective make it into the core or into the contribution
sections?

I you do not get flamed on cons about implementing a local directive, and
Geir states he can put it into CVS, then you can read it as a "go ahead".

-- 
:) Christoph Reck

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - why not storearguments in local context ?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
On 3/27/02 8:21 AM, "Claude Brisson" <cl...@savoirweb.com> wrote:

> Thank you for your responses, Christopher & Geir.
> 
> I get the picture : the side effects I pointed out are the problematic side of
> a volontary implementation choice... (and all
> implementation choices are by definition always somewhere between a bug and a
> feature... ;-) ).
> 
>> 
>> Why not make context.localscope true?  What's the problem there?
>> 
> 
> - it's a global parameter change for a local problematic
> - all needed global stuff must be passed as arguments to macros

That shouldn't be true.

> - and what if, after all, I want the global context to be changed sometimes ?

Indeed.
 
> In fact, to summerize, we have no call stack (hence, no recursion stack)
> inside macros when localscope is false :
> reference resolution is done in the flat world of inlining, and
> retrospectively It looks quite homogenic to do so.

It's just a template language... :)

 
> Chirstopher's #local directive proposal looks quite right to me, and I
> volunteers to write it.
> But (supposing my code is ok), will it be incorporated as a new functionnality
> ? A patch has a quite short lifetime...
> Time for what, now, votes, or validation, or something ?
> 

I posted a response to christoph, but I wrote a version yesterday in a J1
tech session that, shall we say, didn't hold my attention...

I also found a little thing to change in the reference code, so you will
need to get a snapshot from CVS for it to work....

-- 
Geir Magnusson Jr.                                      geirm@optonline.net
System and Software Consulting
"Whoever would overthrow the liberty of a nation must begin by subduing the
freeness of speech." - Benjamin Franklin



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: macro arguments may change implicitely - why not storearguments in local context ?

Posted by Claude Brisson <cl...@savoirweb.com>.
Thank you for your responses, Christopher & Geir.

I get the picture : the side effects I pointed out are the problematic side of a volontary implementation choice... (and all
implementation choices are by definition always somewhere between a bug and a feature... ;-) ).

>
> Why not make context.localscope true?  What's the problem there?
>

 - it's a global parameter change for a local problematic
 - all needed global stuff must be passed as arguments to macros
 - and what if, after all, I want the global context to be changed sometimes ?

In fact, to summerize, we have no call stack (hence, no recursion stack) inside macros when localscope is false :
reference resolution is done in the flat world of inlining, and retrospectively It looks quite homogenic to do so.

Chirstopher's #local directive proposal looks quite right to me, and I volunteers to write it.
But (supposing my code is ok), will it be incorporated as a new functionnality ? A patch has a quite short lifetime...
Time for what, now, votes, or validation, or something ?

Thanx,

CloD



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>