Tuesday 7 January 2014

CFML: Results of survey about function expressions in tag-based code

G'day:
A few days ago I opened a survey, "A quick survey about tags & script in the same statement" (it's still open, so go have your say if you haven't already: I'll update the results posted here). This was in response to a comment from Rupesh:

I am questioning whether this syntax can cause confusion

The syntax in question relates to being able to use function expressions in tag-based code, eg:

<cfset c = function(){ 
    var a = something;
    var b = foo();
    return a * b;
}>

I contended that was pretty bloody patronising, and I doubted anyone who was going to be using function expressions would be thrown by this sort of thing.

Still... what do I know? And I decided to poll my readers to see what they thought.

Now I get that no survey I run on this blog is going to be statistically meaningful, due to the way demographics work, and how low my readership is, and accordingly how few responses I get. But I figured in this case it might be slightly more representation of the audience for this question because stuff like function expressions is probably going to be limited to the more... erm... "engaged" people in the CFML community, and not that I reach out to all of them, but the people who take time to read blogs are going to be part of that "more engaged" demographic.

So, anyway, I was hoping for 50+ responses before I reported back, but the news is getting "stale" now, and I seem to have stalled on 49 responses. Near enough.

Right, so the first / main question was:

This code demonstrates a function expression being declared via a <cfset> tag.

<cfset c = function(){ 
    var a = something;
    var b = foo();
    return a * b;
}>

How confusing is it that this statement has a mix of both tags and CFScript?
And the responses were as follows:



Rating%age of totalNumber of votes
1 - Not at all confusing67.35%33
214.29%7
310.20%5
46.12%3
5 - Incomprehensible2.04%1
Total49

So about 80% err towards "not really a problem" with about 8% erring towards "yeah, that's (at least ~) a bit confusing". With 10% in the middle. That's about what I expected, I guess. Well it doesn't surprise me anyhow.

I got a bunch of feedback comments, listed below. I'll add my own thoughts in response where appropriate.


It was one of the first things I tried to do when playing with the new features of CF 10. I work mostly with tags and not in cfscript.
From this I infer the respondent had no problem with the syntax at all and sees no problem with the approach of havnig function expressions - leveraging script - in tag-based code. Which is pretty much my position. This is actually interesting in contrast to some of the other answers...

This reads fine, however if you're going to take it this far, what if I wanted to use tags inside the function? As you would in a tag, shouldn't that be possible? I struggle to find a good reason to define an anonymous function in a tag based context though.
A problem here is that a function expression needs to be an expression, and one cannot have tags in an expression in CFML. Then again, prior to function expressions, one couldn't have statements in an expression either, so I guess that is slightly unfair. That said, how could this possibly work in tags? But how could one possibly codify a very common idiom: assigning an expression to a variable? Or using it as an attribute value if it was done in tags? EG:

<cfset squarer =    <cffunction>
                        <cfargument name="x" type="numeric" required="true">
                        <cfreturn x * x>
                    </cffunction>
>

That's ghastly. Surely, even if you like tags for everything, that sort of syntax there is bloody awful? The problem is, tags just don't lend themselves to expressions. Whereas curly-bracket code kinda does. Function expressions aside, people have asked for a way of switching from CFscript code to tag code within an existing <cfscript> block (analogous to the <cfscript> tag for putting script in the middle of tag code). I have yet to see a good suggested approach to this, and I think it's a fool's errand anyhow: the disparate nature of the two coding styles are such that if you are thinking of mixing the two... yer probably writing yer code wrong. And, anyhow, one can do an include in script, and that can pull in an entire tag-based file.

As for why one would want to write an anonymous function in tag-based code? Well... you might want to call arrayEach() or structEach(), which are specifically designed to take inline function expressions. Or perhaps call some other bespoke code that takes a callback... one might - quite reasonably - want to write it inline.

Obviously one could declare the callback function with <cffunction> first, then pass it, but that's slightly contrary to the common paradigm for this sort of code. Plus often these things need to leverage closure, and a <cffunction> can't. And suggesting one could take the <cffunction> route is just offering a band-aid for not simply doing it the "normal" way.


The issue here is one of consistency. I do believe that, for ColdFusion to be a consistent language, then the following will have to be true: c=x; is a valid statement in cfscript if and only if is a valid statement To moot, consider x to be function(){ var a = something; var b = foo(); return a * b; }
I have no idea what you are trying to say here.

While it's not hard at all to follow the code, I would think new developers might have to spend some time figuring this out and why tags and scripts are mixed since it doesn't work in the other direction - tags within scripts.
Well a new developer isn't going to have any expectations. And let's not forget that expressions already aren't created with tags, so I don't think it necessarily follows that it would be unexpected to see script-style code where script-style code is called for.

I think observations like this - and I don't think they're invalid - stem from people who are used to writing all their programming code in tags, and therefore think curly-brace code to seem "odd". The only demographic of developers for whom that will apply are habitual CFML developers who - for whatever reason - write all their code in tags. This will not apply to new developers. New developers will not have seen vast tracts of code written in tags before. But they will more than likely have familiarity with curly-brace code.


You're asking the wrong question. Its not confusing but bad form. Why are those who prefer cfscript trying to mess with those of us who prefer tags? Are we trying to reinsert tags back into cfscript? I agree with you the vast majority of the time but you're off base here. Getting cfscript advocates to support you does not make a cf majority.
This is a very odd outlook, and a very tag-defensive one. It's all CFML. Whilst there might be "tag developers" (ones who only use tags), there's no such thing, I think, as "script developers". They're just "CFML developers, who happen to not restrict themselves from using a specific subset of the language (ie: CFScript)".

Equally, this is not "messing with those of use who prefer tags", by its very nature it's a suggestion to implement something for tag users that they otherwise cannot do in tag-based code. There is currently no way of creating a function expression in tag-based code. I'm suggesting there ought to be!!


Give an example where this would be useful.
Function expressions? Look at pretty much anything anyone does in JQuery (or node.js if you want to stick server side). Or... most languages. Function expressions are really very very common.

Bear in mind that function expressions already exist in CFML (since Railo 4 / ColdFusion 10).


Honestly, being able to write closures inside a cfset tag isn't a big deal for me. I'd already be writing in cfscript. I believe that how we code in tags vs script should be as consistent as possible, but if we're looking at priorities there are other things I'd like Adobe to work on instead. Since we're talking about making script work inside a closure defined in a cf tag, why not just make cfset evaluate like cfscript? Maybe do a . What about doing the same between # signs?
Don't get hung-up on the <cfset>. That was just an example I lifted from the ticket. The issue here is being able to do function expressions at all in tags. This doesn't have to be done in <cfset>, it could just be passing a callback to a function which expects one. These don't always require closure - so a function declared by a <cffunction> would do, but often they do leverage closure, and a function expression is currently the only way to create a function which uses closure. This could be worked around by an attribute of <cffunction>, but I suspect the way they're compiled might need to change to make it work (I dunno). Plus it's just a work-around for how these things are usually done.

It's not confusing, but it's definitely ugly!
I reckon all tag code is ugly. Shrug. ;-)

