Skip to content

Function overloading (and enforcing number of parameters) #2005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
behnam opened this issue Jan 4, 2012 · 13 comments
Closed

Function overloading (and enforcing number of parameters) #2005

behnam opened this issue Jan 4, 2012 · 13 comments

Comments

@behnam
Copy link

behnam commented Jan 4, 2012

Function (or method) overloading has been discussed before (issue #531), and the reason for not implementing it was the lack of a design that does not add to the complexity and/or confuse the users. Thus, I would like to suggest the following syntax and conversions:

func = *> (
    (a) ->
        'one'
    (a, b) ->
        'two'
    (a...) ->
        'many'
)

The new *> operator says that a list of functions will follow, which:

  • no two of them can have the same number of parameters; and
  • at most one of the parameters can have a splatted parameter.

Knowing the number of formal arguments, the compiler generates the following javascript:

var func = function() {
    switch (arguments.length) {
        case 1: return (function(a) {
                return 'one';
            }).apply(this, arguments);

        case 2: return (function(a, b) {
                return 'two';
            }).apply(this, arguments);

        default: return (function() {
                var a;
                a = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
                return 'many';
            }).apply(this, arguments);
    }
}

When there's no function with splatted arguments, the default case shall throw an exception.

And an additional feature could be to ban using arguments inside these functions (who needs arguments when there are splatted parameters?!), thus the number of arguments passed to the overloaded function can be verified on compile-time and otherwise a compile-error will be generated.

Having this feature also allows user to enforce the number of arguments passed to a function and get a compile-error otherwise. As most of such functions would not need overloading, we can also allow *> Code in syntax, making it possible to apply the enforcement easily as follows.

func = *> (a) ->
    'one'

It worths noting that this feature cannot be implemented in javascript that easily, as the number of formal parameters of the functions are not known before they are actually running (otherwise, we could implement this features using a couple of helper functions!). Thus, having this features, and the fact that we can catch the errors on compile-time, would be a big improvement to coffeescript.

@jashkenas
Copy link
Owner

One of CoffeeScript's strong policies (perhaps the strongest) is to never add features that would make a CoffeeScript library impossible to interact with seamlessly from JavaScript. Unfortunately, this proposal, as lovely as it is, is just that. These special operators would be inaccessible as operators from JS.

Great material for your own forked version of the compiler though, if you'd like.

@michaelficarra
Copy link
Collaborator

I don't think function overloading is a common or recommended pattern in JS. And how hard is it to do without this special syntax?

func = ->
  do switch arguments.length
    when 1 then (a) -> 'one'
    when 2 then (a, b) -> 'two'
    else (a...) -> 'many'

Not that hard.

@behnam
Copy link
Author

behnam commented Jan 4, 2012

Thanks @jashkenas for the quick response, but I don't see any feature that is not "accessible" from JavaScript. The *> operator will create a wrapper for the original function(s), and the wrapper is accessible the same from CS and JS, and the wrapped functions are not accessible the same, again. Would you explain more please?

@jashkenas jashkenas reopened this Jan 4, 2012
@jashkenas
Copy link
Owner

Whoops, in my haste, I misunderstood totally. I thought you were defining a *> overloaded operator. Reopened.

@TrevorBurnham
Copy link
Collaborator

You might want to look at my proposal at #1091, which I think more elegantly addresses common variable-arg-length use cases. You don't usually want completely different functions to run depending on whether you receive one argument or two; but you do want callbacks to always come at the end, so you might write

watch = (file, options?, callback) ->

@michaelficarra
Copy link
Collaborator

@jashkenas: I don't get that argument. JS users can still use the functions, they just have to be specified differently. That's the whole point of CS: allowing users to specify common patterns in a nicer, shorter way.

@michaelficarra
Copy link
Collaborator

Agreed that @TrevorBurnham's #1091 proposal would be a much better addition to the language.

@behnam
Copy link
Author

behnam commented Jan 4, 2012

I agree that #1091 is better for handling optional parameters, and I think it can be implemented along-side this proposal.

But overloading is also used in cases that the type of the objects is different, based on the number of parameters. For example, having a bind function that can bind one pair of event-name/callback or an array of them, like bind(eventName, callback) and bind(pairs).

Also, another feature in this proposal is to be able to tell the compiler that the number of parameters are fixed, letting compiler catch some common error.

@michaelficarra
Copy link
Collaborator

Another alternative way to specify the same functionality, this time using a helper:

overload = (funcs) -> ->
  unless fn = funcs[l = arguments.length] or funcs.default
    throw new Error "overloaded function doesn't support #{l} arguments"
  fn.apply this, arguments

func = overload
  1: (a) -> 'one'
  2: (a, b) -> 'two'
  default: (a...) -> 'many'

@showell
Copy link

showell commented Jan 4, 2012

-1

I should admit that I'm biased against functions having multiple interfaces in the first place, but it's a common pattern in Javascript, so I think this proposal's motivation is sound.

My main objection is similar to what @michaelficarra says above. You can solve this problem without special syntax:

f = multimethod
  1: (a) ->
  2: (a, b) ->
  null: (args...) ->

You'd provide your own implementation of multimethod, which would allow you to customize fallthru handling as needed.

If CS had a little introspection into call signatures at runtime, you could DRY it up a bit:

f = multimethod [
  (a) ->
  (a, b) ->
  (args...) ->
]

In this case multimethod could automatically detect which function accepts the right number of arguments.

FWIW Python doesn't allow multiple functions signatures, but it does provide optional functional annotations, as well as other introspection on calling arguments:

http://www.python.org/dev/peps/pep-3107/

I don't think the Python concepts necessarily map to JS/CS, just throwing them out there as food for thought.

P.S. Allow me to do some preemptive bikeshedding. If CS must provide compile-time support for multi-signature methods, can we do with a keyword instead of an operator? I think "*>" is ugly, and I think multiple methods should be rare enough that typing out a keyword shouldn't be too onerous.

(Minor edit: snipped line relating to async waterfalls.)

@satyr
Copy link
Collaborator

satyr commented Jan 4, 2012

ClojureScript ftw?

@steelbrain
Copy link

Open since "Jan 4, 2012". Oo Lala
+1

@michaelficarra
Copy link
Collaborator

@steelbrain I'll fix that for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants