What do you call a `function` function?

# T.J. Crowder (5 months ago)

Bit of a silly one, but begging the list's indulgence:

I routinely explain various JavaScript topics to learners, including arrow functions, method syntax, etc. When I want to contrast "arrow function" (for instance) with functions defined with function, it trips me up, and often I end up saying/writing something awful like "function function". I've considered using "normal function" instead, but arrow functions are normal in today's world, as are functions defined with method syntax (although I'd usually call them methods), so it's...unsatisfying.

But function function is just so clumsy. And a pedant (none of those here, surely!) could argue the definition (are generators function functions? they're defined with function [when you're not using generator method syntax], it just has a * after it).

I've also considered "old-style function," but function functions still have a place in today's JavaScript, just not as prominent a place as they used to.

A recent post to the list used "conventional function," but it may well have the same problems "normal function" does.

My goal is to be clear, and reasonably accurate, without being overly pedantic.

Any ideas? Should I just stop worrying and learn to love "normal function"? Is there a better term?

Thanks in advance, folks.

-- T.J. Crowder

# Eli Perelman (5 months ago)

I've always referenced them as:

Function declarations:

function a() {}

Function expression:

const a = function() {}

Named function expression:

const b = function a() {}

Arrow function:

const a = () => {}

Not sure it's 100% semantic or descriptive, but it's how I've differentiated.

Eli Perelman

# Naveen Chawla (5 months ago)

"function" function is the best out of all of the alternatives you mentioned.

"Anonymous function declared with the function keyword" if it's not too wordy.

# C. Scott Ananian (5 months ago)

"keyword function" might not be too bad, either...

# Isiah Meadows (5 months ago)

I just call them "regular functions", "function declarations", or similar. I'll add "arrow" if there's an arrow, and "async" if there's an async keyword.

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Darien Valentine (5 months ago)

I usually say "function function," but yeah ... it’s painful haha. I sometimes work with devs whose experience with ES functions is limited to methods and arrow functions, so "normal function" doesn’t really fly anymore. I’ve considered "classic function," but that probably wouldn’t help much.

I have no good answer to the root question here, but I’ve thought about this a bit. There seem to be many ways we could categorize and name "types of functions" in ES and the best choices probably vary with context / audience.

1. Naming after syntactic productions

The ES spec includes the following relevant named syntactic productions. So the nice thing about using a taxonomy based on syntax is that it minimizes subjective factors and matches up fairly well with how many people think about code:

  • ArrowFunction
  • AsyncArrowFunction
  • AsyncFunctionDeclaration
  • AsyncFunctionExpression
  • AsyncGeneratorDeclaration
  • AsyncGeneratorExpression
  • ClassDeclaration
  • ClassExpression
  • FunctionDeclaration
  • FunctionExpression
  • GeneratorDeclaration
  • GeneratorExpression
  • MethodDefinition

The ClassDeclaration and ClassExpression ones are a bit fuzzy, since an explicit constructor is a MethodDefinition syntactically, but ClassDeclarations and ClassExpressions without explicit constructor MethodDeclarations do still create functions. In any case, the fact that constructor is a MethodDeclaration is an example of a shortcoming of this approach: a constructor is specifically not usable as a method in the sense that we usually mean, so you really feel the "these are just names for the grammar" factor there.

Another concern might be that syntax is not the only means by which functions are created, so it we don’t get a corresponding name for everything. Use of the four Function constructors, bound function exotic objects, and Proxies are also avenues by which new callable/constructable objects can be created, but they have no syntactic expression, so they might be awkward to categorize along these lines.

In this taxonomy, the name would be "function declaration" or "function expression". That doesn’t actually address the ambiguity problem around unless everybody is on the same page, so I think this angle is not very good.

2. Functions by class

All function objects (or at least all non-"extrinsic" ones) will report themselves as instances of one of these four corresponding constructors from their Realm:

  • %AsyncFunction% ((async function() {}).constructor)
  • %AsyncGeneratorFunction% ((async function * () {}).constructor)
  • %Function% (Function)
  • %GeneratorFunction% ((function * () {}).constructor)

In this taxonomy, we get to place every (or very nearly every) function into a category without trouble. On one hand, these are the most dramatic "function type" distinctions we can draw, so it isn’t-not-useful — but it doesn’t give us a way to talk about distinctions between functions created with class syntax, arrow syntax, etc, so it’s also a nonstarter.

3. What constitutes a type of function

I’m figuring that underlying the communication problem is a different issue: what constitutes a "type of function" in ES may not really have a simple and objective answer.

For example, from a syntactic POV, FunctionDeclaration and FunctionExpression are different in several ways, like how their names may be derived, what/whether bindings are created and in what scope, and whether hoisting takes place. But looked at as values (that is, considering just the function objects that the syntax creates), these two forms end up exhibiting no observable behavioral differences. Given that some functions do possess contrasting semantics that are very observable, like async functions, can we really say a FunctionDeclaration and FunctionExpression are different "types" on the same order?

I think the best answer is no — they are, instead, different syntactic forms for describing what should be considered the same type of function. So let’s look at what behaviors of the functions themselves, rather than the syntax used, really can differ, because maybe there’s a better answer there.

4. Functions by semantics