Think of ColdBox view helpers, which will often use and therefore are at least partially written in tag syntax, add the ability to use a closure in arraySort()/arrayFilter()/... and you have a standard example for this kind of situation. Then again this doesn't really apply to us, since our company has already switched to Railo because we have better uses for our money than and updates/patches that do more damage than good ...
Indeed.

It's not confusing, but they should not be mixed.
OK, so how else to create a function expression in tag-based code?

While I can agree that mixing script and tags together is ridiculous, this is not the same case. I welcome such a feature for its power and in my opinion if your not embracing both sides (cfscript/tags) and using them thoroughly in their justified manners, then you're not using CF to it's full pinnacle of power and fluid-ness.
Well said.

It's a little out of the ordinary, but I think people who *should* be doing CF development won't have a problem with it - it might confuse the copy-paste brigade. What I do find unusual is that, if Adobe finds this to be confusing, how on earth does it explain the syntax they're using with CFCLIENT? Seems like that has a similar script-in-tag sort of flavor.
[cough]. I'm glad someone mentioned <cfclient>. Although looking at the sample <cfclient> code, it specifically creates a stand-alone function, then passes it by name as a callback. Which is pretty unnatural in the context it's being done:

<cfset $(document).ready(on_ready)>

<!--- on JQuery ready --->
<cffunction name="on_ready" >
    <cfset setupPaths()> <!--- in cfsummit.js--->
    
    <!--- initialize a few global variables --->
    <cfset window.ds_name = "cfsummit"> <!--- Data source name --->
    <cfset window.activeDay = "1">
    <cfset window.session_scroll_pos = 0>
    <cfset window.speaker_scroll_pos = 0>
    <cfset window._g_db_initialized = false>
    
    <!--- Setup image map for back and home button in the bottom banner image ---> 
    <cf_image_map image_id="title_img1"
            image_areas= #titleImageAreas# <!--- images areas defined in cfsummit.js --->
            image_width=640 <!--- original width of the image --->
            image_height=110 > <!--- Original height of the image --->
            
    <cfset displayStartPage()> <!--- in cfsummit.js --->

