Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Velocity Wiki" for 
change notification.

The "MacroEvaluationStrategy" page has been changed by SergiuDumitriu.
The comment on this change is: Initial version.
http://wiki.apache.org/velocity/MacroEvaluationStrategy

--------------------------------------------------

New page:
>From [[http://en.wikipedia.org/wiki/Evaluation_strategy|Wikipedia]]: In 
>computer science, an evaluation strategy is a set of (usually deterministic) 
>rules for evaluating expressions in a programming language. An evaluation 
>strategy defines when and in what order the arguments to a function are 
>evaluated, when they are substituted into the function, and what form that 
>substitution takes.

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.

In non-strict mode, Velocity macros work mostly as call-by-macro expansion 
internally, with call-by-sharing external effects, but affected by automatic 
assignment of global variables when formal parameters can't be assigned (using 
a non-lvalue argument as an lvalue). We could call this ''relaxed call by lazy 
sharing''. This means that:

 * '''Lazy evaluation''': actual parameters are not computed (reduced in 
lambda-calculus semantics) until they are actually used. For example, calling a 
macro with `#someMacro($aList.add('a value'))` will not execute the `add` call 
until the formal parameter of the macro is actually used, which means that the 
call might not get executed at all.
 * '''Call by macro expansion:''' the actual parameters are computed each time 
the formal parameter is used. This means that in the previous example, the 
`add` call will be executed each time the formal parameter is used, possibly 
adding the value to the list several times. ''This is the behavior of C 
macros.''
 * '''Call by sharing:''' the method arguments are not passed by reference, 
meaning that trying to assign a new value to a formal parameter will not change 
the value of the actual parameter after the macro terminates execution, but 
changing the internal data of a passed object will work. ''This is the behavior 
of Java method calling.'' '''Warning: the call-by-sharing behavior is new in 
Velocity 1.7. The previous behavior was closer to true call-by-macro 
expansion.'''
 * '''Relaxed''', or '''error-free non-lvalue assignment:''' trying to assign a 
value to a formal parameter which is bound to a non-lvalue, for example calling 
`#someMacro('a')` where `someMacro` tries to assign a new value to that 
parameter, will create a new variable which shadows the formal parameter. 
''This is closer to the behavior of C method calling, but holds true only when 
assigning values to non-lvalue arguments.''

== Examples ==

=== Call by sharing example (new behavior introduced 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


=== 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.


=== 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. Since `$a` 
is not an lvalue, a call-by-name behavior would result in an exception.


=== 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'}) -> Before: old value, After: 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


=== 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.

== Future ==

The current (1.7 beta 1) behavior is complex, non-uniform and surprising in 
several (not so common) cases, and might be revisited for 2.0. The most likely 
candidate is a true ''call by sharing'' behavior, familiar to Java developers.

Reply via email to