Guide to Verse Functions

This page describes the built-in functions you can use in your Verse programs.

It is divided into several main sections:

Each function is described using a regular format, consisting of:

Table of Contents

Effects


log

Usage

let message = 'Hello!'
yield log(message)

Explanation

Causes the message to appear at the bottom of the log output, the area at the top of Verse’s display. The log output is then scrolled so the new message is visible.

Log messages persist until the program is restarted, so they’re most useful when you want the user of your program to be able to scroll back through the history. The disadvantage of log (compared to startDisplay) is that once a message is logged it cannot be altered or deleted.

See Also

Example: Print a message forever

define({
  *run() {
    yield log('theendisnevertheendisnevertheendisnevertheend')
    yield wait(0.1)
    yield retry(run())
  }
})

retry

Usage

yield retry(routine())

Explanation

Aborts the current routine and starts the given routine in its place. You can pass parameters to the routine as you would when calling it directly.

As the name implies, retry is intended to be used to restart the current routine from the beginning. The examples below should clarify what I’m talking about.

Caveats

When the routine passed to retry returns, control goes back to the caller of the routine that called retry. When retry is used as its name implies, this behavior is intuitive. However, it’s also possible to use retry as a goto-like contruct, which gets much more confusing.

It is recommended to always pass the currently-executing routine to retry to avoid headaches.

Example: Validate user input

The code below gets a numeric digit (0-9) from the user and prints it out. It is very persistent: if you give it a keypress that’s not a number it retries until it gets one.

define({
  *run() {
    yield log('Enter a number.')
    let number = yield waitForNumber()
    yield log('Your number is: ' + number)
  },

  *waitForNumber() {
    let input = yield waitForChar()
    if (isNumber(parseInt(input))) {
      return parseInt(input)
    } else {
      yield log(input + ' is not a number!')
      yield log('Please press 0-9')
      yield retry(waitForNumber)
    }
  }
})

startDisplay

Usage

let view = function() {
  return [
    'This is line 1',
    'This is line 2'
  ]
}
yield startDisplay(view)

Explanation

Begins continuously displaying the lines returned by the view function on the screen. The return value of the view should be an array of strings.

Like all JavaScript functions, the view function can use variables defined in the same routine to construct its return value (e.g. the count variable in the examples below). The view function is re-evaluated frequently, ensuring that up-to-date information is displayed on screen.

When a routine calls startDisplay, it “covers up” the output of any previous startDisplay calls made by other routines. When a routine returns, it clears its startDisplay output and “uncovers” the previous startDisplay call (if any), allowing it to be shown again.

See Also

Example: Count to 10 with a loop

define({
  *run() {
    let count = 0
    yield startDisplay(function() {
      return [
        `The count is ${count}`
      ]
    })
    for (count of range(1, 10)) {
      yield wait(0.5)
    }
  }
})

Example: Count forever with retry

define({
  *run(count = 1) {
    yield startDisplay(function() {
      return [
        `The count is ${count}`
      ]
    })
    yield wait(0.5)
    yield retry(run(count + 1))
  }
})

wait

Usage

let seconds = 10
yield wait(seconds)

Explanation

Causes the routine to wait for the given number of seconds before continuing. The seconds parameter must be a number: either an integer, or a decimal like 0.1.

Waiting for zero seconds, or a negative number of seconds, does nothing. The routine simply continues as if the wait were not there.

You can yield wait(Infinity) to cause the program to halt forever.

Example: Wait for 2 seconds

define({
  *run() {
    yield log('This gets output immediately')
    yield wait(2)
    yield log('This gets output after 2 seconds')
  }
})

Example: Wait forever

define({
  *run() {
    yield log('This gets output immediately')
    yield wait(Infinity)
    yield log('This is never output')
  }
})

waitForChar()

Usage

let char = yield waitForChar()

Explanation

Pauses the routine until the user presses a key on the keyboard. Returns a string indicating which key was pressed. For keys that type a character, the return value is the character that was typed.

Possible return values include:

Example

define({
  *run() {
    let key = yield waitForChar()
    yield log('You pressed ' + key)
  }
})

Data Processing


assert

Usage

assert(value, compareFunction, ...expected)

Explanation

The assert() function checks whether the value matches the expected items using the compareFunction. If they match, assert() does nothing. If they do not match, it throws an error.

The compareFunction may be any function that returns true or false. The arguments will be passed to it in this order:

compareFunction(...expected, value)

assert() is especially useful in tests. You can also use it to check that the arguments passed to a function are of the correct type before you try to do things with them.

See also

Verse has many built-in functions that are appropriate to use as the compareFunction. Here are a few of them:

Example: Checking the arguments to a function

define({
  displayText() {
    add('this', 'fails')
  },

  add(a, b) {
    assert(a, isNumber)
    assert(b, isNumber)
    return a + b
  }
})

Example: Testing basic arithmetic

define({
  'test that math works'() {
    assert(3 * 4 - 5, is, 7)
  }
})

equals

Usage

let a = {something: 1}
let b = {something: 1}

if (equals(a, b)) {
  // ...
}

Explanation

Compares the content of two objects, returning true if the objects are equal and false otherwise.

