Why can't objects be callable?

# Brandon Benvie (13 years ago)

I start this coming from the standpoint of an honest question that I don't know the answer to: is there a specific reason that objects can't be callable in js? Aside from the "that's just how the language is" answer, I was wondering if there's some other computer sciency or performance reason for the restriction.

I've had this question a number of times but the most recent spart was seeing this from Allen Wirfs-Brock:

//define a non constructible superclass that provides some Smalltalk-like conventions const AbstractClass = Function.prototype <| { subclassResponsibility() {throw new Error(this.name+" did not implemented an abstract method")}, shouldNotImplement() {throw new Error(this.name+" should not implemented by "+this.name)}, name: "AbstractClass", prototype: Object.prototype <|{ get class() {return this.constructor}, error(message) {throw new Error(message)}, subclassResponsibility() {return this.class.subclassResponsibility()}, shouldNotImplement() {return this.class.shouldNotImplement()}, errorSubscriptBounds(index) {this.error("subscript is out of bounds: "+index)} } };

Currently if you wish to callables that have a useful inheritance tree you have to use a function literal and then use __proto__ to manually set it up. Presumably this is fixed one way or another with ES6 by whatever thing is decided on to implement classes but it still brings up the fundamental question for me.

As it is currently, the behavior of a function is that it defaults to be a constructor-like. It has a prototype that it bestows upon objects JS tells it to create, and it's not even possible for it to have no prototype property. I would imagine a callable object is more like a non-constructor function (something like isNaN) or, currently, it's essentially the same as doing object.valueOf(). In fact there's nothing stopping one from making every single object they use (aside from an array) callable simply by starting with a function literal and assigning its proto.

This is similar to how it's also currently impossible to make an arraylike that really acts like an array without using proto. In this case the answer to my initial question could be something like "the storage of an array's indexed properties in memory asserts specific requirements on its usage that are different from standard properties" or something like that. That doesn't seem to follow with a callable object though, since it has an affect only on something which is other wise not usable in JS (default functionality or whatever it would be called).

Partilly this is resolved with ES6, as is the arraylikes problem. But I still am left curious as to why an object, not just something with Function.prototype in its prototype tree, shouldn't be callable. JavaScript fashions functions to be constructors from the getgo but it seems to me that there's two almost completely separate types of things between constructors and the traditional concept of "function". On reflection is almost seems like in prototypal inheritance, calling an object is where the constructor should actually be, and functions should non-creational collections of instructions.

# David Bruant (13 years ago)

Le 07/03/2012 02:10, Brandon Benvie a écrit :

I start this coming from the standpoint of an honest question that I don't know the answer to: is there a specific reason that objects can't be callable in js? Aside from the "that's just how the language is" answer, I was wondering if there's some other computer sciency or performance reason for the restriction.

One further detailled explanation of "that's just how the language work" would be that a JavaScript invariant is that a value has a stable result to "typeof". So it's impossible to change a non-callable object into a callable object without breaking this invariant (as stated by ES5, a callable object has "function" for it's typeof value while non-callable objects have "object").

Besides this, I don't see a restriction on why you couldn't just use functions each time you need an object.

I've had this question a number of times but the most recent spart was seeing this from Allen Wirfs-Brock:

//define a non constructible superclass that provides some Smalltalk-like conventions const AbstractClass = Function.prototype <| { subclassResponsibility() {throw new Error(this.name+" did not implemented an abstract method")}, shouldNotImplement() {throw new Error(this.name+" should not implemented by "+this.name)}, name: "AbstractClass", prototype: Object.prototype <|{ get class() {return this.constructor}, error(message) {throw new Error(message)}, subclassResponsibility() {return this.class.subclassResponsibility()}, shouldNotImplement() {return this.class.shouldNotImplement()}, errorSubscriptBounds(index) {this.error("subscript is out of bounds: "+index)} } }; As it is currently, the behavior of a function is that it defaults to be a constructor-like. It has a prototype that it bestows upon objects JS tells it to create, and it's not even possible for it to have no prototype property. I would imagine a callable object is more like a non-constructor function (something like isNaN) or, currently, it's essentially the same as doing object.valueOf(). In fact there's nothing stopping one from making every single object they use (aside from an array) callable simply by starting with a function literal and assigning its proto.

However, you do not need to use functions as constructors. I wish block lambda [1] to be adopted for ES6. I think they would fit your vision of functions, because it certainly wouldn't make sense to construct with them and they would certainly have no use of a 'prototype' property.

Hopefully, it will be possible to combine block-lambda with the <| operator to create callable objects with arbitrary prototype.

(...) Partilly this is resolved with ES6, as is the arraylikes problem.

Indeed. All the "change the prototype at object creation time" was the rational behind the <| operator (and as you note, it will work for arrays, and functions and most things)

David

[1] strawman:block_lambda_revival

# Russell Leggett (13 years ago)

On Wed, Mar 7, 2012 at 2:58 AM, David Bruant <bruant.d at gmail.com> wrote:

Le 07/03/2012 02:10, Brandon Benvie a écrit :

I start this coming from the standpoint of an honest question that I don't know the answer to: is there a specific reason that objects can't be callable in js? Aside from the "that's just how the language is" answer, I was wondering if there's some other computer sciency or performance reason for the restriction. One further detailled explanation of "that's just how the language work" would be that a JavaScript invariant is that a value has a stable result to "typeof". So it's impossible to change a non-callable object into a callable object without breaking this invariant (as stated by ES5, a callable object has "function" for it's typeof value while non-callable objects have "object").

