Any progress in this? I think I will give it a try in the coming days otherwise.
Sunday, November 18, 2018, 10:31:29 PM, Daniel Dekany wrote: > See my answers inline... > > Sunday, November 18, 2018, 8:44:40 PM, Christoph Rüger wrote: > >> Thanks Daniel for your feedback. See my answers below >> >> Am So., 11. Nov. 2018 um 19:14 Uhr schrieb Daniel Dekany <[email protected] >>>: >> >>> Sunday, November 11, 2018, 11:40:50 AM, Christoph Rüger wrote: >>> >>> > Am So., 11. Nov. 2018 um 09:25 Uhr schrieb Daniel Dekany < >>> [email protected] >>> >>: >>> > >>> >> Saturday, November 10, 2018, 3:08:14 PM, Denis Bredelet wrote: >>> >> >>> >> > Hi, >>> >> > >>> >> > Le 9 novembre 2018 à 22:36, Christoph Rüger <[email protected]> a >>> >> écrit : >>> >> > >>> >> > Am Fr., 9. Nov. 2018 um 22:55 Uhr schrieb Daniel Dekany < >>> >> [email protected] >>> >> > : >>> >> > >>> >> > It's certainly tricky, but as far as I see possible (but then, who >>> >> > >>> >> > knows what will one find when actually working on it). It's also a >>> >> > feature missing a lot. It's especially missing for #list (I know that >>> >> > you need it for something else), because if you filter the items >>> >> > inside #list with #if-s, then #sep, ?hasNext, etc. will not be usable. >>> >> > >>> >> > Let me say that I disagree here. >>> >> > >>> >> > I do not think that closures are required for FreeMarker, nor that >>> they >>> >> are a good idea. >>> >> > >>> >> > If we add new features to the FreeMarker *tempate engine* I would >>> >> > rather we focus on multi-part macro body rather than an advanced >>> >> language feature like closures. >>> >> > >>> >> > You can add ?filter and ?map if you want, a simple expression as >>> >> parameter should be enough. >>> >> >>> >> Yes, as I said, we certainly start with only allowing lambdas in >>> >> ?filter/?map, also certainly in ?contains. >>> >> >>> > Would be enough in my opinion and very useful. >>> > >>> > Is it possiblefor you to give some pointers to the code on how this could >>> > be implemented? I would maybe like to wrap my head around this a little >>> bit. >>> >>> Please feel yourself encouraged! (: >>> >>> > I started looking at seq_containsBI ( >>> > >>> https://github.com/apache/freemarker/blob/a03a1473b65d9819674b285a0538fed824f37478/src/main/java/freemarker/core/BuiltInsForSequences.java#L291 >>> ) >>> > and >>> > and reverseBI ( >>> > >>> https://github.com/apache/freemarker/blob/a03a1473b65d9819674b285a0538fed824f37478/src/main/java/freemarker/core/BuiltInsForSequences.java#L264 >>> ) >>> > just to find something related (seq_containsBI checks something) and >>> > reverseBI returns a new sequence. >>> > What I haven't found is a function which takes an Expression as a >>> > parameter. >>> > Is there something similar already or would that be a new thing? >>> >>> It's a new thing in that it will be part of the expression syntax >>> (even if for now we will only allow lambdas as the parameters of a few >>> built-ins, so that we can get away without closures). So it's a new >>> Expression subclass, and has to be part of the parser (ftl.jj) as >>> well. >> >> Hmm, that parser stuff is new for me, it'll take me some time to get into >> it. > > So it's a JavaCC lexer+parser. With a few twists... sorry, but this > code has history... :) > >>> As of lazy evaluation of parameters expressions, that's already >>> done in the built-ins in BuiltInsWithParseTimeParameters, and you will >>> see it's trivial to do, but the situation there is much simpler. >>> >> >> >>> >>> In principle, a LambdaExpression should evaluate to a >>> TemplateMethodModelEx, and then you pass that TemplateMethodModelEx to >>> the called built-in or whatever it is. But with the approach of >>> BuiltInsWithParseTimeParameters we can certainly even skip that, and >>> just bind to the LambdaExpression directly, add a LocalContext that >>> contains the lambda arguments, end evaluate the LambdaExpression right >>> there, in the built-in implementation. Or at least at a very quick >>> glance I think so. >>> >> Not sure I can follow completely but that hint with* >> BuiltInsWithParseTimeParameters* got me started, but at the moment I'm >> stuck as I need to get more familiar with the internals of Freemarker. I am >> also not sure I am on the same page regarding the syntax we are aiming for >> and why I would need to extend the parser when there is something like >> BuiltInsWithParseTimeParameters.... > > I assumed that the syntax will be similar to the Java lambda syntax > (see below why), and that's of course needs lexer/parser changes. > >> Here is an example what I have in mind: >> I started with the ?filter() builtin. I had a syntax like this in mind: >> >> *Example 1: ["a","b","c"]?filter(element, element == "c")* >> *Example 2: ["a","b","c"]?filter(element, element == someOtherVariable, >> someOtherVariable="c")* > > Looking at the above one believe that the value of `element` is passed > in as first argument, and the the value of `element == "c"` as the > second, but that's not the case. It's much better if it's visible that > you got some kind of anonymous function definition there without > knowing about the "filter" built-in. > > Java already has a syntax for expressing this kind of thing, so it > would be better to use that familiar syntax. As far as I know it > doesn't conflict with ours (it kind of it does, but not fatally). > > Also, even if for now we only allow this in said built-ins, we > shouldn't exclude the possibility of making this kind of expression > accessible elsewhere as well. Then, on many places the parsed won't > know what kind of value is expected (consider passing a lambda to an > user defined directive for example), so a syntax like above won't > work. > >> Not sure if that's what you have in mind too, but to me it made sense with >> regards to BuiltInsWithParseTimeParameters and I could start without >> touching parser stuff. > > BuiltInsWithParseTimeParameters doesn't affect the syntax (only a > bit...), as a call to a such built-in looks like a call to any other > built-in. > >> *1st argument 'element'* would just be the iterator variable similar to >> <#list ["a","b","c"] as *element*> >> 2nd argument is the filter lambda expression... aka our filter condition >> 3rd+n argument are optional parameters in case used in the lambda expression > > #list is special, similarly as `for ... in ...` is in most languages. > It's not lambda-ish either; you can't write `as element/2` or such. > >> So at first I was looking how <#list> works and found IteratorBlock, and >> though I could reuse it somehow. >> >> Here is some simple pseudo code I played around for the for Example 1: >> >> static class filter_BI extends BuiltInWithParseTimeParameters { >> >> >> >> TemplateModel _eval(Environment env) throws TemplateException { >> >> // sequence >> >> TemplateModel targetValue = target.evalToNonMissing(env); >> >> >> >> List parameters = this.parameters; >> >> >> >> Expression iteratorAlias = (Expression) parameters.get(0); >> >> Expression conditionExpression = (Expression) parameters.get(1); >> >> >> >> TemplateSequenceModel seq = (TemplateSequenceModel) target.eval >> (env); >> >> for (int i = 0; i < seq.size(); i++) { >> >> TemplateModel cur = seq.get(i); >> >> >> // this is where I am stuck at the moment >> >> // I basically want to evaluate conditionExpression >> where iteratorAlias >> is basically what I passed as 'element' >> >> // I am not sure if or how LocalContext could come into play >> here >> >> // basically for each iteration I would assign the current >> loop element to a context variable with the name 'element' >> >> // and then evaluate conditionExpression with that context. >> >> // if conditionExpression is "true" then I would populate >> add the current sequence element 'cur' >> >> // to a new result-List.... and return that.... something >> >> // I wanted to reuse IteratorBlock here somehow, but didn't >> get it to work yet. >> >> // maybe this is a stupid idea, or we just need something >> similar >> >> >> >> } >> >> } >> >> >> >> Ok so far for my pseudo code.... Maybe you could give some more pointers >> based on that... in case this makes any sense ... > > For LocalContext, Environment has a pushLocalContext method. See the > calls to it, like in visit(TemplateElement[], TemplateDirectiveModel, > Map, List). > > To avoid running into more new things at once than necessary, perhaps > you should start with seq?seq_contains(lambda). The end result should > be like: > > users?contains(u -> u.paying) > >>> Another similarity to BuiltInsWithParseTimeParameters is that we won't >>> allow separating the `?someBI` and `(arg)`. Like, with the example of >>> `cond?then(1, 2)`, you aren't allowed to do this: >>> >>> <#assign t=cond?then> >>> ${t(1, 2)} >>> >>> That maybe looks natural, but most other built-ins allow that. Of >>> course we can't allow that for ?filter etc., because then we need >>> closures again. >>> >>> >> Multi-part macro body is also planned. Means, I know it definitely >>> >> should be added, but who knows when that's done... I mean, it's like >>> >> that for what, a decade? (: It's not even decided what it exactly >>> >> does, as there are many ways of approaching this. (I have my own idea >>> >> about what the right compromise would be, but others has other >>> >> ideas...) >>> >> >>> >> Filtering lists bothers me because the template language should be >>> >> (and somewhat indeed is) specialized on listing things on fancy ways >>> >> that used to come up when generating document-like output. (If it >>> >> doesn't do things like that, you might as well use a general purpose >>> >> language.) Thus, that filter is unsolved (filtering with #if is >>> >> verbose and spoils #sep etc.) bothers me a lot. >>> >> >>> >> BTW, ?filter and ?map is also especially handy in our case as >>> >> FreeMarker doesn't support building new sequences (sequences are >>> >> immutable). Although it has sequence concatenation with `+`, it's not >>> >> good for building a sequence one by one, unless the sequence will be >>> >> quite short. >>> >> >>> > Good point. >>> > >>> > >>> >> >>> >> > Cheers, >>> >> > -- Denis. >>> >> >>> >> -- >>> >> Thanks, >>> >> Daniel Dekany >>> >> >>> >> >>> > >>> >>> -- >>> Thanks, >>> Daniel Dekany >>> >>> >> Thanks >> Christoph >> > -- Thanks, Daniel Dekany
