wren

a classy little scripting language

Functions

Like many languages today, functions in Wren are little bundles of code you can store in a variable, or pass as an argument to a method.

Notice there’s a difference between function and method.

Since Wren is object-oriented, most of your code will live in methods on classes, but free-floating functions are still eminently handy.

Functions are objects like everything else in Wren, instances of the Fn class.

Creating a function #

To create a function, we call Fn.new, which takes a block to execute. To call the function, we use .call() on the function instance.

var sayHello = Fn.new { System.print("hello") }

sayHello.call() //> hello

Note that we’ll see a shorthand syntax for creating a function below.

Function parameters #

Of course, functions aren’t very useful if you can’t pass values to them. The function above takes no arguments. To change that, you can provide a parameter list surrounded by | immediately after the opening brace of the body.

To pass arguments to the function, pass them to the call method:

var sayMessage = Fn.new {|recipient, message|
  System.print("message for %(recipient): %(message)")
}

sayMessage.call("Bob", "Good day!")

It’s an error to call a function with fewer arguments than its parameter list expects. If you pass too many arguments, the extras are ignored.

Returning values #

The body of a function is a block. If it is a single expression—more precisely if there is no newline after the { or parameter list—then the function implicitly returns the value of the expression.

Otherwise, the body returns null by default. You can explicitly return a value using a return statement. In other words, these two functions do the same thing:

Fn.new { "return value" }

Fn.new {
  return "return value"
}

The return value is handed back to you when using call:

var fn = Fn.new { "some value" }
var result = fn.call()
System.print(result) //> some value

Closures #

As you expect, functions are closures—they can access variables defined outside of their scope. They will hold onto closed-over variables even after leaving the scope where the function is defined:

class Counter {
  static create() {
    var i = 0
    return Fn.new { i = i + 1 }
  }
}

Here, the create method returns the function created on its second line. That function references a variable i declared outside of the function. Even after the function is returned from create, it is still able to read and assign toi:

var counter = Counter.create()
System.print(counter.call()) //> 1
System.print(counter.call()) //> 2
System.print(counter.call()) //> 3

Callable classes #

Because Fn is a class, and responds to call(), any class can respond to call() and be used in place of a function. This is particularly handy when the function is passed to a method to be called, like a callback or event.

class Callable {
  construct new() {}
  call(name, version) {
    System.print("called %(name) with version %(version)")
  }
}

var fn = Callable.new()
fn.call("wren", "0.4.0")

Block arguments #

Very frequently, functions are passed to methods to be called. There are countless examples of this in Wren, like list can be filtered using a method where which accepts a function:

var list = [1, 2, 3, 4, 5]
var filtered = list.where(Fn.new {|value| value > 3 }) 
System.print(filtered.toList) //> [4, 5]

This syntax is a bit less fun to read and write, so Wren implements the block argument concept. When a function is being passed to a method, and is the last argument to the method, it can use a shorter syntax: just the block part.

Let’s use a block argument for list.where, it’s the last (only) argument:

var list = [1, 2, 3, 4, 5]
var filtered = list.where {|value| value > 3 } 
System.print(filtered.toList) //> [4, 5]

We’ve seen this before in a previous page using map and where:

numbers.map {|n| n * 2 }.where {|n| n < 100 }

Block argument example #

Let’s look at a complete example, so we can see both ends.

Here’s a fictional class for something that will call a function when a click event is sent to it. It allows us to pass just a function and assume the left mouse button, or to pass a button and a function.

class Clickable {
  construct new() {
    _fn = null
    _button = 0
  }

  onClick(fn) {
    _fn = fn
  }

  onClick(button, fn) {
    _button = button
    _fn = fn
  }

  fireEvent(button) {
    if(_fn && button == _button) {
      _fn.call(button)
    }
  }
}

Now that we’ve got the clickable class, let’s use it. We’ll start by using the method that accepts just a function because we’re fine with it just being the default left mouse button.

var link = Clickable.new()

link.onClick {|button|
  System.print("I was clicked by button %(button)")
}

// send a left mouse click
// normally this would happen from elsewhere

link.fireEvent(0)  //> I was clicked by button 0

Now let’s try with the extra button argument:

var contextMenu = Clickable.new()

contextMenu.onClick(1) {|button|
  System.print("I was right-clicked")
}

link.fireEvent(0)  //> (nothing happened)
link.fireEvent(1)  //> I was right-clicked

Notice that we still pass the other arguments normally, it’s only the last argument that is special.

Just a regular function

Block arguments are purely syntax sugar for creating a function and passing it in one little blob of syntax. These two are equivalent:

onClick(Fn.new { System.print("clicked") })
onClick { System.print("clicked") }

And this is just as valid:

var onEvent = Fn.new {|button|
  System.print("clicked by button %(button)")
}

onClick(onEvent)
onClick(1, onEvent)

Fn.new
As you may have noticed by now, Fn accepts a block argument for the Fn.new. All the constructor does is return that argument right back to you!



Classes → ← Variables