Daniel Fagerstrom wrote:<snip/>BURGHARD Éric wrote:
Variable nature and scope (kind of non-mutable that you can overwrite ?),
but someone could tell me this a feature :-). Perhaps i've miss something,
but look what i need to do to retrieve a value incremented inside a forEach
loop:
<jx:set var="globalvars" value="${java.util.HashMap(5)}"/>
<jx:set var="dummy" value="${globalvars.put('a_count', 0)}"/>
...
<jx:set var="a_count" value="${globalvars.a_count}"/>
<jx:forEach begin="1" end="3" varStatus="i">
...
<jx:if test="#{$node/@path != ''}">
...
<jx:set var="a_count" value="${a_count+1}"/>
<jx:set var="dummy" value="${globalvars.put('a_count', a_count)}"/>
</jx:if>
</jx:forEach>
<jx:out>this is my count: ${globalvars.a_count}</jx:out>
It would be easiest (and efficient) to do <jx:set var="a_count" value="0"/> <jx:forEach begin="1" end="3" varStatus="i"> ... <jx:if test="#{$node/@path != ''}"> ... <jx:set var="a_count" value="${a_count+1}"/> </jx:forEach>
<jx:out>this is my count: ${a_count}</jx:out>
the fact that "set" make always a "new define" is quite annoying at first
glance (functional programming ?).
The expression context is implemented as a stack of maps and the put instruction on the context is a put on the top map. ForEach is implemented in such way that the loop is performed in a local map that is pushed on stack before the loop and poped afterwards. The set instruction will do a put on the top map and will disapear afterwards because of that.
Ok, that explains the behaviour, the question is what we should do about the, admitingly, weird behaviour.
One possibilty would be to decide that we want JXTG to be more like a functional language. In that case we should deprecate jx:set and introduce a jx:let instead that just gives a local name for an expression and that gives an exception if you try to set the "variable" to a new value. I think I would prefer this behaviour as I would prefer having a template language without side effects.
This proposal does not solve the use case provided.
Could possibly be done with:
<jx:forEach items="[EMAIL PROTECTED]"> ...
But it wasn't clear to me what the use case does.
I also had several cases where I wanted the numbering to be consistent across several forEach calls and also had to implement hacks.
Can possibly be done by adding count([EMAIL PROTECTED]) to the counter.
I'm certain that you can find cases that not are solved with this. The question is how much of a programming language we want our template language to be, and my opinion is that we should contain as little "programming" constructs as possible while still being usable as a view. We must of course discuss where we should draw the line. But I would go for less. WDOT?
Another possibility is to let set asign the value to the first variable binding with the same name that it finds when the stack is searched, instead of creating a new binding at the top of the stack if the name doesn't exist on the top of the stack. If we go for this we need two constructions one that declare and possibly gives an intial value to a variable in the current context and one that binds the uppermost occurance of the name, a jx:declare and a jx:set e.g.I prefer the second solution. The only thing to make the use case work is the ability to set the variable without implicit declaration.
Also we would need to change the API and behaviour of ExpressionContext so that we both have a declare and a set method. This should probably be done anyway.
WDYT?
/Daniel
One more question: should jx:set automatically declare if there is no previous declaration? I think yes.
When we reach an agreement I can implement it as it looks quite straightforward.
Thinking a little bit more about it, we can't change the current behavoiur of jx:set in the indicated dirresction, it will break peoples templates and we don't want that.
I suggest that (given that we decide to go the "assignment" way, that we introduce two new constructions: jx:define (or declare, let, variable etc) and jx:assign. jx:define introduces a new variable in the current context and give it an initial value, it is an error to use jx:define for the same variable two times in the same context. jx:asign assigns the value of a previous declared variable and it is an error to assign an undefined variable. I prefer to not have any implicit declaration of undefined variable from jx:assign.
From an implementation POV the ExpressionContext must be extended with new methods for assignment and declaration of variables as the put instruction supports the current jx:set behaviour.
/Daniel