The distinction between arrow functions and non-arrow functions (of various types) is that their [[ThisMode]] is "lexical" and the others are not — but the there are actually two other [[ThisMode]] values, "global" and "strict". It is not simply a binary distinction between lexical and non-lexical "this" because functions created in sloppy mode may resolve "this" to the global object and functions created in strict mode may not.

(Bear with me alright ^_^;)

Related to (but not directly corresponding to) the "functions by class" section, there is a [[FunctionKind]] slot. Its value may be "normal", "classConstructor", "generator", or "async". During function allocation, other similar values are transiently in the mix, like "non-constructor" (which prevents the allowance of a [[Construct]] method for an otherwise "normal" function), and "async generator" (which becomes "generator"); during function initialization there are also "Arrow", "Normal" and "Method". But I mention these only to clarify that they are used to set other properties (e.g. Arrow leads to setting [[ThisMode]] to "lexical") — they don’t remain "part" of the function like [[FunctionKind]] does.

So both the "classConstructor" and "normal" kinds correspond to %Function%, while both %AsyncGeneratorFunction% and %GeneratorFunction% correspond to "generator". Whew.

(I’ve probably got some details wrong there, but I think it’s mostly accurate.)

Two fundamental forms correspond to constructable functions:

  • class Foo {}, (class {})
  • function Foo() {}, (function() {}), new Function()

These types of functions obtain a prototype property when they are created.

In addition a bound function exotic object will be constructable if the function it proxies is constructable.

All other functions are not constructable — this is the distinction between function foo() {} and the method foo() {} (it’s not sugar). But async functions, generators, and async generators are never constructable in any of their syntactic forms — declarations, expressions/arrows, methods — so for those, method syntax is just sugar — or rather would be, if not for one last key slot, [[HomeObject]], which exists only for methods and affords them the ability to use super expressions. You know ... writing it all out like this makes it suddenly feel very complicated @_ at .

As for [[Call]], all functions possess this internal method, even classes; however, if the [[FunctionKind]] is "classConstructor", then an attempt to use [[Call]] will always throw, making it effectively unavailable. What this means in practice is that the unique property of function functions is that they are the only functions which can be both called and constructed (not counting black magic that makes other functions seem to have both capabilities).

Putting [[HomeObject]] (which doesn’t seem like a "type" issue so much) and bound function exotic objects (which are more or less proxies to other functions) aside, we could say there are three abstract semantic attributes that contribute significantly to what we as language users typically think of as "function type":

  • presence of [[Construct]]
  • value of [[FunctionKind]]
  • value of [[ThisMode]]

At the loss of some precision, we can conflate "global" and "strict" [[ThisMode]] since, admittedly, we don’t often think about this and they don’t correspond to any specific syntax. What we get in this taxonomic model then is a matrix with three dimensions. The first column is [[FunctionKind]], the second is whether [[Construct]] exists (the extra axis), and then horizontally we have [[ThisMode]]. Cells with "-" describe combinations that aren’t possible, while the others provide (non-exhaustive) examples of corresponding syntax.

(I’m not sure if ES discuss will let me include a GFM table, and I imagine some people look at this via email, so here’s an alternative link: gist.github.com/bathos/6bb1e5ef92d2c8f89363c0931d048caf)

FK Cstr TM: Lexical TM: Global / Strict
normal cstr - function() {}; new Function
normal no () => {} x() {}
classConstructor cstr - class {}
classConstructor no - -
generator cstr - -
generator no - function * () {}; * x() {}
async cstr - -
async no async () => {} async function() {}; async x() {}

If we do turn the non-empty cells into a list, we get seven types of functions. If we label them by the primary facets that make each semantically unique, we end up with a final taxonomy that would look something like this:

  • lexical function
  • async lexical function
  • callable constructor
  • method
  • constructor
  • generator / generator method
  • async function / async method

So circling back to the original question, I got "callable constructor"! (record scratch) Eh...

Okay, I guess that isn’t gonna catch on haha. It may more accurately reflect what makes function-keyword-functions unique, but given common usage patterns, it would be too confusing. Inversion could help a little ("constructable function"), but not enough.

Anyway, it’s just a little investigation. Nobody’s gonna start using these terms. But it was interesting to think about and you’re not alone in sometimes struggling to find the best terminology for different kinds of functions that ES developers with diverse experience levels all find clear.

# Raul-Sebastian Mihăilă (5 months ago)

Traditional function.

# T.J. Crowder (5 months ago)

Wow, thanks everyone for all the input!!

I woke up this morning with "classic function" in my head, and immediately worried about confusing people (particularly those for whom English is not their native tongue) with class, since it creates a function (the constructor).

So I'm currently leaning toward Raul-Sebastian's "traditional function" (thank you!) since it largely conveys the same meaning as "classic" without the class problem.

So taking a page from Darien's magnum opus :-) I get:

  • Traditional function (function declaration, function expression)
  • Constructor (for both class and function when the intent is to create a constructor; draw a distinction when necessary)
  • Method (some vagueness here, we used to use "method" to refer to traditional functions assigned as object properties, but of course, we got methods with discernable differences -- can't be constructors, can use super -- in ES2015)
  • Arrow function
  • async function
  • async arrow function
  • Generator function
  • async generator function

I think this mostly conveys useful meaning, without getting too pedantic.

Again, thank you everyone for the very useful responses!

-- T.J. Crowder