website is under construction
Language

Functions

Functions are first-class values in Ghost. That means they can be stored in variables, passed as arguments to other functions, and returned as results. This gives great flexibility to the language.

Defining Functions

You define functions using the function statement, followed by a list of parameters, and a body:

function sum(a, b) {
  console.log(a + b)
}

The body of a function is always a block. Inside it, you can return a value using a return statement.

function sum(a, b) {
  return a + b
}

Calling Functions

Once you have a function, calling it is as simple as passing the required parameters along with the function name:

value = sum(1, 2)

The assigned value is whatever the function's return statement produced. A function that falls off the end of its body without returning gives back null — the last expression in a body is not an implicit return value.

Default Parameters

A parameter may declare a default value, used when the caller leaves that argument out:

function greet(name, greeting = "Hello") {
  return `${greeting}, ${name}`
}

console.log(greet("Ghost"))       // >> Hello, Ghost
console.log(greet("Ghost", "Hi")) // >> Hi, Ghost

The default expression is evaluated when the function is called, not when it is defined, and it is evaluated in the function's own scope — so a default can reference an earlier parameter or a name from the enclosing scope.

Argument Count

A function has a minimum but no maximum. Leaving a required parameter unbound is an argument error naming the call:

function sum(a, b) {
  return a + b
}

sum(1)
// argument error: `sum()` expects at least 2 arguments, got 1

The error is raised before the body runs, so a miscall never half-executes, and no call frame is added to the report — the call never got started.

Extra arguments, on the other hand, are simply dropped:

console.log(sum(1, 2, 3, 4)) // >> 3

That is what lets a function name only the parameters its body actually uses. A map() callback is handed both the element and its index, but a callback that only wants the element can just say so:

console.log([1, 2, 3].map(function (item) {
  return item * 2
}))

// >> [2, 4, 6]

A parameter with a default is optional and does not count toward the minimum. A rest parameter collects whatever is left and does not count either.

Standard library functions are stricter: they enforce a maximum as well, so math.sqrt(16, 2) is an argument error rather than a silently ignored second argument. A library call has one known shape; a function you write is often a callback whose caller decides what to pass.

Rest Parameters

The last parameter may be written ...name, which collects every remaining argument into a list:

function sum(...numbers) {
  total = 0

  for (n in numbers) {
    total = total + n
  }

  return total
}

console.log(sum(1, 2, 3)) // >> 6
console.log(sum())        // >> 0

It is always a list, even when there was nothing left to collect — an empty one, never null. A rest parameter cannot have a default, since it is already optional, and writing ... anywhere but last is a syntax error.

Ordinary parameters may come before it:

function tag(label, ...values) {
  return `${label}: ${values.join(", ")}`
}

console.log(tag("scores", 1, 2, 3)) // >> scores: 1, 2, 3

Spread

... in front of a list at a call site expands it into separate arguments:

numbers = [1, 2, 3]

console.log(sum(...numbers))     // >> 6
console.log(sum(0, ...numbers))  // >> 6

The same works inside a list literal, which is the tidy way to build one list out of others:

console.log([0, ...[1, 2], 3]) // >> [0, 1, 2, 3]

What you spread has to be a list; spreading anything else is a type error. ... is only meaningful in those two places — written anywhere else, such as x = ...list, it is a syntax error rather than a value.

Anonymous Functions

Functions are first class in Ghost, which just means they are real values that you can get a reference to, store in variables, pass around, etc.

function addPair(a, b) {
  return a + b
}

function identity(a) {
  return a
}

console.log(identity(addPair)(1, 2)) // >> 3

Leaving the name off gives you a function value with nowhere to live but the expression it's in:

square = function (x) {
  return x * x
}

console.log(square(4)) // >> 16

Since function declarations are statements, you can declare local functions inside another function:

function outerFunction() {
  function localFunction() {
    console.log("I'm local!")
  }

  localFunction() // >> I'm local!
}

Closures

A function keeps hold of the scope it was defined in, so a function returned from another function can still see that function's variables:

function returnFunction() {
  outside = "outside"

  function inner() {
    console.log(outside)
  }

  return inner
}

newFunction = returnFunction()
newFunction() // >> outside

Note that a closure can read those variables but assigning to one inside the inner function creates a local instead. See Variables.