Functions

So far every program has run from top to bottom, once. A function is a piece of program with a name, which you can run whenever you like, as often as you like, with different values each time. Functions are how programs avoid saying the same thing twice.

Making your own

Here is a function called double. It takes a number and gives back twice that number:

function double(n: 0) {
  return n * 2
}

preview.append(`Double 21 is ${double(21)}.`)

test 'double doubles' {
  expect(double(21)).toBe(42)
  expect(double(0)).toBe(0)
}

Read it a line at a time:

What : 0 means

The : 0 after n says what kind of value n should be, by giving an example of one: 0 is a whole number, so double expects a whole number. The example is not a default and not a special value; any whole number will do.

Because TJS knows what double expects, it checks every call. Try these at the console's prompt:

function double(n: 0) {
  return n * 2
}

console.log('Try double(5), then double(2.5), then double("ten").')

double(5) is 10. double(2.5) and double("ten") do not run the function at all: the answer is an error saying what was expected and what arrived, instead of a nonsense result that would cause trouble later. If you want a function that takes any number, fractions included, give it the example 0.0 instead.

Several parameters

A function can take more than one value. Separate the parameters with commas, and give the values in the same order when you call it:

function area(width: 0, height: 0) {
  return width * height
}

preview.append(`A 6 by 7 rectangle has an area of ${area(6, 7)}.`)

test 'area multiplies' {
  expect(area(6, 7)).toBe(42)
  expect(area(1, 1)).toBe(1)
}

Words in, words out

Parameters can be any kind of value. This one takes a name ('Ada' is the example, so the parameter is a string) and gives back a sentence:

function greet(name: 'Ada') {
  return `Hello, ${name}!`
}

preview.append(greet('Grace'))

test 'greet puts the name in the middle' {
  expect(greet('Grace')).toBe('Hello, Grace!')
}

The example is also a test

You can give an example for the answer too, after the parentheses:

function greet(name: 'Ada'): 'Hello, Ada!' {
  return `Hello, ${name}!`
}

preview.append(greet('Katherine'))

test 'greet works for other names too' {
  expect(greet('Grace')).toBe('Hello, Grace!')
}

The first line now reads: "given 'Ada', greet returns 'Hello, Ada!'". That documents the function, and it is a test as well: TJS runs greet('Ada') and checks the answer. The result line counts it beside the test below. Change 'Hello, Ada!' to 'Hi, Ada!' and the line turns red, saying greet('Ada') actually returned 'Hello, Ada!'.

Values that can be left out

Write = instead of : to give a parameter a default: the value it gets when the caller leaves it out.

function greet(name = 'friend') {
  return `Hello, ${name}!`
}

preview.append(greet(), ' ', greet('Ada'))

test 'a default fills the gap' {
  expect(greet()).toBe('Hello, friend!')
  expect(greet('Ada')).toBe('Hello, Ada!')
}

: means "you must give me one of these"; = means "you may, and here is what I use if you don't". The default is an example too, so greet still only takes strings.

Try it

Write a function minutes(hours: 0) that gives back how many minutes are in that many hours. Show a sentence using it, and add a test that checks minutes(2) is 120. Then give it a second parameter, extraMinutes = 0, so that minutes(1, 30) is 90, and test that too.