1
0
forked from Mirror/wren
Files
wren/doc/site/functions.markdown

163 lines
4.8 KiB
Markdown
Raw Normal View History

2014-01-30 06:51:52 -08:00
^title Functions
2014-04-14 21:23:46 -07:00
^category types
2014-01-30 06:51:52 -08:00
2015-01-03 23:27:02 -08:00
No self-respecting language today can get by without functions—first
class little bundles of code. Since Wren is object-oriented, most of your code
will live in methods on classes, but free-floating functions are still
eminently handy.
2014-04-04 20:45:12 -07:00
2015-01-03 23:27:02 -08:00
Functions are objects like everything else in Wren, instances of the `Fn`
class.
2014-04-04 20:45:12 -07:00
2015-01-03 23:27:02 -08:00
## Block arguments
2014-04-04 20:45:12 -07:00
2015-01-03 23:27:02 -08:00
Most of the time you create a function just to pass it to some method. For
example, if you want to filter a [list](lists.html) by some criteria, you'll
call its `where` method, passing in a function that defines the predicate
you're filtering on.
2014-04-04 20:45:12 -07:00
2015-01-03 23:27:02 -08:00
Since that's the most common usage pattern, Wren's syntax optimizes for that.
Taking a page from Ruby, a function is created by passing a *block argument* to
a method. At its simplest, it looks like this:
2014-04-04 20:45:12 -07:00
:::dart
blondie.callMe {
System.print("This is the body!")
2014-04-04 20:45:12 -07:00
}
2015-01-03 23:27:02 -08:00
Here we're invoking the `callMe` method on `blondie`. We're passing one
argument, a function whose body is the following
[block](syntax.html#blocks)—everything between that pair of curly braces.
2014-04-04 20:45:12 -07:00
2015-01-03 23:27:02 -08:00
Methods that take a block argument receive it as a normal parameter. `callMe`
could be defined like so:
2014-04-04 20:45:12 -07:00
:::dart
class Blondie {
callMe(fn) {
// Call it...
}
}
2015-01-03 23:27:02 -08:00
A method can take other arguments in addition to the block. They appear before
the block just like a regular argument list. For example:
2014-04-04 20:45:12 -07:00
:::dart
blondie.callMeAt(867, 5309) {
System.print("This is the body!")
2014-04-04 20:45:12 -07:00
}
2015-01-03 23:27:02 -08:00
Of course, you don't *have* to use a block argument to pass a function to a
method. If you already have a function object, you can pass it like a regular
argument:
2014-04-04 20:45:12 -07:00
:::dart
var someFn = // Get a function...
blondie.callMe(someFn)
2015-01-03 23:27:02 -08:00
Block arguments are purely sugar for creating a function and passing it in one
little blob of syntax. There are some times when you want to create a function
but *don't* need to pass it to a method. For that, you can call the `Fn`
class's constructor:
2014-04-04 20:45:12 -07:00
2014-04-14 21:23:46 -07:00
:::dart
var someFn = Fn.new {
System.print("Hi!")
2014-04-04 20:45:12 -07:00
}
2015-01-03 23:27:02 -08:00
As you can see it takes a block argument too! All the constructor does it
return that, so this exists purely as a convenience method for you.
2014-04-04 20:45:12 -07:00
## Calling functions
2015-01-03 23:27:02 -08:00
Once you have a function, how do you invoke it? Like everything in Wren, you do
so by calling a method on it:
2014-04-04 20:45:12 -07:00
:::dart
class Blondie {
callMe(fn) {
fn.call()
2014-04-04 20:45:12 -07:00
}
}
Functions expose a `call()` method that executes the body of the function. This
2015-01-03 23:27:02 -08:00
method is dynamically-dispatched like any other, so you can define your own
"function-like" classes and pass them to methods that expect "real" functions.
2014-04-04 20:45:12 -07:00
:::dart
class FakeFn {
call() {
System.print("I'm feeling functional!")
2014-04-04 20:45:12 -07:00
}
}
blondie.callMe(FakeFn.new())
2014-04-04 20:45:12 -07:00
## Function parameters
2015-01-03 23:27:02 -08:00
Of course, functions aren't very useful if you can't pass values to them. The
functions that we've seen so far take no arguments. To change that, you can
provide a parameter list surrounded by `|` immediately after the opening brace
of the body, like so:
2014-04-07 21:03:16 -07:00
:::dart
blondie.callMe {|first, last|
System.print("Hi, " + first + " " + last + "!")
2014-04-07 21:03:16 -07:00
}
2015-01-03 23:27:02 -08:00
Here we're passing a function to `greet` that takes two parameters, `first` and
`last`. They are passed to the function when it's called:
2014-04-07 21:03:16 -07:00
:::dart
class Blondie {
callMe(fn) {
fn.call("Debbie", "Harry")
}
}
2015-01-23 20:33:05 -08:00
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.
2014-04-07 21:03:16 -07:00
## Returning values
2015-01-03 23:27:02 -08:00
The body of a function is a [block](syntax.html#blocks). 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.
2014-04-07 21:03:16 -07:00
2015-01-03 23:27:02 -08:00
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:
2014-04-07 21:03:16 -07:00
:::dart
Fn.new { "return value" }
2014-04-07 21:03:16 -07:00
Fn.new {
2014-04-07 21:03:16 -07:00
return "return value"
}
2014-04-04 20:45:12 -07:00
## Closures
2015-01-03 23:27:02 -08:00
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:
2014-04-04 20:45:12 -07:00
:::dart
class Counter {
static create {
var i = 0
return Fn.new { i = i + 1 }
2014-04-04 20:45:12 -07:00
}
}
2015-01-03 23:27:02 -08:00
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
to`i`:
2014-04-04 20:45:12 -07:00
:::dart
var counter = Counter.create
System.print(counter.call()) // Prints "1".
System.print(counter.call()) // Prints "2".
System.print(counter.call()) // Prints "3".