</cffunction>

That's not how anyone would ever do that "in real life". One would use an inline function expression in the ready() call. As we have all done, many times.

There is no other way to define a closure inside tag notation at all...
Indeed. I'm open to other suggestions...?

It's weird and inconsistent, which is asking for trouble. But it's far from incomprehensible.


I suppose I do not find this confusing because I understand programming languages. This is a function expression, very similar to how JavaScript would do it. Easy enough.
Yup. Not. Confusing.

I wonder if this felt so natural because of my JavaScript experience. Maybe other developers not as familiar with client technologies would have a more difficult time.
Quite possibly. And that is fair enough. However this is not grounds to hamstring the language.

Trying to wrap my head around closures in general, but the syntax above is very straightforward.
Yeah, this is another good point... function expressions & closure in general are "interesting" topics, and not straight forward. And I reckon if one can get one's brain around that, then that one might sometimes have one in the middle of tag-based code is not going to be "confusing".

I don't think I'd love to see this kind of cfset in my codebase, but I certainly don't find it confusing.
I'm thinking maybe I should not have recycled Rupesh's example.

I'm glad Railo got it right. :-P
Almost right. It has at least one bug (cannot use ">" in the function expression), and another limitation (cannot evaluate() a function expression).

All expressions should be treated equally. ColdFusion's inability to use == and || and && in cfif has always been a ridiculous limitation in my opinion. Function expressions are in exactly the same class as that, as far as I'm concerned.
This is precisely my position too. An expression is just an expression. Same in tags as it is in script.

A rather strange (strange, as in, "why?") mix of tag-based CF and CFScript, I assume. I say, "I assume", because I don't use CFScript. It's not that difficult to understand, mainly because I use jQuery all the time, and everything inside the tag is understandable. But why start mixed tags and script, would be my question? Do we need even more of a hybrid language? I would keep tag-based and script-based completely separate. ColdFusion was always touted a simpler to learn because it was tag-based. Now, beginners are going to have to grasp tags, as well as script for a understanding of the language. Keep tags and script separate, but equal.
Well in general one would just stick with CFScript. But some people don't like CFScript, but they might still want to use function expressions...

What is confusing is the use of the name "Closure" in this context. That's just an anonymous function, not a closure.
I know. But I was mimicking Adobe's misuse of the term. It's a fairly common misuse, too: one of those terms that is now considered "correct" due to how misused it's been.

