|
|
|
Yuh-Ruey Chen
|
On Fri, Sep 11, 2009 at 12:37 AM, Daniel Dekany <[hidden email]> wrote:
Okay, I've got my ObjectWrapper to extend from BeansWrapper, and added some better (IMO) enum handling: /** * Add special handling for class objects. */ @Override protected ModelFactory getModelFactory(@SuppressWarnings("unchecked") Class clazz) { if (clazz.equals(Class.class)) { return CLASS_MODEL_FACTORY; } return super.getModelFactory(clazz); } /** * Uses BeansWrapper.getEnumModels() for Enum classes, and StringModel for other * classes. */ protected static final ModelFactory CLASS_MODEL_FACTORY = new ModelFactory() { public TemplateModel create(Object object, freemarker.template.ObjectWrapper wrapper) { BeansWrapper beansWrapper = (BeansWrapper) wrapper; Class<?> clazz = (Class<?>) object; // If class is Enum, use beansWrapper.getEnumModels(). if (clazz.isEnum()) { try { return beansWrapper.getEnumModels().get(clazz.getName()); } catch (TemplateModelException e) { // This should never occur, since a TemplateModelException is only // thrown when the enum class cannot be found, but the enum class // obviously exists here. throw new IllegalStateException(e); } } // Otherwise, use the default StringModel. return new StringModel(object, beansWrapper); } }; I believe this code could work for Java 1.2+ if the generics and annotations are stripped out and the isEnum() is reflectively called. I did find another issue with BeansWrapper though: the seq_contains builtin doesn't work correctly for Sets. The seq_contains builtin first checks if the object is a TemplateSequenceModel and then uses indexing operations (get(int)), before checking if the object is a TemplateCollectionModel. Since Sets are wrapped in a CollectionModel, which implements both TemplateSequenceModel and TemplateCollectionModel, it's treated as a TemplateSequenceModel in seq_containsBI, and get(int) throws an exception for Sets. I have a very ugly and hacky fix for that. I created a class in pkg freemarker.core so it can access BuiltIn.builtins, which is used to replace the seq_contains builtin with the fixed version. The fixed version is basically a copy of the seq_containsBI with the TemplateSequenceModel and TemplateCollectionModel checking reversed. I suppose that's a testament to the need for an API revamp for builtins. By the way, I would have reported this bug to the SourceForge bug tracker, but it hardly looks used (practically no discussion on it, and plenty of spam). Regards, Yuh-Ruey Chen ------------------------------------------------------------------------------ Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Daniel Dekany
|
Sunday, September 13, 2009, 4:52:59 AM, YR Chen wrote:
> On Fri, Sep 11, 2009 at 12:37 AM, Daniel Dekany <[hidden email]> wrote: > Friday, September 11, 2009, 4:10:28 AM, YR Chen wrote: > >> Hmm, I'll give that a try. I did implement a couple extra things >> like wrapping an Enum class into a Hash. Since I believe Enums are a >> Java 5 feature, it might not be possible to add the FreeMarker code base. > > Enum support in FreeMarker: > http://freemarker.org/docs/pgui_misc_beanwrapper.html#jdk_15_enums > > -- > Best regards, > Daniel Dekany > > > Okay, I've got my ObjectWrapper to extend from BeansWrapper, and > added some better (IMO) enum handling: > > /** > * Add special handling for class objects. > */ > @Override > protected ModelFactory > getModelFactory(@SuppressWarnings("unchecked") Class clazz) { > if (clazz.equals(Class.class)) { > return CLASS_MODEL_FACTORY; > } > return super.getModelFactory(clazz); > } > > /** > * Uses BeansWrapper.getEnumModels() for Enum classes, and StringModel for other > * classes. > */ > protected static final ModelFactory CLASS_MODEL_FACTORY = new ModelFactory() { > public TemplateModel create(Object object, > freemarker.template.ObjectWrapper wrapper) { > BeansWrapper beansWrapper = (BeansWrapper) wrapper; > Class<?> clazz = (Class<?>) object; > // If class is Enum, use beansWrapper.getEnumModels(). > if (clazz.isEnum()) { > try { > return > beansWrapper.getEnumModels().get(clazz.getName()); > } catch (TemplateModelException e) { > // This should never occur, since a TemplateModelException is only > // thrown when the enum class cannot be found, but the enum class > // obviously exists here. > throw new IllegalStateException(e); > } > } > // Otherwise, use the default StringModel. > return new StringModel(object, beansWrapper); > } > }; > > I believe this code could work for Java 1.2+ if the generics and > annotations are stripped out and the isEnum() is reflectively called. (I left this part to Attila...) > I did find another issue with BeansWrapper though: the seq_contains > builtin doesn't work correctly for Sets. The seq_contains builtin > first checks if the object is a TemplateSequenceModel and then uses > indexing operations (get(int)), before checking if the object is a > TemplateCollectionModel. Since Sets are wrapped in a > CollectionModel, which implements both TemplateSequenceModel and > TemplateCollectionModel, it's treated as a TemplateSequenceModel in > seq_containsBI, and get(int) throws an exception for Sets. > > I have a very ugly and hacky fix for that. I created a class in pkg > freemarker.core so it can access BuiltIn.builtins, which is used to > replace the seq_contains builtin with the fixed version. The fixed > version is basically a copy of the seq_containsBI with the > TemplateSequenceModel and TemplateCollectionModel checking reversed. Unless someone will tell it's not backward-compatible, that fix will be in the next release. > I suppose that's a testament to the need for an API revamp for builtins. I'm not 100% sure what you mean, but it's deliberate that you can't add built-ins. I you could, we couldn't add new ones in a backward-compatible way. That's why they are syntactically separated from plain method calls. Overriding built-ins is also not fun, because they are part of the template language, quite much like operators are, and they are documented in the official manual, so it would be really confusing if some user customize them. > By the way, I would have reported this bug to the SourceForge bug > tracker, And you should have. > but it hardly looks used (practically no discussion on it, I don't know what kind of projects you worked in, but I think the activity there is quite much usual among OS project. But, maybe you only listed the entries with "open" status? > and plenty of spam). Plenty? Where? I have found (and deleted) two. > Regards, Yuh-Ruey Chen -- Best regards, Daniel Dekany ------------------------------------------------------------------------------ Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Yuh-Ruey Chen
|
Daniel Dekany wrote:
> Sunday, September 13, 2009, 4:52:59 AM, YR Chen wrote: > > I suppose that's a testament to the need for an API revamp for builtins. > > I'm not 100% sure what you mean, but it's deliberate that you can't > add built-ins. I you could, we couldn't add new ones in a > backward-compatible way. That's why they are syntactically separated > from plain method calls. Overriding built-ins is also not fun, because > they are part of the template language, quite much like operators are, > and they are documented in the official manual, so it would be really > confusing if some user customize them. > Well I have to disagree with that. If there's one thing that I don't like about the FreeMarker design - it's the builtins. Most of them simply provide equivalent functionality with methods already in the Java API, e.g. substring, contains, seq_contains. Whether they are "quite much like operators are" is moot, since in Java you can override most operators you want (in the generic sense of the term "operator", so I mean Java methods). I can understand the desire not to have builtins collide with the model, but that doesn't preclude them from being user-defined. After all, FreeMarker already has namespaces. Look at JavaScript for inspiration. It has plenty of builtins but they can be overridden. And since it's the user that's overriding them, he knows the exact semantics of it, so I don't see how it can be confusing at all. IMO, there are relatively few builtins that can be considered as core operators: eval, type builtins (int, float, etc.), interpret, and some others. In fact, I would have those particular builtins be actual core language operators rather than "builtins". The rest are hardly related to the core and should be fully customizable. > > but it hardly looks used (practically no discussion on it, > > I don't know what kind of projects you worked in, but I think the > activity there is quite much usual among OS project. But, maybe you > only listed the entries with "open" status? > > > and plenty of spam). > > Plenty? Where? I have found (and deleted) two. > Oops, I was actually listing all entries - including deleted ones. My bad :p Regards, Yuh-Ruey ------------------------------------------------------------------------------ Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Daniel Dekany
|
Monday, September 14, 2009, 7:35:53 AM, Yuh-Ruey Chen wrote:
> Daniel Dekany wrote: >> Sunday, September 13, 2009, 4:52:59 AM, YR Chen wrote: >> > I suppose that's a testament to the need for an API revamp for builtins. >> >> I'm not 100% sure what you mean, but it's deliberate that you can't >> add built-ins. I you could, we couldn't add new ones in a >> backward-compatible way. That's why they are syntactically separated >> from plain method calls. Overriding built-ins is also not fun, because >> they are part of the template language, quite much like operators are, >> and they are documented in the official manual, so it would be really >> confusing if some user customize them. >> > > Well I have to disagree with that. If there's one thing that I don't > like about the FreeMarker design - it's the builtins. Most of them > simply provide equivalent functionality with methods already in the > Java API, e.g. substring, contains, seq_contains. And for some there is no equivalent... now assuming the template author is a Java programmer who knows what is in the Java API-s and what is not, this wouldn't be automatically a problem. Only of course with the current language design we stuck due to the object wrapping. Something that is exposed to be, say, a string in the template language might not be a String on the Java-language-side. Surely we could still simulate that it has the java.lang.String methods, but it's not that rare that the same object had to expose the actual Java methods of the unwrapped object, or that the object is a custom TempateModel object implementation and hence it has methods that are not part of java.lang.String. And so on, so on... These can cause name clashes. So at the end you need some kind of trick to separate plain method calls from built-in method class, and it's using "?" instead of ".". > Whether they are "quite > much like operators are" is moot, since in Java you can override most > operators you want (in the generic sense of the term "operator", so I > mean Java methods). I can understand the desire not to have builtins > collide with the model, but that doesn't preclude them from being > user-defined. After all, FreeMarker already has namespaces. In my vaporware template language I actually don't have bulilt-ins (and not even built-in directives, you know, the # things), only methods. There is a set of so called core methods, that is part of the template language, but you can change it if you explicitly create a custom template language, with a new name and default extension, one that doesn't collide with the names and extensions of the standard template languages. (As far as you don't also customize syntax, it's not difficult to implement, it's just that you have to design the engine with multiple template languages in mind.) So it's not like I will protect FM built-ins to the last blood, but I also know it's not as a silly concept as it looks. I have suffered with this a lot, and it's not at all free to drop them. Thing is, you don't want clashes with the data-model (Java guys are paranoids :) ), and the data-model variables should be available without any prefix (like "$" or "d:"), because it's what you use the most in an MVC-ish template. So what remains for those poor top-level core methods (and core variables, like "locale", etc.), the equivalents of FM-s built-ins + # directives? Because I don't think it's very acceptable to write namespace prefixes or whatever prefixes before every single core method call either. (I won't tell my solution right now... let's see what idea others has.) FM actually solved this problem with "#" vs "@" for directives, and "." VS "?" for built-ins. This is not a that bad compromise. > Look at JavaScript for inspiration. > It has plenty of builtins but they can be overridden. (I'm actually not exactly known as a JavaScript fan... or, for that mater, neither as a big fan of the "let's make everything incredibly dynamic, while we don't feel like developing the static languages (or not ignoring the new ones), so we will have real reason to hate them and to point out how the dynamic ones are more productive" movement.) > And since it's the user that's overriding them, he knows the exact > semantics of it, so I don't see how it can be confusing at all. Who is "the user"? Because it's certainly not one guy, rather a few. Even if it's one guy, certainly it will be another one a few years later. Not to mention the tendency of people to forget things they did, especially if they participate in several projects over the years. So, what going to happen is that the new guy or a some who just has forgotten things will Google things, opens the FreeMarker Manual, see what built-ins are available and what they do, but... oops. (Then you can continue with supporting IDE-s and other text editors when you have a custom set of built-ins. It's possible if you expose some API-s that help the plugin authors, but, yet another place where customization can come back to you.) > IMO, there are relatively few builtins that can be considered as core > operators: eval, type builtins (int, float, etc.), interpret, and some > others. In fact, I would have those particular builtins be actual core > language operators rather than "builtins". The rest are hardly related > to the core and should be fully customizable. A design goal with a fat set of core "methods" (in FM they are the "built-ins:, and the # directives) is that the wherever the user writes a template, he can be sure they are there, and he can access them immediately. He doesn't even have to suffer with importing something like, I don't know, "fm://utils" and assign it to a prefix, which BTW can be quite annoying if you are working with many little independent template fragments (message templates, UI bits in a CMS, etc.). Also he doesn't need to wonder whether, like, HTML escaping is a core method or a utility method. But of course, splitting the out-of-the-box methods into core and utility groups has its advantages too. But this is about seeking compromises. I actually went back and forth between fat-core and thin-core + utils design, but currently I would go for fat-core. But that decision very much depends on what tricks you find out to avoid the earlier mentioned name clashes. >> > but it hardly looks used (practically no discussion on it, >> >> I don't know what kind of projects you worked in, but I think the >> activity there is quite much usual among OS project. But, maybe you >> only listed the entries with "open" status? >> >> > and plenty of spam). >> >> Plenty? Where? I have found (and deleted) two. >> > > Oops, I was actually listing all entries - including deleted ones. My bad :p > > Regards, Yuh-Ruey -- Best regards, Daniel Dekany ------------------------------------------------------------------------------ Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Yuh-Ruey Chen
|
Daniel Dekany wrote:
> Monday, September 14, 2009, 7:35:53 AM, Yuh-Ruey Chen wrote: > > > Daniel Dekany wrote: > >> Sunday, September 13, 2009, 4:52:59 AM, YR Chen wrote: > >> > I suppose that's a testament to the need for an API revamp for builtins. > >> > >> I'm not 100% sure what you mean, but it's deliberate that you can't > >> add built-ins. I you could, we couldn't add new ones in a > >> backward-compatible way. That's why they are syntactically separated > >> from plain method calls. Overriding built-ins is also not fun, because > >> they are part of the template language, quite much like operators are, > >> and they are documented in the official manual, so it would be really > >> confusing if some user customize them. > >> > > > > Well I have to disagree with that. If there's one thing that I don't > > like about the FreeMarker design - it's the builtins. Most of them > > simply provide equivalent functionality with methods already in the > > Java API, e.g. substring, contains, seq_contains. > > And for some there is no equivalent... now assuming the template > author is a Java programmer who knows what is in the Java API-s and > what is not, this wouldn't be automatically a problem. Only of course > with the current language design we stuck due to the object wrapping. > Something that is exposed to be, say, a string in the template > language might not be a String on the Java-language-side. Surely we > could still simulate that it has the java.lang.String methods, but > it's not that rare that the same object had to expose the actual Java > methods of the unwrapped object, or that the object is a custom > TempateModel object implementation and hence it has methods that are > not part of java.lang.String. And so on, so on... These can cause name > clashes. So at the end you need some kind of trick to separate plain > method calls from built-in method class, and it's using "?" instead of > ".". > > > Whether they are "quite > > much like operators are" is moot, since in Java you can override most > > operators you want (in the generic sense of the term "operator", so I > > mean Java methods). I can understand the desire not to have builtins > > collide with the model, but that doesn't preclude them from being > > user-defined. After all, FreeMarker already has namespaces. > > In my vaporware template language I actually don't have bulilt-ins > (and not even built-in directives, you know, the # things), only > methods. There is a set of so called core methods, that is part of the > template language, but you can change it if you explicitly create a > custom template language, with a new name and default extension, one > that doesn't collide with the names and extensions of the standard > template languages. (As far as you don't also customize syntax, it's > not difficult to implement, it's just that you have to design the > engine with multiple template languages in mind.) So it's not like I > will protect FM built-ins to the last blood, but I also know it's not > as a silly concept as it looks. I have suffered with this a lot, and > it's not at all free to drop them. Thing is, you don't want clashes > with the data-model (Java guys are paranoids :) ), and the data-model > variables should be available without any prefix (like "$" or "d:"), > because it's what you use the most in an MVC-ish template. So what > remains for those poor top-level core methods (and core variables, > like "locale", etc.), the equivalents of FM-s built-ins + # > directives? Because I don't think it's very acceptable to write > namespace prefixes or whatever prefixes before every single core > method call either. (I won't tell my solution right now... let's see > what idea others has.) FM actually solved this problem with "#" vs "@" > for directives, and "." VS "?" for built-ins. This is not a that bad > compromise. > I do like the idea of a minimal design that can be extended into custom template languages, so I very much like to see what you have in mind :) I'm actually not too bothered with the fact that there are builtins or that the syntax is a bit odd-looking. But I just don't think builtins functions should be treated specially from user-defined functions. Same with builtin directives vs. user-defined directives (more on this later). Builtin and user-defined functions should have a single unified syntax. Right now, builtin functions use the suffix "?" syntax and can't be treated as first-class objects (can't be passed around), while user-defined functions are actually in the data model and are first-class objects. Adding functions to the data model (or shared vars) always feels a bit clunky to me, because it does collide with the rest of the data model. So user-defined functions can either collide with the data model or the builtin functions, and I think going with the latter is a both a cleaner and safer design. Cleaner because the semantics and syntax of all functions are unified (hopefully leading to cleaner code as well), and safer because the data model is much more variable then the list of functions (e.g. if I have a function "foo", it's more likely that some data model with a "foo" to collide with it than there would be another user-defined function named "foo" to collide with it). Now to unifying user-defined and builtin directives. There are other syntactical differences besides the "#" vs. "@". Some like #assign and #if can't be done with user-defined directives. And unless we want a XML-like verboseness ala JSTL's choose/when, there's not a good way around it (though I'm trying to think of a way they could be user-defined yet not verbose). But what about #list and some other directives that use the "as" keyword? These directives use the "as" keyword in two ways: introducing a new local (loop) variable (if it requires an end tag), or introducing a variable to the template's scope (if it cannot have an end tag). Meanwhile user-defined directives must use the odd semicolon syntax to introduce new local variables, and cannot introduce template-scope variables. I think there is room for unification here. As to whether it's a good idea treat builtins and user-defined functions and directives the same, it should at the very least be configurable - see below (response to JavaScript). So here's a rough template language design brainstorming session: Directives: Both user-defined and builtins use [#] and share the same "namespace". User-defined directives cannot override builtins. [#foo arg1, arg2 as var1, var2]body[/#foo] Call directive foo with params arg1 and arg2, introducing new "loop" vars var1 and var2 to body. [#foo arg1, arg2 as arg3, arg4/] Call directive foo with params arg1 and arg2, introducing new vars var1 and var2 to the parent scope. Of course, if #foo requires a body, this should throw an exception somewhere. [#macro foo, arg1, arg2 as var1, var2] stuff [#set var1=blah] [#set var2=blah] stuff [/#macro] Replaces macro foo with a directive that uses params arg1 and arg2, and introduces vars var1 and var2. var1 and var2 have C-like "call by reference" semantics (as opposed to Java's "call by value" semantics), i.e. setting var1 actually sets the loop variable. [#macro foo, arg1=dv, ...args as var1, ...vars] [#list args as arg]stuff[/#list] [#list vars as var][#set var=blah][/#list] [/#macro] Example showcasing variadic parameters and default parameters. "vars" is a list of references, so that in [#list vars as var], "var" is a reference, so [#set var=...] works as you'd expect. Functions: Both builtin and user-defined functions share the exact same syntax. Whether builtins are overridable should be configurable (see below). There are two forms that the user can use: the familiar Java prefix style, or the FreeMarker suffix style. To try to keep the syntax as consistent as possible, I'm using "#" for template functions. So to the user, "#" means "template directive/function". ${#blah(x)} Equivalent to FTL expression: ${x?blah}. ${x#blah} Equivalent to FTL expression: ${x?blah}. [#function blah, arg1, arg2]body[/#function] Redefines blah function. ${#blah(arg1, arg2)} Equivalent to FTL expression: ${arg1?blah(arg2)} ${arg1#blah(arg2)} Equivalent to FTL expression: ${arg1?blah(arg2)} [#function blah, arg1=dv, ...args]body[/#function] Showcasing variadic parameters and default parameters. The template language will have very light "bean wrappers", so there's no need for builtins like "substring" or "contains". > > Look at JavaScript for inspiration. > > It has plenty of builtins but they can be overridden. > > (I'm actually not exactly known as a JavaScript fan... or, for that > mater, neither as a big fan of the "let's make everything incredibly > dynamic, while we don't feel like developing the static languages (or > not ignoring the new ones), so we will have real reason to hate them > and to point out how the dynamic ones are more productive" movement.) > Perhaps JavaScript is a bad example. It's too simplistic to say that something should be dynamic or not, when there are multiple ways something can be dynamic. For builtin overriding, I can imagine several "dynamic levels" that we can choose: 1) Allowing builtins to be overridden by anything (and even deleted ala setting it to null) within the template 2) Allowing builtins to be overridden by a function that matches the same interface of the builtin within the template 3) Same as (1) or (2) except template engine can be configured not to allow builtin overriding 4) Same as (1) except builtins can only be overridden during configuration (from Java) 5) Same as (2) except builtins can only be overridden during configuration (from Java) and the interface semantics can be more precise with the static type checking 6) Same as any of the above but allow only non-"core" builtins to be overwritten, where a "core" builtin would be eval, interpret, etc. 7) Cannot be overwritten, period - not even a Java API to do so (status quo) > > And since it's the user that's overriding them, he knows the exact > > semantics of it, so I don't see how it can be confusing at all. > > Who is "the user"? Because it's certainly not one guy, rather a few. > Even if it's one guy, certainly it will be another one a few years > later. Not to mention the tendency of people to forget things they > did, especially if they participate in several projects over the > years. So, what going to happen is that the new guy or a some who just > has forgotten things will Google things, opens the FreeMarker Manual, > see what built-ins are available and what they do, but... oops. (Then > you can continue with supporting IDE-s and other text editors when you > have a custom set of built-ins. It's possible if you expose some API-s > that help the plugin authors, but, yet another place where > customization can come back to you.) > That's a problem with code in general and hardly an argument for keeping builtins unconfigurable. You could easily mention in the docs that builtins can be redefined in user code (whether during configuration or within the template). And I don't find the IDE support argument very convincing. If builtins are configurable in Java, then we have javadocs and method signatures that can provide hints. If builtins can be overridden within the template, well that's just the nature of dynamic languages, although with very good type inferencing, editor support is somewhat possible (there are editors out there that provide decent type/param inferencing for certain dynamic languages). > > IMO, there are relatively few builtins that can be considered as core > > operators: eval, type builtins (int, float, etc.), interpret, and some > > others. In fact, I would have those particular builtins be actual core > > language operators rather than "builtins". The rest are hardly related > > to the core and should be fully customizable. > > A design goal with a fat set of core "methods" (in FM they are the > "built-ins:, and the # directives) is that the wherever the user > writes a template, he can be sure they are there, and he can access > them immediately. He doesn't even have to suffer with importing > something like, I don't know, "fm://utils" and assign it to a prefix, > which BTW can be quite annoying if you are working with many little > independent template fragments (message templates, UI bits in a CMS, > etc.). Also he doesn't need to wonder whether, like, HTML escaping is > a core method or a utility method. But of course, splitting the > out-of-the-box methods into core and utility groups has its advantages > too. But this is about seeking compromises. I actually went back and > forth between fat-core and thin-core + utils design, but currently I > would go for fat-core. But that decision very much depends on what > tricks you find out to avoid the earlier mentioned name clashes. > I'm fine with plenty of builtins. You can have a small set of core builtins and then libraries that provide other "builtins" that are autoimported into the same "namespace". Then allow the user to configure exactly what libraries are autoimported (with the default possibly being importing everything). In fact, I'd like the "standard library" to be much bigger. Regards, Yuh-Ruey ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Daniel Dekany
|
Tuesday, September 15, 2009, 11:37:15 AM, Yuh-Ruey Chen wrote:
[snip] > I do like the idea of a minimal design that can be extended into custom > template languages, so I very much like to see what you have in mind :) > > I'm actually not too bothered with the fact that there are builtins or > that the syntax is a bit odd-looking. It's not that odd-looking... consider "?" as ".", that has something above it. Both is a sentence-ending punctuation character. > But I just don't think builtins functions should be treated > specially from user-defined functions. Same with builtin directives > vs. user-defined directives (more on this later). Builtin and > user-defined functions should have a single unified syntax. Right > now, builtin functions use the suffix "?" syntax and can't be > treated as first-class objects (can't be passed around), while > user-defined functions are actually in the data model and are > first-class objects. I agree with that... but note that treating the built-in directives/functions/methods/whatever-you-call-them is not possible for all of them, because some of them has before-runtime effects. (Anyway, in such cases I planned to drop an exception if and when you try to call them. So you still can get the thing as a value, and, like, query it's name and namespace, but you can't call it.) > Adding functions to the data model (or shared vars) always feels a bit > clunky to me, because it does collide with the rest of the data model. Yes. But later ?new and #import was introduced to counter that tradition. (BTW, one of the big problems of FM is that the whole "FTL library" thing is not very streamlined... For example, you can't just drop a someoneelsesftllibrary.jar into the classpath of your app., and then #import it. Things like that...) > So user-defined functions can either collide with the data model or the > builtin functions, and I think going with the latter is a both a cleaner > and safer design. *If* that's the only two possibility, I agree. > Cleaner because the semantics and syntax of all functions are > unified (hopefully leading to cleaner code as well), and safer > because the data model is much more variable then the list of > functions (e.g. if I have a function "foo", it's more likely that > some data model with a "foo" to collide with it than there would be > another user-defined function named "foo" to collide with it). > > Now to unifying user-defined and builtin directives. There are other > syntactical differences besides the "#" vs. "@". Some like #assign and > #if can't be done with user-defined directives. And unless we want a > XML-like verboseness ala JSTL's choose/when, there's not a good way > around it (though I'm trying to think of a way they could be > user-defined yet not verbose). Yeah, that's the other tricky part of the unification... You will have to add some extra features to your template language, that you maybe wouldn't add otherwise. Anyway, I found that it wasn't a big problem in practice. Like, I wanted out-parameters feature anyway, and something like "set target=source" can be seen as an optional shorthand for an application of that. But I don't think it's wise to allow the usage of that particular shorthand for user-defined stuff (but it's really easy to allow it technically... actually, the extra work is disallowing it). And that wasn't the point anyway... the point is that *semantically* the "set x=1" is nothing special, it's really just "set 1; x". Hence, the "=" thing itself is no in the way if you want to call dynamically something that uses that. > But what about #list and some other directives that use the "as" > keyword? Back then I decided to get rid of "as", "in" and such, and just use the unified syntax (";"). Since the expression language clearly separate the in- and out-parameters (with ";"), it's obvious which one is the source, and which one declare the block-scope variable. Now there is a compromise here: "list foo as bar" is more telling than "list foo; bar". I to find something better instead of ";" but whenever I did it always spoiled something else that I found to be a bigger problem... so currently it's ";". So, back to the compromise, I found it's not a that big compromise eventually, if you assume the user will face a ";" when using some custom macros anyway. Then it's just better that there is only one syntax he has to learn: use ";", end of story. The other possibility is to allow declaring the usage of "as" or "in" in the formal parameter list of the macro definitions... only, depending on what purpose the macro has, you may want another keyword... which, you won't get, custom keywords. I don't think it's need to go into it why not... > These directives use the "as" keyword in two ways: introducing > a new local (loop) variable (if it requires an end tag), or introducing > a variable to the template's scope (if it cannot have an end tag). > Meanwhile user-defined directives must use the odd semicolon syntax to > introduce new local variables, and cannot introduce template-scope > variables. I think there is room for unification here. > > As to whether it's a good idea treat builtins and user-defined functions > and directives the same, it should at the very least be configurable - Hardly. To unify them, you need to touch some quite fundamental rules of the language. In FTL, directives meant to generating markup (hence they kind of look like HTML tags), while other expressions (like function calls) aren't. The importance of this shows when you apply #escape; it will not escape the output of directives. Also, in FreeMarker only directives can return the result progressively (i.e., they start to output it before it's complete). So, what I'm saying, that in the case of FreeMarker there is fundamental difference between the two. (I'm not saying there should be... as I said, in my planned solution there isn't. But of course, that has some painful consequences too...) BTW, the unification require things like blocks inside parameter lists... technically doable, but, well, it's a long story, but it encourages some bad practices, if you have the "multiple bodies" or "template-defined parameters" (see in ZipScript) or whatever we call it feature. > see below (response to JavaScript). > > > > So here's a rough template language design brainstorming session: > > Directives: > > Both user-defined and builtins use [#] and share the same "namespace". > User-defined directives cannot override builtins. > > [#foo arg1, arg2 as var1, var2]body[/#foo] > Call directive foo with params arg1 and arg2, introducing new "loop" > vars var1 and var2 to body. > > [#foo arg1, arg2 as arg3, arg4/] > Call directive foo with params arg1 and arg2, introducing new vars var1 > and var2 to the parent scope. Of course, if #foo requires a body, this > should throw an exception somewhere. > > [#macro foo, arg1, arg2 as var1, var2] > stuff > [#set var1=blah] > [#set var2=blah] > stuff > [/#macro] > Replaces macro foo with a directive that uses params arg1 and arg2, and > introduces vars var1 and var2. var1 and var2 have C-like "call by > reference" semantics (as opposed to Java's "call by value" semantics), > i.e. setting var1 actually sets the loop variable. Yip, that's what I did (on paper, I mean); by value VS by reference. > [#macro foo, arg1=dv, ...args as var1, ...vars] > [#list args as arg]stuff[/#list] > [#list vars as var][#set var=blah][/#list] > [/#macro] > Example showcasing variadic parameters and default parameters. "vars" is > a list of references, so that in [#list vars as var], "var" is a > reference, so [#set var=...] works as you'd expect. Yes. Actually, I also allowed named variadic parameters. Very very useful feature. (And something *similar* to that is painfully missing from most compiled languages. Like, this lack of true named parameters is why dependency injection via constructor arguments sucks.) > Functions: > > Both builtin and user-defined functions share the exact same syntax. > Whether builtins are overridable should be configurable (see below). (Configurable language rules... I would allow it only if you define a new template language... that could/should be made easy, but still, you have to give it another name.) > There are two forms that the user can use: the familiar Java prefix > style, or the FreeMarker suffix style. > > To try to keep the syntax as consistent as possible, I'm using "#" for > template functions. So to the user, "#" means "template directive/function". I didn't do that, among others because then you can just write f(x), you have to write #f(x). So I just used | for doing things like x|f. But, I'm not saying it's wrong... depends on everything else. (Because, at least for me, designing a good language is an endless running of circles... It's like, I pick a particular problem, find a nice solution for it, and go on to the next problem. I will happily do that for while, but then the decisions I made start to conflict with each other on some twisted ways (even on humiliating, petty ways, like you run out of good symbols...). So then comes "Oh, but I can't do feature Z the way I find it's the best, because then it clashes with feature Y. Well, then I rather change feature Y a bit... hmmm, too bad Y is uglier now, but we are at least OK. Hey, no, this modified Y now interferes with feature X!". And so on... but after long suffering and many painful language distortions, I end up with a (hopefully) consistent language. Then I look at it, and decide that I don't like it, and I rather run another "circle", means restarting from 0. Then I somehow end up with something totally different. And so on... I can generate tons of template languages this way. :) ) > ${#blah(x)} > Equivalent to FTL expression: ${x?blah}. > > ${x#blah} > Equivalent to FTL expression: ${x?blah}. > > [#function blah, arg1, arg2]body[/#function] > Redefines blah function. > > ${#blah(arg1, arg2)} > Equivalent to FTL expression: ${arg1?blah(arg2)} > > ${arg1#blah(arg2)} > Equivalent to FTL expression: ${arg1?blah(arg2)} > > [#function blah, arg1=dv, ...args]body[/#function] > Showcasing variadic parameters and default parameters. > > The template language will have very light "bean wrappers", Light bean wrappers? I don't get that. > so there's no need for builtins like "substring" or "contains". [snip] > Perhaps JavaScript is a bad example. It's too simplistic to say that > something should be dynamic or not, when there are multiple ways > something can be dynamic. For builtin overriding, I can imagine several > "dynamic levels" that we can choose: > > 1) Allowing builtins to be overridden by anything (and even deleted ala > setting it to null) within the template > 2) Allowing builtins to be overridden by a function that matches the > same interface of the builtin within the template > 3) Same as (1) or (2) except template engine can be configured not to > allow builtin overriding > 4) Same as (1) except builtins can only be overridden during > configuration (from Java) > 5) Same as (2) except builtins can only be overridden during > configuration (from Java) and the interface semantics can be more > precise with the static type checking > 6) Same as any of the above but allow only non-"core" builtins to be > overwritten, where a "core" builtin would be eval, interpret, etc. > 7) Cannot be overwritten, period - not even a Java API to do so (status quo) BTW, I think that in general, you should try avoid overriding and hiding anything by anything, because it's confusing. The exceptions are the use-cases that explicitly build on some kind of overriding, like subclassing in OOP, or "slots" in some template languages. But there should be an effort to exclude the possibility of accidental overrides (for that reason the Java language should require the usage of @Override, but that would be non-BC.). >> > And since it's the user that's overriding them, he knows the exact >> > semantics of it, so I don't see how it can be confusing at all. >> >> Who is "the user"? Because it's certainly not one guy, rather a few. >> Even if it's one guy, certainly it will be another one a few years >> later. Not to mention the tendency of people to forget things they >> did, especially if they participate in several projects over the >> years. So, what going to happen is that the new guy or a some who just >> has forgotten things will Google things, opens the FreeMarker Manual, >> see what built-ins are available and what they do, but... oops. (Then >> you can continue with supporting IDE-s and other text editors when you >> have a custom set of built-ins. It's possible if you expose some API-s >> that help the plugin authors, but, yet another place where >> customization can come back to you.) >> > > That's a problem with code in general and hardly an argument for keeping > builtins unconfigurable. Surely it's a general problem, and that's why I try to avoid such things in general. Part of that general effort is disallowing built-in overriding... in FM at least, because it's a single-language engine. > You could easily mention in the docs that builtins can be redefined > in user code (whether during configuration or within the template). I would, still that only had a very limited effect... (Unless I put flashing red warning box at each built-in reference pages...) > And I don't find the IDE support argument very convincing. If > builtins are configurable in Java, then we have javadocs and method > signatures that can provide hints. Sure, but it's still harder to solve... that's was the point I tried to make. Think about simple syntax-highlighters, especially those that are not Java applications. Now you just can fill some regexps that match the built-in names (sure, you have to extend that for some new releases). So it's a disadvantage, but of course maybe something that you are willing to take. > If builtins can be overridden within the template, well that's just > the nature of dynamic languages, although with very good type > inferencing, editor support is somewhat possible (there are editors > out there that provide decent type/param inferencing for certain > dynamic languages). As far as they can... which is often not that much in practice. The reason is that if you start to really utilize that you are using a dynamic language (and in principle you should, otherwise why don't just use a static one) then, basically for the same reason it couldn't be compiled like a usual static language, the IDE can't find out what's going on. (BTW, part of my design goals was making the language less dynamic.) >> > IMO, there are relatively few builtins that can be considered as core >> > operators: eval, type builtins (int, float, etc.), interpret, and some >> > others. In fact, I would have those particular builtins be actual core >> > language operators rather than "builtins". The rest are hardly related >> > to the core and should be fully customizable. >> >> A design goal with a fat set of core "methods" (in FM they are the >> "built-ins:, and the # directives) is that the wherever the user >> writes a template, he can be sure they are there, and he can access >> them immediately. He doesn't even have to suffer with importing >> something like, I don't know, "fm://utils" and assign it to a prefix, >> which BTW can be quite annoying if you are working with many little >> independent template fragments (message templates, UI bits in a CMS, >> etc.). Also he doesn't need to wonder whether, like, HTML escaping is >> a core method or a utility method. But of course, splitting the >> out-of-the-box methods into core and utility groups has its advantages >> too. But this is about seeking compromises. I actually went back and >> forth between fat-core and thin-core + utils design, but currently I >> would go for fat-core. But that decision very much depends on what >> tricks you find out to avoid the earlier mentioned name clashes. >> > > I'm fine with plenty of builtins. You can have a small set of core > builtins and then libraries that provide other "builtins" that are > autoimported into the same "namespace". Then allow the user to configure > exactly what libraries are autoimported (with the default possibly being > importing everything). How is auto-importing into the same namespace less problematic regarding the name clashes than having a big core library? > In fact, I'd like the "standard library" to be > much bigger. > > Regards, Yuh-Ruey -- Best regards, Daniel Dekany ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Yuh-Ruey Chen
|
Daniel Dekany wrote:
> Tuesday, September 15, 2009, 11:37:15 AM, Yuh-Ruey Chen wrote: > > [snip] > > I do like the idea of a minimal design that can be extended into custom > > template languages, so I very much like to see what you have in mind :) > > > > I'm actually not too bothered with the fact that there are builtins or > > that the syntax is a bit odd-looking. > > It's not that odd-looking... consider "?" as ".", that has something > above it. Both is a sentence-ending punctuation character. > In Java-land, the "?" means conditional, so at first glance, it looks like some weird conditional statement. But I think we both agree that it does look odd. > > But I just don't think builtins functions should be treated > > specially from user-defined functions. Same with builtin directives > > vs. user-defined directives (more on this later). Builtin and > > user-defined functions should have a single unified syntax. Right > > now, builtin functions use the suffix "?" syntax and can't be > > treated as first-class objects (can't be passed around), while > > user-defined functions are actually in the data model and are > > first-class objects. > > I agree with that... but note that treating the built-in > directives/functions/methods/whatever-you-call-them is not possible > for all of them, because some of them has before-runtime effects. > (Anyway, in such cases I planned to drop an exception if and when you > try to call them. So you still can get the thing as a value, and, > like, query it's name and namespace, but you can't call it.) > Yes, there are a couple directives that take effect before the evaluation of the template. #ftl configures the template. #noparse, #compress, #t, #lt, #rt, and #nt affect parsing. Maybe these directives should have a slightly different syntax to denote this difference, e.g. [#ftl#] or [#t#] (similar to <?xml-processing-instruction?>). But I'm not aware of any builtin functions that have such effects. > > Adding functions to the data model (or shared vars) always feels a bit > > clunky to me, because it does collide with the rest of the data model. > > Yes. But later ?new and #import was introduced to counter that > tradition. I'm just not sure if functions should even belong in the data model at all. I'd rather they be in their own "namespace" (or in the same one as the builtins). There's a higher chance of accidental collision if they're in the data model. > (BTW, one of the big problems of FM is that the whole "FTL > library" thing is not very streamlined... For example, you can't just > drop a someoneelsesftllibrary.jar into the classpath of your app., and > then #import it. Things like that...) > ClassTemplateLoader doesn't do this for you? I haven't actually used that class, so I have no idea. > > So user-defined functions can either collide with the data model or the > > builtin functions, and I think going with the latter is a both a cleaner > > and safer design. > > *If* that's the only two possibility, I agree. > You can put them in their own namespace, but considering the minimal overlap with core builtins (there shouldn't be many) and the requiring of a slightly different syntax, I don't think it's worth it. > > Cleaner because the semantics and syntax of all functions are > > unified (hopefully leading to cleaner code as well), and safer > > because the data model is much more variable then the list of > > functions (e.g. if I have a function "foo", it's more likely that > > some data model with a "foo" to collide with it than there would be > > another user-defined function named "foo" to collide with it). > > > > Now to unifying user-defined and builtin directives. There are other > > syntactical differences besides the "#" vs. "@". Some like #assign and > > #if can't be done with user-defined directives. And unless we want a > > XML-like verboseness ala JSTL's choose/when, there's not a good way > > around it (though I'm trying to think of a way they could be > > user-defined yet not verbose). > > Yeah, that's the other tricky part of the unification... You will have > to add some extra features to your template language, that you maybe > wouldn't add otherwise. Anyway, I found that it wasn't a big problem > in practice. Like, I wanted out-parameters feature anyway, and > something like "set target=source" can be seen as an optional > shorthand for an application of that. But I don't think it's wise to > allow the usage of that particular shorthand for user-defined stuff > (but it's really easy to allow it technically... actually, the extra > work is disallowing it). And that wasn't the point anyway... the point > is that *semantically* the "set x=1" is nothing special, it's really > just "set 1; x". Hence, the "=" thing itself is no in the way if you > want to call dynamically something that uses that. > So "set x=1" would make sense to the C/Java developer, while "set 1; x" would not. How about something like this then - reverse the out-params location and use ":" instead ";": [#set x : 3] [#list x : sequence] [#import foo.ftl] [#import foo : foo.ftl] Also, we could just get rid of "using" in #visit and #recurse, treating "namespace" is an optional param: [#recurse doc] [#recurse doc, namespace] There's no equivalent for [#recurse] and [#recurse using namespace], but since that is just syntactic sugar for [#recurse .node], it's hardly a loss. That leaves #if/#else/#elseif, and #switch/#case/#default as the only directives that require special parsing (besides the ones the affect parsing itself, noted above). > > But what about #list and some other directives that use the "as" > > keyword? > > Back then I decided to get rid of "as", "in" and such, and just use > the unified syntax (";"). Since the expression language clearly > separate the in- and out-parameters (with ";"), it's obvious which one > is the source, and which one declare the block-scope variable. Now > there is a compromise here: "list foo as bar" is more telling than > "list foo; bar". I to find something better instead of ";" but > whenever I did it always spoiled something else that I found to be a > bigger problem... so currently it's ";". So, back to the compromise, I > found it's not a that big compromise eventually, if you assume the > user will face a ";" when using some custom macros anyway. Then it's > just better that there is only one syntax he has to learn: use ";", > end of story. > Understandable, but there's room for improvement :) > The other possibility is to allow declaring the usage of "as" or "in" > in the formal parameter list of the macro definitions... only, > depending on what purpose the macro has, you may want another > keyword... which, you won't get, custom keywords. I don't think it's > need to go into it why not... > Yeah not a good idea... > > These directives use the "as" keyword in two ways: introducing > > a new local (loop) variable (if it requires an end tag), or introducing > > a variable to the template's scope (if it cannot have an end tag). > > Meanwhile user-defined directives must use the odd semicolon syntax to > > introduce new local variables, and cannot introduce template-scope > > variables. I think there is room for unification here. > > > > As to whether it's a good idea treat builtins and user-defined functions > > and directives the same, it should at the very least be configurable - > > Hardly. To unify them, you need to touch some quite fundamental rules > of the language. In FTL, directives meant to generating markup (hence > they kind of look like HTML tags), while other expressions (like > function calls) aren't. The importance of this shows when you apply > #escape; it will not escape the output of directives. Also, in > FreeMarker only directives can return the result progressively (i.e., > they start to output it before it's complete). So, what I'm saying, > that in the case of FreeMarker there is fundamental difference between > the two. (I'm not saying there should be... as I said, in my planned > solution there isn't. But of course, that has some painful > consequences too...) > Oops, I meant to say "builtin and user-defined functions, and builtin and user-defined directives". I'm not suggesting that functions and directives should be unified. > BTW, the unification require things like blocks inside parameter > lists... technically doable, but, well, it's a long story, but it > encourages some bad practices, if you have the "multiple bodies" or > "template-defined parameters" (see in ZipScript) or whatever we call > it feature. > Huh, never heard of ZipScript before - I'll check it out... > > [#macro foo, arg1=dv, ...args as var1, ...vars] > > [#list args as arg]stuff[/#list] > > [#list vars as var][#set var=blah][/#list] > > [/#macro] > > Example showcasing variadic parameters and default parameters. "vars" is > > a list of references, so that in [#list vars as var], "var" is a > > reference, so [#set var=...] works as you'd expect. > > Yes. Actually, I also allowed named variadic parameters. Very very > useful feature. (And something *similar* to that is painfully missing > from most compiled languages. Like, this lack of true named parameters > is why dependency injection via constructor arguments sucks.) > BTW, Python goes pretty extreme with regards to flexibility here: def foo(*args) # variadic function, rest params are stored in args as a list def foo(**args) # variadic function, named rest params are stored in args as a map from param name to param value def foo(*args, **kwargs) # combines the above two def foo(arg1, arg2, *args) # just showing that you can use rest params with normal params def foo(arg1, arg2=val) # optional named param def foo(*args, arg=val) # variadic function; if arg is specified, it must be a name=value pair (cannot be a positional param) def foo(*, arg1=val1, arg2=val2) # non-variadic function with non-positional params > > There are two forms that the user can use: the familiar Java prefix > > style, or the FreeMarker suffix style. > > > > To try to keep the syntax as consistent as possible, I'm using "#" for > > template functions. So to the user, "#" means "template directive/function". > > I didn't do that, among others because then you can just write f(x), > you have to write #f(x). So I just used | for doing things like x|f. > The problem with that it that the function looks like it's part of the data model. Also, it can be incompatible with other JVM languages that allow class-less functions to be defined, tying FreeMarker too closely with Java. On a related note, I think it would be better to not treat FM namespaces like any other hash, making it look like it's part of the data model. How about using ":" instead: [#ns:directive] ${#ns:function()} or ${ns:function()} ${expr#ns:function} or ${expr?ns:function} or ${expr|ns:function} > But, I'm not saying it's wrong... depends on everything else. > (Because, at least for me, designing a good language is an endless > running of circles... It's like, I pick a particular problem, find a > nice solution for it, and go on to the next problem. I will happily do > that for while, but then the decisions I made start to conflict with > each other on some twisted ways (even on humiliating, petty ways, like > you run out of good symbols...). So then comes "Oh, but I can't do > feature Z the way I find it's the best, because then it clashes with > feature Y. Well, then I rather change feature Y a bit... hmmm, too bad > Y is uglier now, but we are at least OK. Hey, no, this modified Y now > interferes with feature X!". And so on... but after long suffering and > many painful language distortions, I end up with a (hopefully) > consistent language. Then I look at it, and decide that I don't like > it, and I rather run another "circle", means restarting from 0. Then I > somehow end up with something totally different. And so on... I can > generate tons of template languages this way. :) ) > I totally get you - I've had my share of experience with designing languages, and it's really a cycle of eurekas followed by "oh wait" followed by eureka, ad infinitum. > > The template language will have very light "bean wrappers", > > Light bean wrappers? I don't get that. > Basically the only object wrapper is the one that allows you to access Java object fields and methods reflectively. No custom "scalars", "sequences", "hashes", etc. and thus no nee to builtins like "substring" or "contains". > BTW, I think that in general, you should try avoid overriding and > hiding anything by anything, because it's confusing. The exceptions > are the use-cases that explicitly build on some kind of overriding, > like subclassing in OOP, or "slots" in some template languages. But > there should be an effort to exclude the possibility of accidental > overrides (for that reason the Java language should require the usage > of @Override, but that would be non-BC.). > I agree to a certain extent, but to me it really depends on the situation. For ex, when I'm programming in Java, I try to be a strict as possible to take advantage of the static checking, but when I'm using Python, I just generally follow best practices and rely on unit tests. FreeMarker is somewhere between the two - dynamic yet trying to be rigid. As I noted, the spectrum between "dynamic" and "static" is wide and there are plenty of advantages and disadvantages on each extreme. So exactly what you pick is a matter of philosophical preference. And I personally lean toward "configurable dynamic-ness" where the language can be as dynamic or as rigid as the user wants. > > And I don't find the IDE support argument very convincing. If > > builtins are configurable in Java, then we have javadocs and method > > signatures that can provide hints. > > Sure, but it's still harder to solve... that's was the point I tried > to make. Think about simple syntax-highlighters, especially those that > are not Java applications. Now you just can fill some regexps that > match the built-in names (sure, you have to extend that for some new > releases). So it's a disadvantage, but of course maybe something that > you are willing to take. > > > If builtins can be overridden within the template, well that's just > > the nature of dynamic languages, although with very good type > > inferencing, editor support is somewhat possible (there are editors > > out there that provide decent type/param inferencing for certain > > dynamic languages). > > As far as they can... which is often not that much in practice. The > reason is that if you start to really utilize that you are using a > dynamic language (and in principle you should, otherwise why don't > just use a static one) then, basically for the same reason it couldn't > be compiled like a usual static language, the IDE can't find out > what's going on. (BTW, part of my design goals was making the language > less dynamic.) > IDE support isn't a big issue IMO. To me, there are 2 main things the IDEs can provide: 1) refactoring/boilerplate aid, and 2) syntax highlighting/visual assist. For (1), only verbose languages (like Java) really benefit from this. And for (2), syntax highlighting is typically good enough, with any extra visual assistance like "intellisense" just being cake frosting. > >> A design goal with a fat set of core "methods" (in FM they are the > >> "built-ins:, and the # directives) is that the wherever the user > >> writes a template, he can be sure they are there, and he can access > >> them immediately. He doesn't even have to suffer with importing > >> something like, I don't know, "fm://utils" and assign it to a prefix, > >> which BTW can be quite annoying if you are working with many little > >> independent template fragments (message templates, UI bits in a CMS, > >> etc.). Also he doesn't need to wonder whether, like, HTML escaping is > >> a core method or a utility method. But of course, splitting the > >> out-of-the-box methods into core and utility groups has its advantages > >> too. But this is about seeking compromises. I actually went back and > >> forth between fat-core and thin-core + utils design, but currently I > >> would go for fat-core. But that decision very much depends on what > >> tricks you find out to avoid the earlier mentioned name clashes. > >> > > > > I'm fine with plenty of builtins. You can have a small set of core > > builtins and then libraries that provide other "builtins" that are > > autoimported into the same "namespace". Then allow the user to configure > > exactly what libraries are autoimported (with the default possibly being > > importing everything). > > How is auto-importing into the same namespace less problematic > regarding the name clashes than having a big core library? > The only thing I'm proposing here is modularizing the library into separate core/builtin libraries. This will have no effect to the typical user, since all these core libraries are imported automatically, but I'm suggesting the ability for advanced users (i.e. making a derivative template language) to configure exactly which libraries are auto-imported. So we can have a larger library, yet import a relatively small amount by default. Regard, Yuh-Ruey ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Daniel Dekany
|
Wednesday, September 16, 2009, 12:21:47 PM, Yuh-Ruey Chen wrote:
> Daniel Dekany wrote: >> Tuesday, September 15, 2009, 11:37:15 AM, Yuh-Ruey Chen wrote: >> >> [snip] >> > I do like the idea of a minimal design that can be extended into custom >> > template languages, so I very much like to see what you have in mind :) >> > >> > I'm actually not too bothered with the fact that there are builtins or >> > that the syntax is a bit odd-looking. >> >> It's not that odd-looking... consider "?" as ".", that has something >> above it. Both is a sentence-ending punctuation character. >> > > In Java-land, the "?" means conditional, so at first glance, it looks > like some weird conditional statement. But I think we both agree that it > does look odd. Yeah, but mostly only because it looks like the ternary operator you mentioned. (Which brings up another typical problem in language design... even if you don't run out of good (telling, suggestive) symbols within your own language, you also have to respect the syntax of Java. And, maybe even of Groovy, etc. Actually, not following them is often acceptable, but using something that looks like something in Java, but does something different...) >> > But I just don't think builtins functions should be treated >> > specially from user-defined functions. Same with builtin directives >> > vs. user-defined directives (more on this later). Builtin and >> > user-defined functions should have a single unified syntax. Right >> > now, builtin functions use the suffix "?" syntax and can't be >> > treated as first-class objects (can't be passed around), while >> > user-defined functions are actually in the data model and are >> > first-class objects. >> >> I agree with that... but note that treating the built-in >> directives/functions/methods/whatever-you-call-them is not possible >> for all of them, because some of them has before-runtime effects. >> (Anyway, in such cases I planned to drop an exception if and when you >> try to call them. So you still can get the thing as a value, and, >> like, query it's name and namespace, but you can't call it.) >> > > Yes, there are a couple directives that take effect before the > evaluation of the template. #ftl configures the template. #noparse, > #compress, #t, #lt, #rt, and #nt affect parsing. Maybe these directives > should have a slightly different syntax to denote this difference, e.g. > [#ftl#] or [#t#] (similar to <?xml-processing-instruction?>). Been there... I ended up on the standpoint that it doesn't worth it (unless you have tons of fancy "pre-processor" directives and like in which case it becomes crucial to visually separate what is executed in the 1st, and what in the 2nd phase). It adds visual clutter, and is one more thing to remember for most users... you language looks more complex, more confusing. All that for a little theoretical clarity, doesn't worth it. > But I'm not aware of any builtin functions that have such effects. Not that it's impossible that one day you want to add a such function... >> > Adding functions to the data model (or shared vars) always feels a bit >> > clunky to me, because it does collide with the rest of the data model. >> >> Yes. But later ?new and #import was introduced to counter that >> tradition. > > I'm just not sure if functions should even belong in the data model at > all. I'd rather they be in their own "namespace" Exactly... Java Language has the trick for it (methods VS fields), and I applied exactly the same trick: if you call it, the name is to be searched in the method namespace, otherwise in the plain-variable namespace. It makes getting methods as values a bit tricky (yet possible: currying), but in exchange you get a lot of benefits. And the same trick you can apply on the data-model, so Map items doesn't conflict with Map methods anymore. Now, at this point, let me ask, why are you so interested in template language design? I mean, it looks like you have given considerable amount of thought to the topic. So, what are you up to? > (or in the same one as > the builtins). There's a higher chance of accidental collision if > they're in the data model. > >> (BTW, one of the big problems of FM is that the whole "FTL >> library" thing is not very streamlined... For example, you can't just >> drop a someoneelsesftllibrary.jar into the classpath of your app., and >> then #import it. Things like that...) >> > > ClassTemplateLoader doesn't do this for you? I haven't actually used > that class, so I have no idea. > >> > So user-defined functions can either collide with the data model or the >> > builtin functions, and I think going with the latter is a both a cleaner >> > and safer design. >> >> *If* that's the only two possibility, I agree. >> > > You can put them in their own namespace, but considering the minimal > overlap with core builtins (there shouldn't be many) and the requiring > of a slightly different syntax, I don't think it's worth it. > >> > Cleaner because the semantics and syntax of all functions are >> > unified (hopefully leading to cleaner code as well), and safer >> > because the data model is much more variable then the list of >> > functions (e.g. if I have a function "foo", it's more likely that >> > some data model with a "foo" to collide with it than there would be >> > another user-defined function named "foo" to collide with it). >> > >> > Now to unifying user-defined and builtin directives. There are other >> > syntactical differences besides the "#" vs. "@". Some like #assign and >> > #if can't be done with user-defined directives. And unless we want a >> > XML-like verboseness ala JSTL's choose/when, there's not a good way >> > around it (though I'm trying to think of a way they could be >> > user-defined yet not verbose). >> >> Yeah, that's the other tricky part of the unification... You will have >> to add some extra features to your template language, that you maybe >> wouldn't add otherwise. Anyway, I found that it wasn't a big problem >> in practice. Like, I wanted out-parameters feature anyway, and >> something like "set target=source" can be seen as an optional >> shorthand for an application of that. But I don't think it's wise to >> allow the usage of that particular shorthand for user-defined stuff >> (but it's really easy to allow it technically... actually, the extra >> work is disallowing it). And that wasn't the point anyway... the point >> is that *semantically* the "set x=1" is nothing special, it's really >> just "set 1; x". Hence, the "=" thing itself is no in the way if you >> want to call dynamically something that uses that. >> > > So "set x=1" would make sense to the C/Java developer, while "set 1; x" > would not. Nobody should write "set 1; x", it's that what "set x=1" means. I know it's a bit non-uniform this way, but you hardly ever can do something both practically and on a totally uniform/generic way on the same time. > How about something like this then - reverse the out-params > location and use ":" instead ";": > > [#set x : 3] > [#list x : sequence] > [#import foo.ftl] > [#import foo : foo.ftl] /-: I think for assignment everyone just wants to write "=". But then, there are directives that do have out-params but are still not about assignment. So there you will just use the unified syntax, and for set the special syntax. > Also, we could just get rid of "using" in #visit and #recurse, treating > "namespace" is an optional param: > > [#recurse doc] > [#recurse doc, namespace] This kind of usage of keywords it actually the result of not using named parameters. I mean, [#recurse doc, namespace] is not very telling, [#recurse doc using namespace] is. But, using named parameters, [#recurse doc, using: namespace] would be too... > There's no equivalent for [#recurse] and [#recurse using namespace], With named parameters there is: [#recurse using: namespace] > but since that is just syntactic sugar for [#recurse .node], it's > hardly a loss. > > That leaves #if/#else/#elseif, and #switch/#case/#default as the only > directives that require special parsing (besides the ones the affect > parsing itself, noted above). They are not *that* special necessarily. In FTL only #if had an end-tag, not the #else/#leseif, which makes then quite much special. But that can be avoided with a different syntax. Like, in WebMacro-style (which is basically JavaScript-style) there is no such kind of problem whatsoever. The only speciality that remains is the way how if/else/elseif changes the control flow. Like, a successful #if skips all following #else/#elseif *siblings*. It's a bit extreme that node has such control over his siblings (as opposed to over its children). >> > But what about #list and some other directives that use the "as" >> > keyword? >> >> Back then I decided to get rid of "as", "in" and such, and just use >> the unified syntax (";"). Since the expression language clearly >> separate the in- and out-parameters (with ";"), it's obvious which one >> is the source, and which one declare the block-scope variable. Now >> there is a compromise here: "list foo as bar" is more telling than >> "list foo; bar". I to find something better instead of ";" but >> whenever I did it always spoiled something else that I found to be a >> bigger problem... so currently it's ";". So, back to the compromise, I >> found it's not a that big compromise eventually, if you assume the >> user will face a ";" when using some custom macros anyway. Then it's >> just better that there is only one syntax he has to learn: use ";", >> end of story. >> > > Understandable, but there's room for improvement :) Maybe, maybe... but as I said, you improve something, and then realize you have to un-improve something else because of that. >> The other possibility is to allow declaring the usage of "as" or "in" >> in the formal parameter list of the macro definitions... only, >> depending on what purpose the macro has, you may want another >> keyword... which, you won't get, custom keywords. I don't think it's >> need to go into it why not... >> > > Yeah not a good idea... > >> > These directives use the "as" keyword in two ways: introducing >> > a new local (loop) variable (if it requires an end tag), or introducing >> > a variable to the template's scope (if it cannot have an end tag). >> > Meanwhile user-defined directives must use the odd semicolon syntax to >> > introduce new local variables, and cannot introduce template-scope >> > variables. I think there is room for unification here. >> > >> > As to whether it's a good idea treat builtins and user-defined functions >> > and directives the same, it should at the very least be configurable - >> >> Hardly. To unify them, you need to touch some quite fundamental rules >> of the language. In FTL, directives meant to generating markup (hence >> they kind of look like HTML tags), while other expressions (like >> function calls) aren't. The importance of this shows when you apply >> #escape; it will not escape the output of directives. Also, in >> FreeMarker only directives can return the result progressively (i.e., >> they start to output it before it's complete). So, what I'm saying, >> that in the case of FreeMarker there is fundamental difference between >> the two. (I'm not saying there should be... as I said, in my planned >> solution there isn't. But of course, that has some painful >> consequences too...) >> > > Oops, I meant to say "builtin and user-defined functions, and builtin > and user-defined directives". I'm not suggesting that functions and > directives should be unified. I see. >> BTW, the unification require things like blocks inside parameter >> lists... technically doable, but, well, it's a long story, but it >> encourages some bad practices, if you have the "multiple bodies" or >> "template-defined parameters" (see in ZipScript) or whatever we call >> it feature. >> > > Huh, never heard of ZipScript before - I'll check it out... > >> > [#macro foo, arg1=dv, ...args as var1, ...vars] >> > [#list args as arg]stuff[/#list] >> > [#list vars as var][#set var=blah][/#list] >> > [/#macro] >> > Example showcasing variadic parameters and default parameters. "vars" is >> > a list of references, so that in [#list vars as var], "var" is a >> > reference, so [#set var=...] works as you'd expect. >> >> Yes. Actually, I also allowed named variadic parameters. Very very >> useful feature. (And something *similar* to that is painfully missing >> from most compiled languages. Like, this lack of true named parameters >> is why dependency injection via constructor arguments sucks.) >> > > BTW, Python goes pretty extreme with regards to flexibility here: > > def foo(*args) # variadic function, rest params are stored in args as a list > def foo(**args) # variadic function, named rest params are stored in > args as a map from param name to param value > def foo(*args, **kwargs) # combines the above two > def foo(arg1, arg2, *args) # just showing that you can use rest params > with normal params > def foo(arg1, arg2=val) # optional named param > def foo(*args, arg=val) # variadic function; if arg is specified, it > must be a name=value pair (cannot be a positional param) > def foo(*, arg1=val1, arg2=val2) # non-variadic function with > non-positional params > >> > There are two forms that the user can use: the familiar Java prefix >> > style, or the FreeMarker suffix style. >> > >> > To try to keep the syntax as consistent as possible, I'm using "#" for >> > template functions. So to the user, "#" means "template directive/function". >> >> I didn't do that, among others because then you can just write f(x), >> you have to write #f(x). So I just used | for doing things like x|f. >> > > The problem with that it that the function looks like it's part of the > data model. Yes, but since you seldom has top-level methods in the data-model, you can assume that any top-level f() is the calling of a core method. That was one of the "painful" tricks I did. > Also, it can be incompatible with other JVM languages that > allow class-less functions to be defined, tying FreeMarker too closely > with Java. > > On a related note, I think it would be better to not treat FM namespaces > like any other hash, making it look like it's part of the data model. Yeah... but unfortunately, that has it's drawbacks to. Like, since namespace *are* hashes, you may want to pass them around as values. How do you refer to them then? "ns:.root" or something... awkward. But whatever... However funny is, the bigger problem I ran into is that there is pretty big competition for the ":" symbol. Named parameters want that, ternary operator want that... and if more of them has it, you usually end up with an ambiguous syntax. So, surely in general it's desirable to separate namespace prefixes from other variables, but unfortunately at the end of that circle (referring to the "running circles" I wrote about earlier) you may end up without that. > How about using ":" instead: > > [#ns:directive] > > ${#ns:function()} or ${ns:function()} > > ${expr#ns:function} or ${expr?ns:function} or ${expr|ns:function} Note that for the last line dot works too: expr#ns.function, etc. >> But, I'm not saying it's wrong... depends on everything else. >> (Because, at least for me, designing a good language is an endless >> running of circles... It's like, I pick a particular problem, find a >> nice solution for it, and go on to the next problem. I will happily do >> that for while, but then the decisions I made start to conflict with >> each other on some twisted ways (even on humiliating, petty ways, like >> you run out of good symbols...). So then comes "Oh, but I can't do >> feature Z the way I find it's the best, because then it clashes with >> feature Y. Well, then I rather change feature Y a bit... hmmm, too bad >> Y is uglier now, but we are at least OK. Hey, no, this modified Y now >> interferes with feature X!". And so on... but after long suffering and >> many painful language distortions, I end up with a (hopefully) >> consistent language. Then I look at it, and decide that I don't like >> it, and I rather run another "circle", means restarting from 0. Then I >> somehow end up with something totally different. And so on... I can >> generate tons of template languages this way. :) ) >> > > I totally get you - I've had my share of experience with designing > languages, and it's really a cycle of eurekas followed by "oh wait" > followed by eureka, ad infinitum. > >> > The template language will have very light "bean wrappers", >> >> Light bean wrappers? I don't get that. >> > > Basically the only object wrapper is the one that allows you to > access Java object fields and methods reflectively. No custom > "scalars", "sequences", "hashes", etc. and thus no nee to builtins > like "substring" or "contains". > >> BTW, I think that in general, you should try avoid overriding and >> hiding anything by anything, because it's confusing. The exceptions >> are the use-cases that explicitly build on some kind of overriding, >> like subclassing in OOP, or "slots" in some template languages. But >> there should be an effort to exclude the possibility of accidental >> overrides (for that reason the Java language should require the usage >> of @Override, but that would be non-BC.). >> > > I agree to a certain extent, but to me it really depends on the > situation. For ex, when I'm programming in Java, I try to be a strict as > possible to take advantage of the static checking, but when I'm using > Python, I just generally follow best practices and rely on unit tests. > FreeMarker is somewhere between the two - dynamic yet trying to be > rigid. As I noted, the spectrum between "dynamic" and "static" is wide > and there are plenty of advantages and disadvantages on each extreme. So > exactly what you pick is a matter of philosophical preference. And I > personally lean toward "configurable dynamic-ness" where the language > can be as dynamic or as rigid as the user wants. Surely that *sounds* like the ideal solution... but I'm not sure yet if in practice how that will work out. Even a little option to be un-strict can make things that worked reliably earlier become suddenly complex and unreliable (because something that doesn't work only in 1% of cases is immediately not reliable). I would rather think the way out is remaining strict, but with more powerful type system and like (see Scala), but we shall see... >> > And I don't find the IDE support argument very convincing. If >> > builtins are configurable in Java, then we have javadocs and method >> > signatures that can provide hints. >> >> Sure, but it's still harder to solve... that's was the point I tried >> to make. Think about simple syntax-highlighters, especially those that >> are not Java applications. Now you just can fill some regexps that >> match the built-in names (sure, you have to extend that for some new >> releases). So it's a disadvantage, but of course maybe something that >> you are willing to take. >> >> > If builtins can be overridden within the template, well that's just >> > the nature of dynamic languages, although with very good type >> > inferencing, editor support is somewhat possible (there are editors >> > out there that provide decent type/param inferencing for certain >> > dynamic languages). >> >> As far as they can... which is often not that much in practice. The >> reason is that if you start to really utilize that you are using a >> dynamic language (and in principle you should, otherwise why don't >> just use a static one) then, basically for the same reason it couldn't >> be compiled like a usual static language, the IDE can't find out >> what's going on. (BTW, part of my design goals was making the language >> less dynamic.) >> > > IDE support isn't a big issue IMO. To me, there are 2 main things > the IDEs can provide: 1) refactoring/boilerplate aid, and 2) syntax > highlighting/visual assist. Good for you! For me, marking the typos (like in method names) and auto-completion (especially for parameter lists) is a must-have too. I just don't have the mental ability of typing without error and memorizing tons of API-s to the letter... But I think I'm not completely alone with these. > For (1), only verbose languages (like Java) really benefit from > this. And for (2), syntax highlighting is typically good enough, > with any extra visual assistance like "intellisense" just being cake > frosting. > >> >> A design goal with a fat set of core "methods" (in FM they are the >> >> "built-ins:, and the # directives) is that the wherever the user >> >> writes a template, he can be sure they are there, and he can access >> >> them immediately. He doesn't even have to suffer with importing >> >> something like, I don't know, "fm://utils" and assign it to a prefix, >> >> which BTW can be quite annoying if you are working with many little >> >> independent template fragments (message templates, UI bits in a CMS, >> >> etc.). Also he doesn't need to wonder whether, like, HTML escaping is >> >> a core method or a utility method. But of course, splitting the >> >> out-of-the-box methods into core and utility groups has its advantages >> >> too. But this is about seeking compromises. I actually went back and >> >> forth between fat-core and thin-core + utils design, but currently I >> >> would go for fat-core. But that decision very much depends on what >> >> tricks you find out to avoid the earlier mentioned name clashes. >> >> >> > >> > I'm fine with plenty of builtins. You can have a small set of core >> > builtins and then libraries that provide other "builtins" that are >> > autoimported into the same "namespace". Then allow the user to configure >> > exactly what libraries are autoimported (with the default possibly being >> > importing everything). >> >> How is auto-importing into the same namespace less problematic >> regarding the name clashes than having a big core library? >> > > The only thing I'm proposing here is modularizing the library into > separate core/builtin libraries. This will have no effect to the typical > user, since all these core libraries are imported automatically, but I'm > suggesting the ability for advanced users (i.e. making a derivative > template language) to configure exactly which libraries are auto-imported. Ah, that. Of course, that how it should work. > So we can have a larger library, yet import a relatively small amount by > default. > > Regard, Yuh-Ruey -- Best regards, Daniel Dekany ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Yuh-Ruey Chen
|
Daniel Dekany wrote:
> Wednesday, September 16, 2009, 12:21:47 PM, Yuh-Ruey Chen wrote: > > > Daniel Dekany wrote: > > Yes, there are a couple directives that take effect before the > > evaluation of the template. #ftl configures the template. #noparse, > > #compress, #t, #lt, #rt, and #nt affect parsing. Maybe these directives > > should have a slightly different syntax to denote this difference, e.g. > > [#ftl#] or [#t#] (similar to <?xml-processing-instruction?>). > > Been there... I ended up on the standpoint that it doesn't worth it > (unless you have tons of fancy "pre-processor" directives and like in > which case it becomes crucial to visually separate what is executed in > the 1st, and what in the 2nd phase). It adds visual clutter, and is > one more thing to remember for most users... you language looks more > complex, more confusing. All that for a little theoretical clarity, > doesn't worth it. > Fair enough. I also don't think it's worth it, and it's unlikely that there will be many new "parsing directives". > > But I'm not aware of any builtin functions that have such effects. > > Not that it's impossible that one day you want to add a such > function... > Can you give an example of an operation affecting parsing that would be better of as a function (within an expression that's evaluated at runtime!) than as a directive? > >> > Adding functions to the data model (or shared vars) always feels a bit > >> > clunky to me, because it does collide with the rest of the data model. > >> > >> Yes. But later ?new and #import was introduced to counter that > >> tradition. > > > > I'm just not sure if functions should even belong in the data model at > > all. I'd rather they be in their own "namespace" > > Exactly... Java Language has the trick for it (methods VS fields), and > I applied exactly the same trick: if you call it, the name is to be > searched in the method namespace, otherwise in the plain-variable > namespace. It makes getting methods as values a bit tricky (yet > possible: currying), but in exchange you get a lot of benefits. And > the same trick you can apply on the data-model, so Map items doesn't > conflict with Map methods anymore. > Well that's an odd use of currying. In any case, I don't know what the future holds for Java's popularity, but the JVM will likely stick around for a while, so it may not be such a good idea to tie a template language so closely to Java's quirks. For example, you mention Scala (which is a language I'm very interested in delving into), and it has function objects. > Now, at this point, let me ask, why are you so interested in template > language design? I mean, it looks like you have given considerable > amount of thought to the topic. So, what are you up to? > Taking over the world of course. Seriously, I just have a general interest in programming language design. And I haven't spent that much time thinking about template languages in particular - maybe less than half a day's worth with these emails. And now a question of my own: do you ever intend to act on any of these language redesigns? :) > > Also, we could just get rid of "using" in #visit and #recurse, treating > > "namespace" is an optional param: > > > > [#recurse doc] > > [#recurse doc, namespace] > > This kind of usage of keywords it actually the result of not using > named parameters. I mean, [#recurse doc, namespace] is not very > telling, [#recurse doc using namespace] is. But, using named > parameters, [#recurse doc, using: namespace] would be too... > But what makes [#recurse] or [#visit] so special that it deserves the same "syntactic attention" as assignment or conditionals? #recurse and #visit could just be left out of the core and defined in a library (in Java of course). To give some perspective: I defined a directive called #let that I use to create "scopes": [@let expr; var] usage of ${var} [/@let] which is a "shortcut" for [#macro some_name_just_provides_scoping] [#set var = expr] usage of ${var} [/#macro] [@some_name_just_provides_scoping] It seems "unfair" that [#recurse] or [#visit] get special attention, when this particular user-defined directive is more "core-like". > > That leaves #if/#else/#elseif, and #switch/#case/#default as the only > > directives that require special parsing (besides the ones the affect > > parsing itself, noted above). > > They are not *that* special necessarily. In FTL only #if had an > end-tag, not the #else/#leseif, which makes then quite much special. > But that can be avoided with a different syntax. Like, in > WebMacro-style (which is basically JavaScript-style) there is no such > kind of problem whatsoever. The only speciality that remains is the > way how if/else/elseif changes the control flow. Like, a successful > #if skips all following #else/#elseif *siblings*. It's a bit extreme > that node has such control over his siblings (as opposed to over its > children). > One point that I forgot to mention is how builtin directives could omit the "/" for body-less directives (in fact, I'm not even sure it's legal?). Which makes #else (among others) special. Here's a wild suggestion concerning control flow: Add a method to DirectiveBody that returns a new template (essentially uses the same context as the current template and is rooted at the directive template element). Then the user can walk the AST and selectively execute nodes. That would allow advanced FreeMarker users (from the Java side) to implement nearly any non-parsing directive they want. > > Also, it can be incompatible with other JVM languages that > > allow class-less functions to be defined, tying FreeMarker too closely > > with Java. > > > > On a related note, I think it would be better to not treat FM namespaces > > like any other hash, making it look like it's part of the data model. > > Yeah... but unfortunately, that has it's drawbacks to. Like, since > namespace *are* hashes, you may want to pass them around as values. > How do you refer to them then? "ns:.root" or something... awkward. But > whatever... However funny is, the bigger problem I ran into is that > there is pretty big competition for the ":" symbol. Named parameters > want that, ternary operator want that... and if more of them has it, > you usually end up with an ambiguous syntax. So, surely in general > it's desirable to separate namespace prefixes from other variables, > but unfortunately at the end of that circle (referring to the "running > circles" I wrote about earlier) you may end up without that. > True. Using ":" may also be result in some confusion with XML namespaces, e.g. ${node["x:foo"]} vs . ${node[x:foo]}. But still, namespaces colliding with the data model is bad. I can envision a scenario where such a collision would be hard to detect (in that no error would be thrown): both the model object named x and a namespace imported as x have a same "method signature". > > I agree to a certain extent, but to me it really depends on the > > situation. For ex, when I'm programming in Java, I try to be a strict as > > possible to take advantage of the static checking, but when I'm using > > Python, I just generally follow best practices and rely on unit tests. > > FreeMarker is somewhere between the two - dynamic yet trying to be > > rigid. As I noted, the spectrum between "dynamic" and "static" is wide > > and there are plenty of advantages and disadvantages on each extreme. So > > exactly what you pick is a matter of philosophical preference. And I > > personally lean toward "configurable dynamic-ness" where the language > > can be as dynamic or as rigid as the user wants. > > Surely that *sounds* like the ideal solution... but I'm not sure yet > if in practice how that will work out. Even a little option to be > un-strict can make things that worked reliably earlier become suddenly > complex and unreliable (because something that doesn't work only in 1% > of cases is immediately not reliable). I would rather think the way > out is remaining strict, but with more powerful type system and like > (see Scala), but we shall see... > So you want extensibility, but "strict" extensibility. I like the idea, but again it really depends on the situation and what exactly is extensible, e.g. if you're designing a template engine that targets arbitrary template languages, you do want a lot of flexibility, but within a particular language, not so much. Also note that even the notoriously strict Java can be fairly dynamic with reflection [abuse]. > > IDE support isn't a big issue IMO. To me, there are 2 main things > > the IDEs can provide: 1) refactoring/boilerplate aid, and 2) syntax > > highlighting/visual assist. > > Good for you! For me, marking the typos (like in method names) and > auto-completion (especially for parameter lists) is a must-have too. I > just don't have the mental ability of typing without error and > memorizing tons of API-s to the letter... But I think I'm not > completely alone with these. > Yet dynamic languages that lack extensive IDE support still flourish. To each, his own. Regards, Yuh-Ruey ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Daniel Dekany
|
Thursday, September 17, 2009, 10:42:01 AM, Yuh-Ruey Chen wrote:
> Daniel Dekany wrote: >> Wednesday, September 16, 2009, 12:21:47 PM, Yuh-Ruey Chen wrote: >> >> > Daniel Dekany wrote: >> > Yes, there are a couple directives that take effect before the >> > evaluation of the template. #ftl configures the template. #noparse, >> > #compress, #t, #lt, #rt, and #nt affect parsing. Maybe these directives >> > should have a slightly different syntax to denote this difference, e.g. >> > [#ftl#] or [#t#] (similar to <?xml-processing-instruction?>). >> >> Been there... I ended up on the standpoint that it doesn't worth it >> (unless you have tons of fancy "pre-processor" directives and like in >> which case it becomes crucial to visually separate what is executed in >> the 1st, and what in the 2nd phase). It adds visual clutter, and is >> one more thing to remember for most users... you language looks more >> complex, more confusing. All that for a little theoretical clarity, >> doesn't worth it. >> > > Fair enough. I also don't think it's worth it, and it's unlikely that > there will be many new "parsing directives". > >> > But I'm not aware of any builtin functions that have such effects. >> >> Not that it's impossible that one day you want to add a such >> function... >> > > Can you give an example of an operation affecting parsing that would be > better of as a function (within an expression that's evaluated at > runtime!) than as a directive? Examples of such functions are those than return their own location in the source code, or those that crate a closure from their parameter expression (for delayed execution). >> >> > Adding functions to the data model (or shared vars) always feels a bit >> >> > clunky to me, because it does collide with the rest of the data model. >> >> >> >> Yes. But later ?new and #import was introduced to counter that >> >> tradition. >> > >> > I'm just not sure if functions should even belong in the data model at >> > all. I'd rather they be in their own "namespace" >> >> Exactly... Java Language has the trick for it (methods VS fields), and >> I applied exactly the same trick: if you call it, the name is to be >> searched in the method namespace, otherwise in the plain-variable >> namespace. It makes getting methods as values a bit tricky (yet >> possible: currying), but in exchange you get a lot of benefits. And >> the same trick you can apply on the data-model, so Map items doesn't >> conflict with Map methods anymore. >> > > Well that's an odd use of currying. In any case, I don't know what the > future holds for Java's popularity, but the JVM will likely stick around > for a while, so it may not be such a good idea to tie a template > language so closely to Java's quirks. Using a separate namespace for the methods and the fields is actually the feature of the JVM. Not that a language can't chose to hidate that... But I don't think we will see the fall of the Java Language earlier than the fall of whole Java (JVM, etc.) thing. Poor Scala has no chance to take over (in popularity), because it's too difficult to grasp. > For example, you mention Scala (which is a language I'm very > interested in delving into), and it has function objects. Note that the planned template language do have functions as objects, only the syntactical trick to get those objects is an extreme case of currying, where you left all parameters unspecified. >> Now, at this point, let me ask, why are you so interested in template >> language design? I mean, it looks like you have given considerable >> amount of thought to the topic. So, what are you up to? >> > Taking over the world of course. Great! Then you are in! ;-) > Seriously, I just have a general interest in programming language > design. And I haven't spent that much time thinking about template > languages in particular - maybe less than half a day's worth with these > emails. > > And now a question of my own: do you ever intend to act on any of these > language redesigns? :) I do *intend* to. But then, it's not at all sure that I will... /-: First of all, it's a *lot* of time to release something like this, secondly I didn't have the impression that any of the key personal here (but me) was very much enthusiastic about this. They may prefer continuing gradual evolution instead. Anyway, if I release something, maybe they will feel like jumping in, so the blame is on me completely. BTW, it's more than language redesign, I also changed the whole public API. I suppose I should hold a presentation on the topic on the next Visigoth Con... >> > Also, we could just get rid of "using" in #visit and #recurse, treating >> > "namespace" is an optional param: >> > >> > [#recurse doc] >> > [#recurse doc, namespace] >> >> This kind of usage of keywords it actually the result of not using >> named parameters. I mean, [#recurse doc, namespace] is not very >> telling, [#recurse doc using namespace] is. But, using named >> parameters, [#recurse doc, using: namespace] would be too... >> > > But what makes [#recurse] or [#visit] so special that it deserves the > same "syntactic attention" as assignment or conditionals? I guess some people finds it more readable that way, and that's it... > #recurse and #visit could just be left out of the core and defined > in a library (in Java of course). In the case of FM, it's just simpler if everything is just there, fixed. > To give some perspective: I defined a directive called #let that I use > to create "scopes": > > [@let expr; var] > usage of ${var} > [/@let] > > which is a "shortcut" for > > [#macro some_name_just_provides_scoping] > [#set var = expr] > usage of ${var} > [/#macro] > [@some_name_just_provides_scoping] > > It seems "unfair" that [#recurse] or [#visit] get special attention, > when this particular user-defined directive is more "core-like". I used to say that if you can't implement a core directive using the same API-s as users can define their own directives, then your API/design certainly has some issues. This doesn't go to very core stuff of course... >> > That leaves #if/#else/#elseif, and #switch/#case/#default as the only >> > directives that require special parsing (besides the ones the affect >> > parsing itself, noted above). >> >> They are not *that* special necessarily. In FTL only #if had an >> end-tag, not the #else/#leseif, which makes then quite much special. >> But that can be avoided with a different syntax. Like, in >> WebMacro-style (which is basically JavaScript-style) there is no such >> kind of problem whatsoever. The only speciality that remains is the >> way how if/else/elseif changes the control flow. Like, a successful >> #if skips all following #else/#elseif *siblings*. It's a bit extreme >> that node has such control over his siblings (as opposed to over its >> children). >> > > One point that I forgot to mention is how builtin directives could omit > the "/" for body-less directives (in fact, I'm not even sure it's > legal?). Which makes #else (among others) special. The empty-tag "/" and like... If the language knows the called directive in parsing time (which is surely the case for core directives), it just knows that it's body-less directive or not. It could know that for user-defined stuff as well, as far as you don't use late-binding to the user libraries (i.e., you import them when you parse the dependent template), but that's usually too restrictive for a template language. For that reason I prefer the syntax where finding out such things (i.e., if this is a start-tag or an empty-tag a.k.a. start-end-tag) doesn't require the knowing of he directives. Also, #else is not body-less (let alone how it was implemented in FM... I don't remember how it was). It definitely has body, but it also has a strange kind of *implied* end-tag. > Here's a wild suggestion concerning control flow: Add a method to > DirectiveBody that returns a new template (essentially uses the same > context as the current template and is rooted at the directive template > element). Then the user can walk the AST and selectively execute nodes. > That would allow advanced FreeMarker users (from the Java side) to > implement nearly any non-parsing directive they want. That's a possible way of allowing this, yes... Only I'm not very happy with such rules that can't "declared", rather they are described by the implementation, also not very happy with custom directives that change the control flow on an irregular way. I mean, you have a few core directives that do that, and for those the user will learn they behave that way. Anyway, for if/elseif/else or attempt/recover (or try/except/finally) it's just intuitive. But when you look at a template full of calls to 3rd party directives, you expect they obey to the regular control flow rules. As of the first thing, "not declared", there is a solution, that also mitigates the second problem (being confusing) a bit: directives could be declared as siblings, means they will be collected by the parser under a common implicit parent node, and also that the engine will not allow any other kind of nodes occur between them (like myIf{...}WOMBAT{...}myElse{...}). >> > Also, it can be incompatible with other JVM languages that >> > allow class-less functions to be defined, tying FreeMarker too closely >> > with Java. >> > >> > On a related note, I think it would be better to not treat FM namespaces >> > like any other hash, making it look like it's part of the data model. >> >> Yeah... but unfortunately, that has it's drawbacks to. Like, since >> namespace *are* hashes, you may want to pass them around as values. >> How do you refer to them then? "ns:.root" or something... awkward. But >> whatever... However funny is, the bigger problem I ran into is that >> there is pretty big competition for the ":" symbol. Named parameters >> want that, ternary operator want that... and if more of them has it, >> you usually end up with an ambiguous syntax. So, surely in general >> it's desirable to separate namespace prefixes from other variables, >> but unfortunately at the end of that circle (referring to the "running >> circles" I wrote about earlier) you may end up without that. >> > > True. Using ":" may also be result in some confusion with XML > namespaces, e.g. ${node["x:foo"]} vs . ${node[x:foo]}. > > But still, namespaces colliding with the data model is bad. Yes... during my latest "circle", I still ended up with that. /-: All because I ran out of symbols. ":" is definitely taken, as the de-facto standard operator in the Java-world for key-name separation (used for Map literals and named parameters) and also as character in the ternary operator. "." is for plain sub-variables. So... I could chose some arbitrary operator as separator, like, x@foo, but it looks so bad, I don't think it would be popular despite it prevents those name clashes. Which are, IMO, more seldom problems than some would believe, because: - The namespace prefixes has lexical scope and are declared at the beginning of the template (not strictly so in FM, but whatever), so as the template author you surely know what prefixes you have. - If you want to get a data-model variable with a runtime known name, something like .data[name], you skip the prefix layer anyway, so it won't interfere. - If some of parts of the template is auto-generated, then you can still chose to store the prefix in a plain template-defined/local variable, which in my language can't hide the data-model, because (now prepare for shock) you must use a @ prefix before their names. (This last decision surely demands explanation, but it's OT now. OK, one factor is that since you are not supposed to do calculations in a template, you are assumed to use non-data-model variables relatively seldom. Anyway, PHP and Velocity survives with demanding you to put $ before *all* variables for, well, not very convicting reasons... yet people are not pulling their hair out, so I suppose most people don't mind typing odd symbols before variable names. I do, but... well, at least in this case I get something in exchange: looking at @foo immediately tells me that "foo" is not something from that undeclared sea of variables that are in the data-model, and foo immediately tells me it is from the data-model... or, it's a prefix. /-: ) - Prefixes are usually short, like "m" "fw", etc., while data-model variables aren't usually. > I can envision a scenario where such a collision would be hard to > detect (in that no error would be thrown): both the model object > named x and a namespace imported as x have a same "method > signature". > >> > I agree to a certain extent, but to me it really depends on the >> > situation. For ex, when I'm programming in Java, I try to be a strict as >> > possible to take advantage of the static checking, but when I'm using >> > Python, I just generally follow best practices and rely on unit tests. >> > FreeMarker is somewhere between the two - dynamic yet trying to be >> > rigid. As I noted, the spectrum between "dynamic" and "static" is wide >> > and there are plenty of advantages and disadvantages on each extreme. So >> > exactly what you pick is a matter of philosophical preference. And I >> > personally lean toward "configurable dynamic-ness" where the language >> > can be as dynamic or as rigid as the user wants. >> >> Surely that *sounds* like the ideal solution... but I'm not sure yet >> if in practice how that will work out. Even a little option to be >> un-strict can make things that worked reliably earlier become suddenly >> complex and unreliable (because something that doesn't work only in 1% >> of cases is immediately not reliable). I would rather think the way >> out is remaining strict, but with more powerful type system and like >> (see Scala), but we shall see... >> > > So you want extensibility, but "strict" extensibility. I like the idea, > but again it really depends on the situation and what exactly is > extensible, e.g. if you're designing a template engine that targets > arbitrary template languages, you do want a lot of flexibility, but > within a particular language, not so much. Also note that even the > notoriously strict Java can be fairly dynamic with reflection [abuse]. I surely don't want to design an engine that targets arbitrary template languages, because template languages can be fundamentally different, which would led to more and more layers of abstraction, that would make the stuff damn hard to understand and use for mere mortals. I want to target a specific class of template engines, which I could describe as WebMacro/Velocty/FreeMarker and it's ad-hoc gown pals. That concept I want to extend on a few ways (like on the field of ensuring valid output), but it's still that good-old imperative stuff. There are the other kind of template languages, like ZPT, or Wicket's templates, etc. I don't say they are mistaken or anything, but I just don't want to target that field, because its requirements are too different. So what I want to achieve with customization: I want to save people from inventing their own little-lame ad-hoc WebMacro-like template engines here and there, because none of the available ones had the ideal syntax (e.g., when the template syntax clashes with something that is frequent in your static parts...), many of them had a too intrusive language (in a mail client you want CC to be a *core* directive, no stupid prefixes...), had too intrusive API (e.g., your storage logic is not like getting templates by path-like strings), etc. I know I can't satisfy all such cases, but being a bit smarter I'm sure I can save a few times more than FM can. Also, I think we have to understand that dynamic Web pages become less and less important for this kind of template language... FM was basically about that, generating HTML pages in Web MVC, but was universal enough to be usable elsewhere. Component-oriented Web UI-s should slowly kill that filed now, so the alternative use-case become more important, and those are much more diverse, so you can do less assumptions than before. So that's again something why I want to allow the easy creation of custom languages. Easy means that you can build it merely by configuring stuff. >> > IDE support isn't a big issue IMO. To me, there are 2 main things >> > the IDEs can provide: 1) refactoring/boilerplate aid, and 2) syntax >> > highlighting/visual assist. >> >> Good for you! For me, marking the typos (like in method names) and >> auto-completion (especially for parameter lists) is a must-have too. I >> just don't have the mental ability of typing without error and >> memorizing tons of API-s to the letter... But I think I'm not >> completely alone with these. >> > > Yet dynamic languages that lack extensive IDE support still flourish. To > each, his own. But it's not like people chose tools on their *inherent* technical merits. Like, as an example, PHP is still the most widespread language for creating web stuff, and certainly 20 years later it still will be... Why? I guess because back then it was on the right place on the right time, so it grew a huge user-base. And since it's what everyone uses, it's what everyone tries, and stuck with. Anyway, because of the huge user-base and focused goal (Web...) there is are tons of useful 3rd party libraries for you. Also, you barely can find a Web server where PHP isn't installed, which makes life easy. So now actually you have good technical reasons to choose it. So PHP is raging success, but not at all because it's a so bright concept or anything... it's not that at all. > Regards, Yuh-Ruey -- Best regards, Daniel Dekany ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Attila Szegedi-3
|
In reply to this post
by Yuh-Ruey Chen
Folks, reading all this, I keep thinking "Lisp" more and more.
Selectively walkable AST, configurable overriding rules, scoped let directive, newly defined directives being same level citizens as previously defined ones, etc. etc. Lots of things you discuss are very close to invoking Greenspun's Tenth Rule, so you might as well go the full length and embrace Lisp for a template engine project. I can quite warmheartedly suggest Clojure - it's a Lisp, and it's native to JVM, and it's modern, performant, and concurrency-safe, among other things. It also compiles to Java bytecode. I think I'm actually getting excited at the prospect of creating a template engine in Clojure. Or it might actually be *the* template engine in Clojure, because if some basics with default behaviour are laid, down, anyone can go in there and customize anything on any level with an appropriate new set of Lisp macros. Just a brainstorming idea... Attila. On 2009.09.17., at 1:42, Yuh-Ruey Chen wrote: > Daniel Dekany wrote: >> Wednesday, September 16, 2009, 12:21:47 PM, Yuh-Ruey Chen wrote: >> >>> Daniel Dekany wrote: >>> Yes, there are a couple directives that take effect before the >>> evaluation of the template. #ftl configures the template. #noparse, >>> #compress, #t, #lt, #rt, and #nt affect parsing. Maybe these >>> directives >>> should have a slightly different syntax to denote this difference, >>> e.g. >>> [#ftl#] or [#t#] (similar to <?xml-processing-instruction?>). >> >> Been there... I ended up on the standpoint that it doesn't worth it >> (unless you have tons of fancy "pre-processor" directives and like in >> which case it becomes crucial to visually separate what is executed >> in >> the 1st, and what in the 2nd phase). It adds visual clutter, and is >> one more thing to remember for most users... you language looks more >> complex, more confusing. All that for a little theoretical clarity, >> doesn't worth it. >> > > Fair enough. I also don't think it's worth it, and it's unlikely that > there will be many new "parsing directives". > >>> But I'm not aware of any builtin functions that have such effects. >> >> Not that it's impossible that one day you want to add a such >> function... >> > > Can you give an example of an operation affecting parsing that would > be > better of as a function (within an expression that's evaluated at > runtime!) than as a directive? > >>>>> Adding functions to the data model (or shared vars) always feels >>>>> a bit >>>>> clunky to me, because it does collide with the rest of the data >>>>> model. >>>> >>>> Yes. But later ?new and #import was introduced to counter that >>>> tradition. >>> >>> I'm just not sure if functions should even belong in the data >>> model at >>> all. I'd rather they be in their own "namespace" >> >> Exactly... Java Language has the trick for it (methods VS fields), >> and >> I applied exactly the same trick: if you call it, the name is to be >> searched in the method namespace, otherwise in the plain-variable >> namespace. It makes getting methods as values a bit tricky (yet >> possible: currying), but in exchange you get a lot of benefits. And >> the same trick you can apply on the data-model, so Map items doesn't >> conflict with Map methods anymore. >> > > Well that's an odd use of currying. In any case, I don't know what the > future holds for Java's popularity, but the JVM will likely stick > around > for a while, so it may not be such a good idea to tie a template > language so closely to Java's quirks. For example, you mention Scala > (which is a language I'm very interested in delving into), and it has > function objects. > >> Now, at this point, let me ask, why are you so interested in template >> language design? I mean, it looks like you have given considerable >> amount of thought to the topic. So, what are you up to? >> > > Taking over the world of course. > > Seriously, I just have a general interest in programming language > design. And I haven't spent that much time thinking about template > languages in particular - maybe less than half a day's worth with > these > emails. > > And now a question of my own: do you ever intend to act on any of > these > language redesigns? :) > >>> Also, we could just get rid of "using" in #visit and #recurse, >>> treating >>> "namespace" is an optional param: >>> >>> [#recurse doc] >>> [#recurse doc, namespace] >> >> This kind of usage of keywords it actually the result of not using >> named parameters. I mean, [#recurse doc, namespace] is not very >> telling, [#recurse doc using namespace] is. But, using named >> parameters, [#recurse doc, using: namespace] would be too... >> > > But what makes [#recurse] or [#visit] so special that it deserves the > same "syntactic attention" as assignment or conditionals? #recurse and > #visit could just be left out of the core and defined in a library (in > Java of course). > > To give some perspective: I defined a directive called #let that I use > to create "scopes": > > [@let expr; var] > usage of ${var} > [/@let] > > which is a "shortcut" for > > [#macro some_name_just_provides_scoping] > [#set var = expr] > usage of ${var} > [/#macro] > [@some_name_just_provides_scoping] > > It seems "unfair" that [#recurse] or [#visit] get special attention, > when this particular user-defined directive is more "core-like". > >>> That leaves #if/#else/#elseif, and #switch/#case/#default as the >>> only >>> directives that require special parsing (besides the ones the affect >>> parsing itself, noted above). >> >> They are not *that* special necessarily. In FTL only #if had an >> end-tag, not the #else/#leseif, which makes then quite much special. >> But that can be avoided with a different syntax. Like, in >> WebMacro-style (which is basically JavaScript-style) there is no such >> kind of problem whatsoever. The only speciality that remains is the >> way how if/else/elseif changes the control flow. Like, a successful >> #if skips all following #else/#elseif *siblings*. It's a bit extreme >> that node has such control over his siblings (as opposed to over its >> children). >> > > One point that I forgot to mention is how builtin directives could > omit > the "/" for body-less directives (in fact, I'm not even sure it's > legal?). Which makes #else (among others) special. > > Here's a wild suggestion concerning control flow: Add a method to > DirectiveBody that returns a new template (essentially uses the same > context as the current template and is rooted at the directive > template > element). Then the user can walk the AST and selectively execute > nodes. > That would allow advanced FreeMarker users (from the Java side) to > implement nearly any non-parsing directive they want. > >>> Also, it can be incompatible with other JVM languages that >>> allow class-less functions to be defined, tying FreeMarker too >>> closely >>> with Java. >>> >>> On a related note, I think it would be better to not treat FM >>> namespaces >>> like any other hash, making it look like it's part of the data >>> model. >> >> Yeah... but unfortunately, that has it's drawbacks to. Like, since >> namespace *are* hashes, you may want to pass them around as values. >> How do you refer to them then? "ns:.root" or something... awkward. >> But >> whatever... However funny is, the bigger problem I ran into is that >> there is pretty big competition for the ":" symbol. Named parameters >> want that, ternary operator want that... and if more of them has it, >> you usually end up with an ambiguous syntax. So, surely in general >> it's desirable to separate namespace prefixes from other variables, >> but unfortunately at the end of that circle (referring to the >> "running >> circles" I wrote about earlier) you may end up without that. >> > > True. Using ":" may also be result in some confusion with XML > namespaces, e.g. ${node["x:foo"]} vs . ${node[x:foo]}. > > But still, namespaces colliding with the data model is bad. I can > envision a scenario where such a collision would be hard to detect (in > that no error would be thrown): both the model object named x and a > namespace imported as x have a same "method signature". > >>> I agree to a certain extent, but to me it really depends on the >>> situation. For ex, when I'm programming in Java, I try to be a >>> strict as >>> possible to take advantage of the static checking, but when I'm >>> using >>> Python, I just generally follow best practices and rely on unit >>> tests. >>> FreeMarker is somewhere between the two - dynamic yet trying to be >>> rigid. As I noted, the spectrum between "dynamic" and "static" is >>> wide >>> and there are plenty of advantages and disadvantages on each >>> extreme. So >>> exactly what you pick is a matter of philosophical preference. And I >>> personally lean toward "configurable dynamic-ness" where the >>> language >>> can be as dynamic or as rigid as the user wants. >> >> Surely that *sounds* like the ideal solution... but I'm not sure yet >> if in practice how that will work out. Even a little option to be >> un-strict can make things that worked reliably earlier become >> suddenly >> complex and unreliable (because something that doesn't work only in >> 1% >> of cases is immediately not reliable). I would rather think the way >> out is remaining strict, but with more powerful type system and like >> (see Scala), but we shall see... >> > > So you want extensibility, but "strict" extensibility. I like the > idea, > but again it really depends on the situation and what exactly is > extensible, e.g. if you're designing a template engine that targets > arbitrary template languages, you do want a lot of flexibility, but > within a particular language, not so much. Also note that even the > notoriously strict Java can be fairly dynamic with reflection [abuse]. > >>> IDE support isn't a big issue IMO. To me, there are 2 main things >>> the IDEs can provide: 1) refactoring/boilerplate aid, and 2) syntax >>> highlighting/visual assist. >> >> Good for you! For me, marking the typos (like in method names) and >> auto-completion (especially for parameter lists) is a must-have >> too. I >> just don't have the mental ability of typing without error and >> memorizing tons of API-s to the letter... But I think I'm not >> completely alone with these. >> > > Yet dynamic languages that lack extensive IDE support still > flourish. To > each, his own. > > Regards, Yuh-Ruey Attila. -- twitter: http://twitter.com/szegedi weblog: http://constc.blogspot.com ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Attila Szegedi-3
|
In reply to this post
by Daniel Dekany
On 2009.09.15., at 6:27, Daniel Dekany wrote:
> (Configurable language rules... I would allow it only if you define a > new template language... that could/should be made easy, but still, > you have to give it another name.) I suggest "Lisp". No, seriously, for a practical Java solution, just take Clojure, add some reader macros that allow you nice expression of templates, and then you can configure everything else you want using other macros. Attila. ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Daniel Dekany
|
Monday, September 21, 2009, 10:38:31 AM, Attila Szegedi wrote:
> On 2009.09.15., at 6:27, Daniel Dekany wrote: > >> (Configurable language rules... I would allow it only if you define a >> new template language... that could/should be made easy, but still, >> you have to give it another name.) > > I suggest "Lisp". No, seriously, for a practical Java solution, just > take Clojure, add some reader macros that allow you nice expression of > templates, and then you can configure everything else you want using > other macros. You think that way you end up with a solution that can be distributed as an easy-to-use and reasonably light-weight template engine? One that has an API and syntax that you imagined? > Attila. -- Best regards, Daniel Dekany ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Attila Szegedi-3
|
On 2009.09.21., at 22:48, Daniel Dekany wrote:
> Monday, September 21, 2009, 10:38:31 AM, Attila Szegedi wrote: > >> On 2009.09.15., at 6:27, Daniel Dekany wrote: >> >>> (Configurable language rules... I would allow it only if you >>> define a >>> new template language... that could/should be made easy, but still, >>> you have to give it another name.) >> >> I suggest "Lisp". No, seriously, for a practical Java solution, just >> take Clojure, add some reader macros that allow you nice expression >> of >> templates, and then you can configure everything else you want using >> other macros. > > You think that way you end up with a solution that can be distributed > as an easy-to-use and reasonably light-weight template engine? One > that has an API and syntax that you imagined? I believe yes. I can't prove it otherwise than actually attempting to do it, though. Attila. >> Attila. > > -- > Best regards, > Daniel Dekany ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
|
Daniel Dekany
|
Tuesday, September 22, 2009, 1:23:58 AM, Attila Szegedi wrote:
> On 2009.09.21., at 22:48, Daniel Dekany wrote: > >> Monday, September 21, 2009, 10:38:31 AM, Attila Szegedi wrote: >> >>> On 2009.09.15., at 6:27, Daniel Dekany wrote: >>> >>>> (Configurable language rules... I would allow it only if you >>>> define a >>>> new template language... that could/should be made easy, but still, >>>> you have to give it another name.) >>> >>> I suggest "Lisp". No, seriously, for a practical Java solution, just >>> take Clojure, add some reader macros that allow you nice expression >>> of >>> templates, and then you can configure everything else you want using >>> other macros. >> >> You think that way you end up with a solution that can be distributed >> as an easy-to-use and reasonably light-weight template engine? One >> that has an API and syntax that you imagined? > > I believe yes. I can't prove it otherwise than actually attempting to > do it, though. To make it clear, it's OK for me if I can use Clojure as an implementation of the template language, but I certainly wouldn't tolerate if Clojure dictates the language design too much... (And I bet it spoils the error messages.) BTW, when I considered how to do the multiple-bodies thing (a frequent request) I found that, of course, for doing that in a nice/generic way (means, parameters and bodies are the same thing) you should switch to functional programming. That's because the order of the execution of the "blocks" can be quite different from what you would deduce from looking at the source code, and that's confusing if you have mutable variables. But surely you don't want functional programming in a practical template language that targets average IT guys, do you? -- Best regards, Daniel Dekany ------------------------------------------------------------------------------ Come build with us! The BlackBerry® Developer Conference in SF, CA is the only developer event you need to attend this year. Jumpstart your developing skills, take BlackBerry mobile applications to market and stay ahead of the curve. Join us from November 9-12, 2009. Register now! http://p.sf.net/sfu/devconf _______________________________________________ FreeMarker-devel mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/freemarker-devel |
||||||||||||||||
| Free Embeddable Forum Powered by Nabble | Help |