On 05/03/2010 04:45 PM, Nathan Bubna wrote:
And the moral of the story is that VTL is that getting creative with
macro args is only for the brave...  simplicity is a good goal.

(more response inline)

On Mon, May 3, 2010 at 3:25 AM, Sergiu Dumitriu<[email protected]>  wrote:
Hi devs,

Looking at the various standard evaluation strategies (see
http://www.knowledgerush.com/kr/encyclopedia/Call-by-something/ and
http://en.wikipedia.org/wiki/Evaluation_strategy ), none of them applies to
Velocity. It's a mix between call by macro expansion, call by sharing, call
by value and other behaviors.


Call by sharing example (new in 1.7b1, and shortly only in 1.6.1)
#macro(callBySharing $x $map)
  #set($x = 'a')
  $map.put('x', 'a')
#end
#set($y = 'y')
#set($map = {})
#callBySharing($y $map)
$y ->  'y' (but is 'a' in 1.6.2, 1.6.0 and before)
$map.x ->  'a'
Java-like behavior.
See https://issues.apache.org/jira/browse/VELOCITY-681

Yeah, i think we've got this right now.  Global context, no magic
proxying.  Simple, unsurprising.

Call by name/macro expansion example (and call by need counter-example)
#macro(callByMacro1 $p)
  not using
#end
#macro(callByMacro2 $p)
  using: $p
  using again: $p
#end
#set($x = [])
#callByMacro1($x.add('t'))
$x ->  [], the add call was not executed
#callByMacro2($x.add('t'))
$x ->  [t,t], the add call was executed twice
This is a classic call by name example.

I've never been a big fan of this.  I think my very first email to the
list so many eons ago was a complaint about it. :)  It is surprising,
and i just don't see much benefit or any use-case that makes this
necessary.  I wouldn't mind seeing this change in 2.0...

Call by value(?) example (and call by name or expansion counter-example)
#macro(callByValueSwap $a $b)
  $a $b becomes ##
  #set($tmp = $a)
  #set($a = $b)
  #set($b = $tmp)
  $a $b
#end
#callByValueSwap('a', 'b') ->
a b becomes b a
In a true call-by-name (or macro-expansion) implementation, $a would always
be 'a'. What actually happens is that #set($a = $b) creates the global
variable $a which shadows the formal parameter. This is a bit strange, since
formal parameters should never be shadowed by external parameters.

I don't think they do unless the user has indicated they wish to
change that formal arg, in which case, this is less surprising.  I
don't care what true call-by-name says.  If the user wants to change
it, we should let them.

Call by macro expansion example (and call by value or sharing
counter-example)
#macro(changeMap $map)
  Before: $map.someKey
  #set($map.someKey = 'new value')
  After: $map.someKey
#end
#changeMap({'someKey' : 'old value'}) ->  old value, then again old value
If this was true call-by-sharing (or call-by-reference), then $map would be
a pointer to a real map which would be changed by the first set. See
https://issues.apache.org/jira/browse/VELOCITY-684

Yeah, i still say what i said in the issue comments.  We should let
the user change it if that's clearly what they want.  I see no point
to strictly following call-by-name here where it is so dramatically
counter-intuitive.

Call by macro expansion example (exposes name capture, call by name
counter-example)
#macro(nameCaptureSwap $a $b)
  $a $b becomes ##
  #set($tmp = $a)
  #set($a = $b)
  #set($b = $tmp)
  $a $b
#end
#set($x = 'a')
#set($tmp = 'b')
#nameCaptureSwap($x $tmp) ->
a b becomes a a
This is the classic name capture example, which only happens with call by
macro expansion.


Mixing different types of actual and formal parameters will expose mixed
behaviors.


In conclusion, Velocity macros work mostly as call-by-macro expansion
internally, with call-by-sharing external behavior, but affected by
automatic assignment of global variables when local variables can't be
assigned (using a non-lvalue as an lvalue). I haven't tested strict mode, so
I don't know what evaluation strategy is used in that case.


So, the question would be, should Velocity choose and stick to one of the
classic evaluation strategies? Should the current behavior be kept as-is? In
the latter case, it should be well documented to avoid surprises. Well, it
won't actually avoid surprises, but at least there will be a piece of
documentation to point to.

i, of course, think VELOCITY-684 should be resolved as i suggested in
the 1.x timeframe, but i'm unlikely to get to it myself and no one
else is stepping up to do that.   in 2.x, i think we should seriously
consider dropping our modified call-by-name behavior, unless someone
can give a compelling reason to keep it.

+1 for 100% compliant call by sharing (java behavior). At least we'll know what to expect.

Still, the "macro" name suggests a call-by-macro behavior, should it be renamed to something else? Leave #macro as it is (for backwards compatibility), deprecate it, add a new #function or something?

documentation is great either way.  Sergiu, perhaps you would be so
kind as to adapt this email for the wiki?  It does a great job of
covering the corner cases and pointing people to the relevant issues
where they can advocate change.

Done at http://wiki.apache.org/velocity/MacroEvaluationStrategy , but I have no idea where to link it from.

--
Sergiu Dumitriu
http://purl.org/net/sergiu/

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to