Closures can be confusing by themselves, whether they have on the other, versus just having a semi colon on the right, whats the freaking difference. Like you said, if it confuses them, they wouldn't even try and do it Not like Adobe hasn't made easy things complicated in the first place.
Agreed.

I find the code confusing and inconsistent with the rest language. I believe I would rather see a tag way of declaring a closure, either through a new tag or perhaps an addition to the existing cffunction tag.
This is worth discussion, but it's kind of a different discussion. As per above "closure" != "function expression". They're two different concepts.

It's not that confusing just really bad visually (and sometimes to troubleshoot) when you mix tags and script imo.
Yeah, true. However I don't think this is a reason to disallow it. And, let's face it, a lot of people think tag code is pretty bloody awful to look at already. And others think the same about script code.

I think it makes sense to do it this way so cf tags are not embedded inside a cf tag. But I would want to do it either way because I am not a huge cfscript fan.
And if you needed to use a function expression (especially one that uses closure)? Then what?

While not confusing, I think it looks weird, and even a little ugly. Mostly because using tabs to follow code flow, clearly seeing cfscript tags (or entire CFCs in script) typically makes things readable, this just seems a tad on the weird side. I'm not sure what benefit you have in doing script within a cfset vs. doing the definition (though admittedly things like the ternary operator used to look weird but are beyond epic).
It does have readability issues, yeah.

Using to create a script function may seem redundant but would save you a few characters of typing over using . I like it.


Is this an attempt to make CF more JQueryish?
Well... not, not really. Remember that function expressions are not new to CFML. The issue here is they're not available in tag-based code.


OK, so that was all the feedback. It seemed to be a mix of a variation on a few different positions:
  • No problem.
  • Not entirely happy with the code-style mixing (no suggested remedies though).
  • Anxiety about mixing script with tags.
  • Not quite understanding the question.
I don't think I did myself any favours here by using that particular example. Maybe something like this would have been better:

<cfset NZCricketSquad = [
    {first="Martin", last="Guptill"},
    {first="Jesse", last="Ryder"},
    {first="Kane", last="Williamson"},
    {first="Ross", last="Taylor"},
    {first="Brendon", last="McCullum"},
    {first="Corey", last="Anderson"},
    {first="Luke", last="Ronchi"},
    {first="Adam", last="Milne"},
    {first="James", last="Neesham"},
    {first="Nathan", last="McCullum"},
    {first="Kyle", last="Mills"},
    {first="Mitchell", last="McClenaghan"}
]>
<cfdump var="#NZCricketSquad#" label="Batting order">
<cfset arraySort(NZCricketSquad, function(v1, v2){
    var compLast = compare(v1.last, v2.last);
    if (compLast != 0) {
        return compLast;
    }
    return compare(v1.first, v2.first);
})>
<cfdump var="#NZCricketSquad#" label="Name order">

Because that's a real world case (and it runs fine on Railo, incidentally), and an example of using a function expression in tag-based code. Is it that bad? I'm used to using script almost all the time, so to me the only thing that looks bad is that it's in tags.

A pure tag-based example would be:

<cffunction name="sorter">
    <cfargument name="v1">
    <cfargument name="v2">
    <cfset var compLast = compare(v1.last, v2.last)>
    <cfif compLast NEQ 0>
        <cfreturn compLast>
    </cfif>
    <cfreturn compare(v1.first, v2.first)>
</cffunction>
<cfset arraySort(NZCricketSquad, sorter)>

And that's fine (well to me it's an eyesore, but that's a separate thing). Fortunately in this example I don't need to use closure, so this approach is possible. If I needed closure, I'd have to use script.

Bottom line: the majority of the respondents did not think the mooted syntax was confusing. And I guess the minority who did could simply use CFScript instead (either all the time like I do, or switch into it when needing to use function expressions).

According to the bug tracker, Rupesh has "seen the light" on this anyhow, as it's now marked "to fix". Good stuff.

--
Adam