An Architect's View

CFML, Clojure, Software Design, Frameworks and more...

An Architect's View

CFML Advisory: Enhancing function declarations in CFSCRIPT

May 8, 2009 ·

When I posted the quick tip about argument types and default values for Railo, Ben Pate asked if the CFML Advisory Committee was discussing this stuff. The short answer is: yes. The long answer is...We're close to consensus on a number of enhancements to CFSCRIPT, most of which we've decided should be part of core CFML (and part of the CFML2009 specification we plan to publish this year). In particular, we decided that core CFML should allow you to specify:
  • access (public, private, package, remote)
  • return type
  • argument types
  • default values
That means that we believe all CFML engines should let you write the following:
public string function greet(string name, string greeting = "Hello") {
return greeting & " " & name;
}
The access and types can all be omitted:
function greet(name, greeting = "Hello") {
return greeting & " " & name;
}
That's equivalent to:
public any function greet(any name, any greeting = "Hello") {
return greeting & " " & name;
}
We also decided that arguments should be optional - which is a change to current behavior (but one that will not break any existing, working code which is currently passing all declared arguments). In order to specify an argument is required, we decided to add required:
public string function greet(required string name, string greeting = "Hello") {
return greeting & " " & name;
}
This is one of many changes we've agreed should be in core CFML. We hope you like it!

Tags: cfml-advisory · coldfusion

