On Sat, Feb 25, 2017 at 4:16 AM, Daniel Dekany <[email protected]> wrote: > Saturday, February 25, 2017, 7:59:55 AM, David E Jones wrote: > >> These all sound like good changes, and clarify what is really going on. > > (Basically, I'm decreasing code size and complexity before the more > interesting changes are started.) > >> Removing object wrapping IMO is more important than removing the >> *Template*Model* interfaces. > > (The two come hand in hand really. If we had no TemplateModel-s, we > had no ObjectWrappers either.) > >> These interfaces don't have the same runtime and >> GC overhead as object wrapping and they can be nice for type checking to make >> sure the operation in the template can actually be done on the object. Still, >> with those interfaces it would be nice to use more standard ones where they >> exist (Java collection interfaces for example). >> >> On the object wrapping topic there might be some useful things to learn from >> Groovy, though as FTL doesn't compile to a class not all of the same applies. >> The biggest performance overhead in Groovy is all the type conversions that >> it >> does to be a type optional scripting language. I've done a lot of work on >> optimizing Groovy code since I made a decision years ago to write a big >> framework mostly in Groovy. >> >> >> >> I don't know that this is directly applicable to the discussion, but here is >> an article I wrote last year on some stuff I learned about Groovy >> optimization, and it may be that we can avoid some of these issues: >> >> >> >> https://dzone.com/articles/how-to-make-groovy-as-fast-as-java >> >> >> >> Even Groovy has a sort of object wrapper, ie its meta class. It's one thing >> that would be great to disable when you're not using the Groovy features that >> rely on it... maybe someday I'll even look into this in Groovy itself or open >> the discussion in that community (for now I'm up to my neck in work :) ). > > Luckily, I don't think this kind of performance is priority for a > template engine like FreeMarker, or at least that it's a high enough > priority to trump easy extendibility and such. That's because it's not > a programming language. Its a kind of domain specific language > basically. For something that generates output, hopefully you don't > have lines that are executed for millions and millions of times, > unless you also generate heck of a lot of output, which has to be > stored or sent, which will then (hopefully...) dominate your > performance anyway. > > Also, for us, a key difficulty is that in general we can't have > assumptions about the data-model content. It's outside the template. I > just don't know that in ${user.name}, what `user` will be. Sometimes > though, the data-model is a bean, so if that's declared inside the > template, then you have better chances. But... what if User has no > fullName property, but some User subclass has, and you write > `${user.fullName!'unknown'}`? Should now I also require the user to do > an explicit type cast? Anyway, one thing is sure, templates must be > able to operate in non-static mode. Adding an alternative static mode > is way out of reach (given the resources we can burn). And without > that... our performance options are rather limited. Well, we can pull > some tricks, like with indy, even if you don't know what the type of > `user` will be, you can during execution learn that it's in practice > always an User, and then you can link User.getName() as if it was a > "static" invocation (with a guard condition so if it's not an User > after all, you can take the slow route). > >> A good step in the right direction might be to start with the use of standard >> Java interfaces. For example when you do a #list it could be required that >> the >> object you are iterating over implements either the List or Iterator >> interfaces (perhaps with optimization to detect RandomAccess lists like >> ArrayList to avoid creating an Iterator). >> This doesn't get us all the way down >> the road, but is a start. > > What I consider instead of ObjectWrapper+TemplateModel-s is the > following. We don't wrap values into anything. Instead we will have an > interface called MOPImplementation (where MOP stands for Meta Object > Protocol), which declares methods for each *operations* that FTL is > interested in, such as `int getSize(env, v)`, `boolean isEmpty(v, > env)`, `Iterator<Object> getListItemIterator(v, env)`, `Object > getParent(v, env)` (for XML DOM-s and such), etc. So if you, for > example, need to #list a value, you get the MOPImplementation instance > based on the class of the value (it's the class of the "raw" value, > not a TemplateMode), and then call getListItemIterator on it, which > either gives you the Iterator or throws an > TemplateOperationNotSupportedException. A key idea behind this is that > you do not have a MOPImplementation per value instance. Like if you > have two List-s, they all will use the same shared MOPImplementation > instance (something like > o.a.f.core.mop.impl.ListMOPImplementation.INSTANCE) to access those > lists. The MOPImplementation isn't bound to a value, it's only bound > to the class of the value (i.e. you have different MOPImplementation > instance for List-s and another for arrays, another for DOM node sets, > etc.). In comparison, with ObjectWrapper-s you have to create at least > as many TemplateSequenceModel instances as many distinct List > instances you will touch, because TemplateModel-s are bound to a > certain value instance. (It's "at least" as many, because if you > access the same List instance for the second time, you often has > re-create the TemplateModel, because caching the TemplateModel-s > doesn't worth it usually. If you assign the list to an FTL variable, > and then access the variable for multiple times, then no such > re-wrapping will happen of course, because you store the TemplateModel > in the variable, not the List directly.) > > So, as you see, it's practically as flexible as ObjectWrapper-s. We > don't hard-wire what's listable, etc., because it all depends on the > set of MOPInterface classes added to the Configuration. So you can > still make classes that we have never seen to be listable, or change > the standard behavior (without changing the source code), etc. > > But, I don't think it's a huge win in performance... it's hopefully > some though: > > - You will generate less garbage to be GC-ed for sure, and that's good. > > - If you just pass through values the template, like in > `obj.m(theValuePassedThrough)`, you don't have to do anything extra > with it. (Currently, you have to wrap `theValuePassedThrough`, only > to unwrap later... ouch!) > > - But... if you assign a value to an FTL variable, it won't keep the result > of the "introspection" anymore (because you just store the original > object, not a TemplateModel that already knows all about it), so > each time you try to do something with it, the MOPImplementation > has to be chosen based on the class of the value. There are tricks > to mitigate this effect, such as side-storing MOPImplementation next > to the variable, but it brings code complexity, and some constant > overhead. > > - Using indy might becomes feasible, as now what to do only depends on > the class of the value, not on the value itself. > > But my real agenda with this is to make the code base less daunting > (because, we need attract contributors, not only users... this is > marked like that), and to make the API more user friendly. > TemplateModel-s, if you have them, are all over the place. For > example, when a user wants to implement a directive in Java, he gets > TemplateModel-s. The first thing they want to do is, quite invariable > I pressume, unwrapping them to the classes they know. But because of > the freedom ObjectWrapper-s and TemplateModel-s have, it's not even > guaranteed that that's possible. Basically, I don't want FTL to have > its own type system (as it has with TemplateModel-s). I just want it > to define a set operations (listing something, getting the size of > something, etc.) instead. That's much easier to grasp. And if you want > your Java code to behave like FTL, you can get a MOPImplementation.
+1 on considering MOP as an alternative. Sounds interesting and more easily understandable and usable to me. Could you give a reference link to indy? I'm curious about how it can be used and something specific like how it handles a dynamic proxy object for instance. Regards, Woonsan > >> -David >> >> >> >>  >> >> >> On Feb 24 2017, at 9:18 pm, Daniel Dekany <[email protected]> wrote: >> >>> In FM3, I propose removing some legacy complexity: >> >>> >> >>> \- Remove BeansWrapper, which is the super class of >> DefaultObjectWrapper, and add its functionality directly to >> DeafultObjectWrapper. That we have this class hierarchy has purely >> historical reasons. (Even if FM2, using pure BeansWrapper is >> deprecated, and causes lot of pain and confusion because it's still >> possible.) >> >>> >> >>> \- Remove the following BeansWrapper/DefaultObjectWrapper >> settings, because they just exist in FM2 so that you can chose >> between FM 2.3.0 compatibility and a better functionality: >> >>> >> >>> - BeansWrapper.simpleMapWrapper (DefaultObjectWrapper ignores it) >> - DefaultObjectWrapper.forceLegacyNonListCollections (will be in >> effect false) >> - DefaultObjectWrapper.iterableSupport (will be in effect true) >>> >> >>> PS I know that there's an idea of removing >> ObjectWrapper-s/TemplateModel-s altogether, and using a MOP instead, >> but that's a fairly brutal change and who knows if it will be >> affordable. So while I try not to invest much into >> ObjectWrapper-s/TemplateModel-s until we figure that out, I do at >> least want to remove stuff that we won't need for sure. And also, baby >> steps, easy things first... keep FM3 working all the time. >> >>> >> >>> \-- >> Thanks, >> Daniel Dekany >> > > -- > Thanks, > Daniel Dekany >