Scala has a notion of "extractor" objects: www.scala-lang.org/node/112 – with the right method signatures, they can be used like a function, or be used in pattern matching (or for us, destructuring only - for now). I can see how this would be useful, and could be achieved using private names instead of method signatures. The result would be an object that could still be typeof "object" and also callable.

import {apply,unapply} from "@extractor";

const Twice = {
    [apply]:function(x){
        return x * 2;
    },
    [unapply]:function(z){
        return z%2 == 0? z/2 : null;
    }
}

let x = Twice(21);    // x == 42
let Twice(n) = x; // n == 21

I think the unapply half could be useful in destructuring, though I think it would be distinctly more useful in pattern matching (which I would love to see included). Or the unapply half could just be dropped if too problematic.

Besides this, I don't see a restriction on why you couldn't just use functions each time you need an object.

The biggest restriction would probably just be forcing the way you have to create it. It has to start life as a function and get modified from there.

# Dean Landolt (13 years ago)

On Wed, Mar 7, 2012 at 8:53 AM, Russell Leggett <russell.leggett at gmail.com>wrote:

On Wed, Mar 7, 2012 at 2:58 AM, David Bruant <bruant.d at gmail.com> wrote:

Le 07/03/2012 02:10, Brandon Benvie a écrit :

I start this coming from the standpoint of an honest question that I don't know the answer to: is there a specific reason that objects can't be callable in js? Aside from the "that's just how the language is" answer, I was wondering if there's some other computer sciency or performance reason for the restriction. One further detailled explanation of "that's just how the language work" would be that a JavaScript invariant is that a value has a stable result to "typeof". So it's impossible to change a non-callable object into a callable object without breaking this invariant (as stated by ES5, a callable object has "function" for it's typeof value while non-callable objects have "object").

Scala has a notion of "extractor" objects: www.scala-lang.org/node/112 – with the right method signatures, they can be used like a function, or be used in pattern matching (or for us, destructuring only - for now). I can see how this would be useful, and could be achieved using private names instead of method signatures. The result would be an object that could still be typeof "object" and also callable.

import {apply,unapply} from "@extractor";

const Twice = {
    [apply]:function(x){
        return x * 2;
    },
    [unapply]:function(z){
        return z%2 == 0? z/2 : null;
    }
}

let x = Twice(21);    // x == 42
let Twice(n) = x; // n == 21

I think the unapply half could be useful in destructuring, though I think it would be distinctly more useful in pattern matching (which I would love to see included). Or the unapply half could just be dropped if too problematic.

<3

Of course, we'd need a completely different pattern for duck-testing callables. If functions were re-specified to include the apply private name then testing for its presence may be enough. This would fall down in the face of naive iframe-sandboxing -- is this a big problem? If so, at worst sandboxes could use a proxy to alias the various apply brands (I suspect there's a lighter weight strategy that could work too -- I only say this to note the benefits of using brands for psuedo-typing).

# Russell Leggett (13 years ago)

Scala has a notion of "extractor" objects: www.scala-lang.org/node/112 – with the right method signatures, they can be used like a function, or be used in pattern matching (or for us, destructuring only - for now). I can see how this would be useful, and could be achieved using private names instead of method signatures. The result would be an object that could still be typeof "object" and also callable.

import {apply,unapply} from "@extractor";

const Twice = {
    [apply]:function(x){
        return x * 2;
    },
    [unapply]:function(z){
        return z%2 == 0? z/2 : null;
    }
}

let x = Twice(21);    // x == 42
let Twice(n) = x; // n == 21

I think the unapply half could be useful in destructuring, though I think it would be distinctly more useful in pattern matching (which I would love to see included). Or the unapply half could just be dropped if too problematic.

<3

Of course, we'd need a completely different pattern for duck-testing callables. If functions were re-specified to include the apply private name then testing for its presence may be enough. This would fall down in the face of naive iframe-sandboxing -- is this a big problem? If so, at worst sandboxes could use a proxy to alias the various apply brands (I suspect there's a lighter weight strategy that could work too -- I only say this to note the benefits of using brands for psuedo-typing).

It could just be something as simple as Object.isCallable or Function.isCallable, though I agree it would be nice if Function used it as well - that would go along with the idea of putting intrinsic builtin stuff in privates like with Date.

# Brendan Eich (13 years ago)

Russell Leggett wrote:

import {apply,unapply} from "@extractor";
const Twice = {
    [apply]:function(x){
        return x * 2;
    },

This would have made pre-Proxies Narcissus a piece of cake. Instead I had to extend SpiderMonkey (call, construct, etc.

# David Bruant (13 years ago)

Le 07/03/2012 14:53, Russell Leggett a écrit :

On Wed, Mar 7, 2012 at 2:58 AM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:

Besides this, I don't see a restriction on why you couldn't just use
functions each time you need an object.

The biggest restriction would probably just be forcing the way you have to create it. It has to start life as a function and get modified from there.

True. This restriction applies to every object "form". For instance, arrays have to start their lives as array as well (with the special "length" and numeric properties relationship). Same goes for DOM nodes. It's not possible for a regular object to get the internal magics of a NodeList.