Understanding Generic Functions

# John Resig (17 years ago)

Hey All -

So I've been trying to understand generic functions (and their presumed use for Multimethods). Reading through the white paper and the wiki I can surmise the following (and since the RI doesn't include them yet, these are just assumptions):

Generic functions are used like this:

generic function a(b); generic function a(b:int){} generic function a(b:string){}

Generics offer a point upon which future functions can be bound, like for operator overloading:

class Foo!{}

generic intrinsic function +(a:string, b:Foo){} generic intrinsic function +(a:Foo, b:string){} generic intrinsic function +(a:Foo, b:Foo){}

So that's all well-and-good. Now, where I making an assumption is that it's not possible to do the following:

generic function a(b); generic function a(b:int){} generic function a(b:string){}

generic function a(b,c); generic function a(b:string, c:int){}

also, it's not clear if you can use generics for constructors - so I'm assuming that that's also not possible:

class Foo { generic function Foo(b); generic function Foo(b:int){} generic function Foo(b:string){} }

Ok - with all of that assumed, I am seriously struggling to think of a real-world use case for generics beyond the compelling operator overloading example.

Assuming that I wanted to continue to try to do method overloading, it sounds like the de-facto solution is to just use the rest arguments to figure out what I want. This is not an acceptable solution. I lose virtually all of the benefits that I had of doing type annotations in the first place if I can't actually use them on "overloaded" functions.

For example, I've been pouring through my JavaScript library (jQuery) looking for ways in which ES4 could be of benefit. I immediately looked to using (what I thought was) method overloading to ease some of the severe complexity of the library. We do overloading on virtually every single method: Thus, if there was no form of method overloading included in ES4, then jQuery would receive significantly less, tangible, benefit from these updates.

To give a couple, crude, examples:

