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
>>
>>
>>
>> ![](https://link.nylas.com/open/5xm8m568zhx9qyloglsg31huz/local-
>> 178b2039-1688?r=ZGV2QGZyZWVtYXJrZXIuaW5jdWJhdG9yLmFwYWNoZS5vcmc=)
>>
>>
>> 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
>

Reply via email to