26 responses

  • 1 Dan Vega // May 8, 2009 at 7:27 PM

    Sean - The one thing that has always bugged me about functions is the default value for output. Every single function I define I need to add output="false" to make sure no output is generated. I would say 98% of the time my functions do not produce output. I would like to see this become the default. That coupled with the new script syntax will stand to save me a ton of time! Just my 2c
  • 2 Aaron Greenlee // May 8, 2009 at 8:31 PM

    Exciting.
  • 3 Russ // May 8, 2009 at 8:52 PM

    Whoa, I'm really looking forward to that. It will really enhance code readability for long function & argument declarations.

    Now how about adding support for component declarations inside cfscript?

    Here's another idea: implement a specific file extension that compiles cfscript so we don't have to wrap everything in cfscript tags. Maybe you could name it ".cfs" (ColdFusion Script)? (I'm not the first one to suggest this idea)
  • 4 Sean Corfield // May 8, 2009 at 11:29 PM

    @Dan, functions in CFSCRIPT don't generate output unless you use writeOutput() - they are effectively output="false".
  • 5 Sean Corfield // May 8, 2009 at 11:35 PM

    @Russ, the committee is already ahead of you! We decided that:

    component { ... }

    should be core CFML, as should:

    interface { ... }

    So you would be able to write:

    component extends="Base" implements="ICommand" { ... }

    As for omitting the cfscript tags, we decided that is extended core, i.e., it's optional for vendors (to allow the tags to be omitted within a .cfc file), but if they support it, we are specifying how it should work - when those tags can be omitted and when they are still required.
  • 6 Nathan Strutz // May 8, 2009 at 11:47 PM

    Sean,
    Yes, I do like it. The ability to retain all my existing code, as well as the ability to be more specific. It sounds great. I can almost see cfscript becoming my dream language. Hard to say exactly what's missing, probably the same as all the other guys - closures, multiple inheritance, a better organized library, a more forgiving compiler, and on and on forever. For this, though, I like it.

    Dan - I concur, but that would break some existing applications, and how could you specify output="sort of"? ;) Remember:
    output=no/false = output is discarded once the method returns or completes
    no output attribute = yes, but just plain text, use your own cfoutput tags if you have anything #dynamic#
    output=yes/true = acts like your method has cfoutput tags wrapped around it.
  • 7 Sean Corfield // May 9, 2009 at 10:42 AM

    @Nathan, it won't ever get multiple inheritance. I was on the C++ committee for eight years and when you add multiple inheritance, there are a lot of complications that come with which we had to figure out. This is one area where Java got it right!

    Also note my response to Dan: CFSCRIPT is already output="no" because you have to explicitly call writeOutput() to produce anything.
  • 8 Russ // May 9, 2009 at 5:32 PM

    One question about the new function declaration specs: will you be able to declare them on separate lines?
    Oh, and about function calls... will you be able to define arguments explicitly?
  • 9 Ben Pate // May 9, 2009 at 7:47 PM

    Hey Sean,

    Thanks for expanding on this. From all the comments, it looks like there's a lot of interest in seeing CFSCRIPT bloom.

    I totally agree with Nathan's "dream language" statement. I know CFSCRIPT has been a second-class citizen for a while, so I'm glad that the committee is moving forwards with things like this that make it an equal partner with markup. Thanks!

    -- Ben
  • 10 Sean Corfield // May 10, 2009 at 1:10 AM

    @Russ, not sure what you mean. What have lines got to do with it? Also, what do you mean about defining arguments explicitly?

    @Ben, glad you like the changes the committee has approved.
  • 11 David C-L // May 10, 2009 at 6:37 AM

    What about package access? Is that the same as "protected"? Why the change in terminology?
  • 12 marc esher // May 10, 2009 at 7:22 AM

    I'd say that CF already handles the multiple inheritance problem, via mixins. this is how functional languages like scala and even javascript deal with it, and mixins with CF are quite easy.

    thanks for the update, Sean.
  • 13 Sean Corfield // May 10, 2009 at 9:46 AM

    @David C-L, a typo on my part. Fixed.
  • 14 Sean Corfield // May 10, 2009 at 9:48 AM

    @Marc, yes, mixins are a good alternative to multiple inheritance for dynamic languages. Just to be clear that mixins and multiple inheritance are very different - and mixins completely side-step many of the problems that come with multiple inheritance.
  • 15 Barney Boisvert // May 10, 2009 at 10:05 AM

    What's the logic behind making arguments non-required? I really dislike the tri-state nature of the required/default pair of settings. It seems like the "standard" behaviour in other languages is for non-defaulted arguments to be required, and defaulted arguments to be optional. Things get a little dicey with CFML because it lacks a null equivalent, but that's a whole other bag of worms.

    In any case, if the tri-state behaviour carries through, it should be an explicit part of the spec that supplying a default to a 'required' argument is a syntax error (and if you ask me, CFARGUMENT should error on a default attribute if the required attribute is set to true as well).
  • 16 Sean Corfield // May 10, 2009 at 10:30 AM

    @Barney, our options for a purely optional arg were:

    SomeType argName required="false"

    optional SomeType argName

    Given that the following makes an argument optional as well, neither of the above seemed like good choices:

    SomeType argName = defaultValue

    Also, given the amount of code out there today that does this:

    <cfargument name="argName" type="SomeType" required="true" default="defaultValue" />

    We did not want the script equivalent to be illegal:

    required SomeType argName = defaultValue

    (and we clearly could not make the cfargument format illegal due to backward compatibility)

    Unfortunately, many of the past design decisions for CFML tie our hands to some extent since we cannot make arbitrary working code illegal and we have to consider consistency between new features and that existing code. I think most people know how I feel about 'private' access and the whole 'this' / 'variables' mess. There's a lot of things I'd change if we were designing CFML from scratch - but we're not.
  • 17 Elliott // May 10, 2009 at 11:49 PM

    It seems like making arguments optional is going to make debugging code harder and mean you need to add "required" to almost every argument to every function.

    required arguments are far more common than optional ones, so shouldn't the common case be less verbose?

    If we look through the code in most frameworks the arguments are mostly required, not optional.

    Now instead of getting an "Argument X is required for function Z" from most library code you'll probably get "Variabke X is undefined" or no error at all and have to go check the method signature, especially since stack traces don't have argument lists...

    This really doesn't make sense to me.

    function setX( string X ) {
    variables.X = X;
    }

    This function does not do what most people expect it to do if called without arguments. It will NOT error, and will just reassign X to the original value already in the variables scope!

    So now we need to add "required" to basically everything.
  • 18 Sean Corfield // May 11, 2009 at 10:30 AM

    @Elliott, the committee carefully considered this and, FWIW, Adobe were the main advocate of the change. The key in this case was to provide the most usable syntax without breaking existing code.

    As to the error you'd get from your setX() function, try it in tag syntax today - with <cfargument name="X" required="false"> - and that's what you should expect to get in the script version.

    Also, bear in mind that in JavaScript, function arguments are all optional and JavaScript is considered one of the models for CFSCRIPT.
  • 19 Henry Ho // May 11, 2009 at 11:07 AM

    Can 'return' be optional, like Groovy? (just return whatever on the last line)

    Save some typing. :)
  • 20 Sean Corfield // May 11, 2009 at 11:25 AM

    @Henry, even after using Groovy extensively for about a year I'm still on the fence about that one. It makes one line methods very compact but it looks odd for multi-line methods.
  • 21 Elliott // May 11, 2009 at 12:32 PM

    @Sean

    There would be no error from the setX() function, that was my point. ;)

    While I still think this is a bad idea, fair enough with the argument about JavaScript. I suppose that makes sense.
  • 22 Sean Corfield // May 11, 2009 at 12:51 PM

    @Elliott, did you actually try it? Both Adobe CF8 and Railo give:

    Variable X is undefined

    on the assignment.
  • 23 Elliott // May 11, 2009 at 6:02 PM

    @Sean

    Did you actually read my post? ;P

    <cfset variables.X = 1>
    <cfset setX()>

    There will be no error. Try it.

    Re my statement: "It will NOT error, and will just reassign X to ***the original value already in the variables scope!***"
  • 24 Sean Corfield // May 11, 2009 at 6:37 PM

    Ah, gotcha.

    Well, that's an advert for using ARGUMENTS.X instead I suppose (when you *do* get the expected errors) :)

    I'm actually a bit surprised that the unadorned lookup of X doesn't find the declared-but-not-actually-present X in arguments scope (since structCount(arguments) and arrayLen(arguments) are both 1 and cfdump shows X is present in arguments scope but just null). An interesting side effect of yet another peculiarity of CFML variable lookup... :(
  • 25 Barney Boisvert // May 11, 2009 at 10:33 PM

    Re: your reply to my previous comment, have you guys considered a two-layer spec? Same sort of deal as MySQL's ANSI mode, for example. Railo has a number of settings like this as well. Seems like that sort of thing could be a good fit for a CFML spec since historically CFML is quite broken, but there is significant value to making it not broken. Vendors can pick one, the other, or both modes to support, and be able to advertise that fact. If I've got legacy apps, "old" mode is going to be attractive, but if I'm developing from scratch (or coming from a different language), "sane" mode will be what I want.
  • 26 Sean Corfield // May 12, 2009 at 9:15 AM

    @Barney, the spec has 'core', 'extended core' and 'vendor specific' - see the website http://opencfml.org/