function attr(name : string) : string { // get attribute value }

function attr(name : string, value : (string,int)) : jQuery { // set an attribute value, return a jQuery object }

function removeEvent() : jQuery { // Remove all events for each ( var type in types ) removeEvent( type ); }

function removeEvent(type : string) : jQuery { // Remove all events of a specific type for each ( var fn in events[type] ) removeEvent( type, fn ); }

function removeEvent(type : string, fn: Callable) : jQuery { // Remove the event handler bound to a type }

Additionally, I've been working on building a DOM implementation to sit on top of the ES4 RI, but have hit some walls, especially with constructors. Thankfully, private/protected/etc. constructors will be implemented at some point (I'm looking forward to it) but I was kind of expecting the ability to do multiple constructors - and even the ability to mix private/protected/public constructors, for example:

class Foo { private function Foo(){ // Do initialization stuff } function Foo(name : string) { this(); this.name = name; } }

I'm simply most concerned about getting a useful version of method overloading and constructor overloading. I'd love to find out that I could use generics to achieve this, but I just don't have a way of determining that right now.

# Lars Hansen (17 years ago)

-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of John Resig Sent: 15. november 2007 02:27

Generic functions are used like this:

generic function a(b); generic function a(b:int){} generic function a(b:string){}

Yes.

Generics offer a point upon which future functions can be bound, like for operator overloading:

class Foo!{}

generic intrinsic function +(a:string, b:Foo){} generic intrinsic function +(a:Foo, b:string){} generic intrinsic function +(a:Foo, b:Foo){}

Yes.

So that's all well-and-good. Now, where I making an assumption is that it's not possible to do the following:

generic function a(b); generic function a(b:int){} generic function a(b:string){}

generic function a(b,c); generic function a(b:string, c:int){}

An early proposal had this. It became complicated. See below.

also, it's not clear if you can use generics for constructors

  • so I'm assuming that that's also not possible:

class Foo { generic function Foo(b); generic function Foo(b:int){} generic function Foo(b:string){} }

At the moment that is true, but there's no reason I'm aware of why that restriction can't be lifted, apart from issues of having to use the "settings" (the initializer list preceding the body) for nullable fields; allowances could presumably be made if the use cases were strong.

Ok - with all of that assumed, I am seriously struggling to think of a real-world use case for generics beyond the compelling operator overloading example.

Generic functions can be useful for adding type-dispatched functionality after the fact without creating facades/wrappers. The visitor pattern is one example that's repeated in the literature. I circulated a JSON approach recently based on generic functions, along these lines:

generic function toJSON(x);

generic function toJSON(x:Object) { for ( let n in obj ) if (obj.hasOwnProperty(n)) serialize(n, toJSON(obj[n])) }

generic function toJSON(x:string) { ... }

The key here is that the protocol for JSON conversion is defined outside the object, not inside the object, which can be a real advantage if you're hooking up to somebody else's code that you don't want to edit.

If you are happy in a class-based OO world and you control all your source code, you'll probably have limited use for generic functions, just like you'll have limited use for structural types. These features speak to different use cases having to do with evolutionary programming. (I'm working on a tutorial, which will probably not be finished this week).

Assuming that I wanted to continue to try to do method overloading, it sounds like the de-facto solution is to just use the rest arguments to figure out what I want. This is not an acceptable solution. I lose virtually all of the benefits that I had of doing type annotations in the first place if I can't actually use them on "overloaded" functions.

What you mean is, you want parameter lists of different lengths on the generic methods.

For example, I've been pouring through my JavaScript library (jQuery) looking for ways in which ES4 could be of benefit. I immediately looked to using (what I thought was) method overloading to ease some of the severe complexity of the library. We do overloading on virtually every single method: Thus, if there was no form of method overloading included in ES4, then jQuery would receive significantly less, tangible, benefit from these updates.

To give a couple, crude, examples:

function attr(name : string) : string { // get attribute value }

function attr(name : string, value : (string,int)) : jQuery { // set an attribute value, return a jQuery object }

function removeEvent() : jQuery { // Remove all events for each ( var type in types ) removeEvent( type ); }

function removeEvent(type : string) : jQuery { // Remove all events of a specific type for each ( var fn in events[type] ) removeEvent( type, fn ); }

function removeEvent(type : string, fn: Callable) : jQuery { // Remove the event handler bound to a type }

Additionally, I've been working on building a DOM implementation to sit on top of the ES4 RI, but have hit some walls, especially with constructors. Thankfully, private/protected/etc. constructors will be implemented at some point (I'm looking forward to it) but I was kind of expecting the ability to do multiple constructors - and even the ability to mix private/protected/public constructors, for example:

class Foo { private function Foo(){ // Do initialization stuff } function Foo(name : string) { this(); this.name = name; } }

I'm simply most concerned about getting a useful version of method overloading and constructor overloading. I'd love to find out that I could use generics to achieve this, but I just don't have a way of determining that right now.

As I wrote above, an early version of the generic function proposal supported multiple parameter list lengths and allowed generic functions to be instance methods. The proposal was complicated; I don't know if it was sound, and it probably would not have been accepted in that form.

If you don't care about extending a generic function outside the class it's defined in (excluding also extensions in the subclass) because you just want to implement overloaded functions then there are no problems with having generic functions be instance methods. There are also no problems with having generic constructors or meta:: functions.

What you suggest above with private/public constructors isn't expressible with generic functions, and I don't expect it ever will be. (Your example is not a strong use case given that you call the private constructor from the public one; the private constructor could just be a private method.)

Parameter lists of various lengths... I understand the use cases, and if there are no optional or rest arguments involved it's not actually all that hard. It's when you try to generalize that it becomes difficult.

IMO we have a couple of options:

  • remove generic functions from ES4 because they are too limited. Operator overloading goes away, too.
  • accept them as they are, recognizing that they are future-proof and that they can be extended in later editions of the language (as we've done for type parameterization)
  • try to extend them with instance methods, constructor methods, variable-length parameter lists without going over the complexity budget
# P T Withington (17 years ago)

On 2007-11-15, at 02:52 EST, Lars Hansen wrote:

[...]

Generic functions can be useful for adding type-dispatched
functionality after the fact without creating facades/wrappers.

My view is that generic functions recognize that any function with
more than one class parameter can't logically belong to any particular
class. They allow behaviors between classes to be described
logically, rather than being artificially attached to one of the
classes of their parameters.

Hey, can I say something like:

generic function get a (this: Foo) ?

[...]

IMO we have a couple of options:

  • remove generic functions from ES4 because they are too limited. Operator overloading goes away, too.
  • accept them as they are, recognizing that they are future-proof and that they can be extended in later editions of the language (as we've done for type parameterization)
  • try to extend them with insta

Is my mail client broken, or did you hit send too soon?

# Lars Hansen (17 years ago)

-----Original Message----- From: P T Withington [mailto:ptwithy at gmail.com] On Behalf Of P T Withington Sent: 15. november 2007 14:02 To: Lars Hansen Cc: John Resig; es4-discuss Subject: Re: Understanding Generic Functions

On 2007-11-15, at 02:52 EST, Lars Hansen wrote:

[...]

Generic functions can be useful for adding type-dispatched functionality after the fact without creating facades/wrappers.

My view is that generic functions recognize that any function with more than one class parameter can't logically belong to any particular class. They allow behaviors between classes to be described logically, rather than being artificially attached to one of the classes of their parameters.

Right, the binary method problem (even for larger values of "binary").

Hey, can I say something like:

generic function get a (this: Foo) ?

Not at the moment, but this came up briefly the other day, I forget if it was on this list or elsewhere, that maybe there is a global generic function meta::get (just like there is a global intrinsic::+), and if property lookup fails in an object the dispatch would go through that function. No decision on that (not even a ticket).

This discussion highlights the fact that generic functions sometimes feel a little bolted on, because there are already class protocols that do part of what generic functions do. We're really not trying to do a MOP for ES4, it's just that once we have generic function it's so easy to be tempted. "Just a little tweak here."

[...]

IMO we have a couple of options:

  • remove generic functions from ES4 because they are too limited. Operator overloading goes away, too.
  • accept them as they are, recognizing that they are future-proof and that they can be extended in later editions of the language (as we've done for type parameterization)
  • try to extend them with insta

Is my mail client broken, or did you hit send too soon?

The former, probably. The last bullet (and the end of the message) was:

  • try to extend them with instance methods, constructor methods, variable-length parameter lists without going over the complexity budget

(I'm adding this pointless sentence here in case the bug in your mail reader has to do with signature removal being thrown off by the bullet starting the line above, or something silly like that.)