The equals function differs from is in that it deeply compares objects and arrays by checking the equality of each of their properties. The is function only does a shallow comparison, so it will return false if passed two objects that look the same but are stored at different locations in memory.

Here is an example of a case where equals and is return different values:

let a = {hello: 'world'}
let b = {hello: 'world'}

is(a, b) // false! a and b are two separate objects.
equals(a, b) // true. a and b have equivalent content.

Caveats

For the sake of speed, equals does not do any special comparison for more esoteric JavaScript objects like HtmlElement. It also does not check the “class” of objects (indicated by the constructor property). Comparing DOM elements or objects that were constructed with the new operator may produce unexpected results. Verse recommends that you not use the new operator in your programs.

See also

Example: Testing that two arrays are equal

define({
  words(sentence) {
    return sentence.split(' ')
  },

  'test words() splits on spaces'() {
    let result = words('three word phrase')
    assert(result, equals, ['three', 'word', 'phrase'])
  }
})

is

if (is(a, b)) {
  // ...
}

Explanation

TL;DR use equals instead unless you know what you’re doing.

Compares two values using the === operator, returning true if they are identical and false otherwise.

Note that, unlike equals, is does not look at the content of objects and arrays when comparing them. It merely checks if they are the selfsame object. Therefore is may return false in cases where equals returns true.

The main cases where you’d want to use is over equals are:

See also

Example: Testing that a function doesn’t copy an object

define({
  identity(a) {
    return a
  },

  'test identity returns its argument'() {
    let object = {foo: 1}
    assert(identity(object), is, object)
  }
})

lowercase

Usage

let text = 'A Phrase in Title Case'
let output = lowercase(text)

Explanation

Returns a copy of the text with each of the characters converted to its lowercase equivalent.

Characters in the input text that have no lowercase variant (such as numbers and punctuation) are not affected. Lowercase letters in the input are also left as-is.

Example: Converting a string to lower case

define({
  displayText() {
    return lowercase('i Am sO aNnOyInG')
  }
})

reverse

Usage

let text = 'read me backwards'
let backwards = reverse(text)

Explanation

Returns a backwards copy of the text. The string is reversed character by character, so "hello" becomes "olleh".

Example: Backwards “hello world”

define({
  displayText() {
    return reverse('Hello world!')
  }
})

uppercase

Usage

let text = 'some things should be shouted'
let output = uppercase(text)

Explanation

Returns a copy of the text with each of the characters converted to its uppercase equivalent.

Characters in the input text that have no uppercase variant (such as numbers and punctuation) are not affected. Uppercase letters in the input are also left as-is.

You might expect that the uppercased version of a string would have the same number of characters as the original, but this is not always true. For instance, the German letter Ăź becomes SS when uppercased, so a string consisting entirely of Ăź characters will double in length when uppercased.

See also

Example: Uppercasing in Spanish

define({
  displayText() {
    return uppercase('¿Cómo estás?')
  }
})

Magic Definitions


displayText

Usage

displayText() {
  return 'some text'
}

Explanation

If you define a function named displayText, its return value (converted to a string) will be continuously displayed on the screen. Whenever you change your code, the output will update.

If displayText throws an error instead of returning a value, the error will be displayed instead of crashing the program.

Example: Messing with math

define({
  displayText() {
    return 1 + 2 + 3 + 4 + 5
  }
})

run

Usage

*run() {
  // ...
}

Explanation

If you define a routine named run, it becomes the “entry point” to your program. In other words, it’s the routine that Verse will call first, when you tell it to start your program.

If you define both *run() and displayText(), the return value of displayText() is shown first. When you press a key, the run routine starts.

See Also

Example: A one-line program

define({
  *run() {
    yield log('Hello from *run()')
  }
})

Example: Defining both displayText and run

define({
  displayText() {
    return 'This is shown until a key is pressed...'
  },

  *run() {
    yield log('...then this is output.')
  }
})

'test ...'()

Usage

'test something cool'() {
  // ...
}

Explanation

Verse considers functions that begin with the word test to be tests for the main program.

Verse calls all of your test functions whenever you change code. If any of the tests throw errors, the TEST tab will change color. Clicking on the TEST tab shows all the errors encountered by the tests.

See also

Example: Testing a list-formatting function

Here is an example of some tests for a function called listOf. The listOf function takes an array of words and constructs an English phrase that lists them all, using commas and “and” as needed.

Though the code for listOf looks complicated, the tests show that it does what it’s supposed to, at least for the four inputs passed to it in the tests.

define({
  'test a list of nothing is empty'() {
    assert(listOf([]), is, '')
  },

  'test a list of one item is just that item'() {
    assert(listOf(['cupcakes']), is, 'cupcakes')
  },

  'test a list of two items uses "and"'() {
    assert(listOf(['dogs', 'cats']), is, 'dogs and cats')
  },

  'test a list of three items uses commas'() {
    let list = listOf(['dogs', 'cats', 'bats'])
    assert(list, is, 'dogs, cats, and bats')
  },

  listOf(things) {
    if (things.length < 3) {
      return things.join(' and ')
    } else {
      return things.slice(0, things.length - 1).join(', ')
        + ', and '
        + lastOf(things)
    }
  }
})