1
0
forked from Mirror/wren

documentation revisions and missing pieces

This commit is contained in:
ruby0x1
2021-04-07 20:54:18 -07:00
parent 0be504832e
commit d38c047a5a
10 changed files with 250 additions and 160 deletions

View File

@ -267,7 +267,7 @@ inside a method works like this:
So, in the above example, we hit case #2 and it prints "Francis". Distinguishing
self sends from outer variables based on the *case* of the first letter in the
name probably seems crazy but it works surprisingly well. Method names are
name probably seems weird but it works surprisingly well. Method names are
lowercase in Wren. Class names are capitalized.
Most of the time, when you're in a method and want to access a name from outside

View File

@ -88,8 +88,7 @@ The basic process is simple:
If there are no failures, you're good to go.
2. **[Fork the repo][fork] so you can change it locally.** Please make your
changes in separate [feature branches][] to make things a little easier on
me.
changes in separate [feature branches][] to make things a little easier.
3. **Change the code.** Please follow the style of the surrounding code. That
basically means `camelCase` names, `{` on the next line, keep within 80
@ -105,8 +104,7 @@ The basic process is simple:
6. **Add your name and email to the [AUTHORS][] file if you haven't already.**
7. **Send a [pull request][].** Pat yourself on the back for contributing to a
fun open source project! I'll take it from here and hopefully we'll get it
landed smoothly.
fun open source project!
## Getting help

View File

@ -65,7 +65,7 @@ if (ready) {
Unlike most other [operators][] in Wren which are just a special syntax for
[method calls][], the `&&` and `||` operators are special. This is because they
only conditionally evaluate right operand—they short-circuit.
only conditionally evaluate the right operand—they short-circuit.
[operators]: method-calls.html#operators
[method calls]: method-calls.html
@ -92,7 +92,7 @@ System.print(1 || 2) //> 1
Also known as the "ternary" operator since it takes three arguments, Wren has
the little "if statement in the form of an expression" you know and love from C
and its brethren.
and similar languages.
<pre class="snippet">
System.print(1 != 2 ? "math is sane" : "math is not sane!")

View File

@ -1,129 +1,43 @@
^title Functions
No self-respecting language today can get by without functions&mdash;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.
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.
## Block arguments
## Creating a function
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.
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:
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.
<pre class="snippet">
blondie.callMe {
System.print("This is the body!")
}
var sayHello = Fn.new { System.print("hello") }
sayHello.call() //> hello
</pre>
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)&mdash;everything between that pair of
curly braces.
Methods that take a block argument receive it as a normal parameter. `callMe`
could be defined like so:
<pre class="snippet">
class Blondie {
callMe(fn) {
// Call it...
}
}
var blondie = Blondie.new()
</pre>
A method can take other arguments in addition to the block. They appear before
the block just like a regular argument list. For example:
<pre class="snippet">
blondie.callMeAt(867, 5309) {
System.print("This is the body!")
}
</pre>
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:
<pre class="snippet">
var someFn = // Get a function...
blondie.callMe(someFn)
</pre>
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:
<pre class="snippet">
var someFn = Fn.new {
System.print("Hi!")
}
</pre>
As you can see it takes a block argument too! All the constructor does is
return that argument, so this exists purely as a convenience method for you.
## Calling functions
Once you have a function, how do you invoke it? Like everything in Wren, you do
so by calling a method on it:
<pre class="snippet">
class Blondie {
callMe(fn) {
fn.call()
}
}
</pre>
Functions expose a `call()` method that executes the body of the function. This
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.
<pre class="snippet">
class FakeFn {
call() {
System.print("I'm feeling functional!")
}
}
blondie.callMe(FakeFn.new())
</pre>
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
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:
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:
<pre class="snippet">
blondie.callMe {|first, last|
System.print("Hi, " + first + " " + last + "!")
var sayMessage {|recipient, message|
System.print("message for %(recipient): %(message)")
}
</pre>
Here we're passing a function to `callMe` that takes two parameters, `first` and
`last`. They are passed to the function when it's called:
<pre class="snippet">
class Blondie {
callMe(fn) {
fn.call("Debbie", "Harry")
}
}
sayMessage.call("Bob", "Good day!")
</pre>
It's an error to call a function with fewer arguments than its parameter list
@ -148,6 +62,14 @@ Fn.new {
}
</pre>
The return value is handed back to you when using `call`:
<pre class="snippet">
var fn = Fn.new { "some value" }
var result = fn.call()
System.print(result) //> some value
</pre>
## Closures
As you expect, functions are closures&mdash;they can access variables defined
@ -175,6 +97,149 @@ System.print(counter.call()) //> 2
System.print(counter.call()) //> 3
</pre>
## 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.
<pre class="snippet">
class Callable {
construct new() {}
call(name, version) {
System.print("called %(name) with version %(version)")
}
}
var fn = Callable.new()
fn.call("wren", "0.4.0")
</pre>
## Block arguments
Very frequently, functions are passed to methods to be called. There are
countless examples of this in Wren, like [list](lists.html) can be filtered
using a method `where` which accepts a function:
<pre class="snippet">
var list = [1, 2, 3, 4, 5]
var filtered = list.where(Fn.new {|value| value > 3 })
System.print(filtered.toList) //> [4, 5]
</pre>
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:
<pre class="snippet">
var list = [1, 2, 3, 4, 5]
var filtered = list.where {|value| value > 3 }
System.print(filtered.toList) //> [4, 5]
</pre>
We've seen this before in a previous page using `map` and `where`:
<pre class="snippet">
numbers.map {|n| n * 2 }.where {|n| n < 100 }
</pre>
## Block argument example
Let's look at a complete example, so we can see both ends.
This may be a bit of a spoiler for [classes](classes.html), so feel free
to read that page first and come back.
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.
<pre class="snippet">
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)
}
}
}
</pre>
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.
<pre class="snippet">
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
</pre>
Now let's try with the extra button argument:
<pre class="snippet">
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
</pre>
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:
<pre class="snippet">
onClick(Fn.new { System.print("clicked") })
onClick { System.print("clicked") }
</pre>
And this is just as valid:
<pre class="snippet">
var onEvent = Fn.new {|button|
System.print("clicked by button %(button)")
}
onClick(onEvent)
onClick(1, onEvent)
</pre>
**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!
<br><hr>
<a class="right" href="classes.html">Classes &rarr;</a>
<a href="variables.html">&larr; Variables</a>

View File

@ -20,23 +20,23 @@ element you want. Like most languages, indexes start at zero:
[subscript operator]: method-calls.html#subscripts
<pre class="snippet">
var hirsute = ["sideburns", "porkchops", "'stache", "goatee"]
System.print(hirsute[0]) //> sideburns
System.print(hirsute[1]) //> porkchops
var trees = ["cedar", "birch", "oak", "willow"]
System.print(trees[0]) //> cedar
System.print(trees[1]) //> birch
</pre>
Negative indices counts backwards from the end:
<pre class="snippet">
System.print(hirsute[-1]) //> goatee
System.print(hirsute[-2]) //> 'stache
System.print(trees[-1]) //> willow
System.print(trees[-2]) //> oak
</pre>
It's a runtime error to pass an index outside of the bounds of the list. If you
don't know what those bounds are, you can find out using count:
<pre class="snippet">
System.print(hirsute.count) //> 4
System.print(trees.count) //> 4
</pre>
## Slices and ranges
@ -45,7 +45,7 @@ Sometimes you want to copy a chunk of elements from a list. You can do that by
passing a [range](values.html#ranges) to the subscript operator, like so:
<pre class="snippet">
System.print(hirsute[1..2]) //> [porkchops, 'stache]
System.print(trees[1..2]) //> [birch, oak]
</pre>
This returns a new list containing the elements of the original list whose
@ -56,7 +56,7 @@ Negative bounds also work like they do when passing a single number, so to copy
a list, you can just do:
<pre class="snippet">
hirsute[0..-1]
trees[0..-1]
</pre>
## Adding elements
@ -65,22 +65,22 @@ Lists are *mutable*, meaning their contents can be changed. You can swap out an
existing element in the list using the subscript setter:
<pre class="snippet">
hirsute[1] = "muttonchops"
System.print(hirsute[1]) //> muttonchops
trees[1] = "spruce"
System.print(trees[1]) //> spruce
</pre>
It's an error to set an element that's out of bounds. To grow a list, you can
use `add` to append a single item to the end:
<pre class="snippet">
hirsute.add("goatee")
System.print(hirsute.count) //> 5
trees.add("maple")
System.print(trees.count) //> 5
</pre>
You can insert a new element at a specific position using `insert`:
<pre class="snippet">
hirsute.insert(2, "soul patch")
trees.insert(2, "hickory")
</pre>
The first argument is the index to insert at, and the second is the value to
@ -143,8 +143,8 @@ System.print(letters.remove("not found")) //> null
If you want to remove everything from the list, you can clear it:
<pre class="snippet">
hirsute.clear()
System.print(hirsute) //> []
trees.clear()
System.print(trees) //> []
</pre>
<br><hr>

View File

@ -9,22 +9,26 @@ curly braces. Each entry is a key and a value separated by a colon:
<pre class="snippet">
{
"George": "Harrison",
"John": "Lennon",
"Paul": "McCartney",
"Ringo": "Starr"
"maple": "Sugar Maple (Acer Saccharum)",
"larch": "Alpine Larch (Larix Lyallii)",
"oak": "Red Oak (Quercus Rubra)",
"fir": "Fraser Fir (Abies Fraseri)"
}
</pre>
This creates a map that associates the first name of each Beatle with his last
name. Syntactically, in a map literal, keys can be any literal, a variable
name, or a parenthesized expression. Values can be any expression. Here, we're
using string literals for both keys and values.
This creates a map that associates a type of tree (key) to a specific
tree within that family (value). Syntactically, in a map literal, keys
can be any literal, a variable name, or a parenthesized expression.
Values can be any expression. Here, we're using string literals for both keys
and values.
*Semantically*, values can be any object, and multiple keys may map to the same
value. Keys have a few limitations. They must be one of the immutable built-in
value.
Keys have a few limitations. They must be one of the immutable built-in
[value types][] in Wren. That means a number, string, range, bool, or `null`.
You can also use a [class object][] as a key.
You can also use a [class object][] as a key (not an instance of that class,
the actual class itself).
[value types]: values.html
[class object]: classes.html
@ -67,12 +71,12 @@ doesn't necessarily mean the key wasn't found.
To tell definitively if a key exists, you can call `containsKey()`:
<pre class="snippet">
var belief = {"nihilism": null}
var capitals = {"Georgia": null}
System.print(belief["nihilism"]) //> null (though key exists)
System.print(belief["solipsism"]) //> null
System.print(belief.containsKey("nihilism")) //> true
System.print(belief.containsKey("solipsism")) //> false
System.print(capitals["Georgia"]) //> null (though key exists)
System.print(capitals["Idaho"]) //> null
System.print(capitals.containsKey("Georgia")) //> true
System.print(capitals.containsKey("Idaho")) //> false
</pre>
You can see how many entries a map contains using `count`:
@ -113,16 +117,38 @@ System.print(capitals.count) //> 0
The subscript operator works well for finding values when you know the key
you're looking for, but sometimes you want to see everything that's in the map.
For that, map exposes two methods: `keys` and `values`.
You can use a regular for loop to iterate the contents, and map exposes two
additional methods to access the contents: `keys` and `values`.
The first returns a [Sequence][] that [iterates][] over all of the keys in the
map, and the second returns one that iterates over the values.
The `keys` method on a map returns a [Sequence][] that [iterates][] over all of
the keys in the map, and the `values` method returns one that iterates over the values.
[sequence]: modules/core/sequence.html
[iterates]: control-flow.html#the-iterator-protocol
If you want to see all of the key-value pairs in a map, the easiest way is to
iterate over the keys and use each to look up its value:
Regardless of how you iterate, the *order* that things are iterated in
isn't defined. Wren makes no promises about what order keys and values are
iterated. All it promises is that every entry will appear exactly once.
**Iterating with for(entry in map)**
When you iterate a map with `for`, you'll be handed an _entry_, which contains
a `key` and a `value` field. That gives you the info for each element in the map.
<pre class="snippet">
var birds = {
"Arizona": "Cactus wren",
"Hawaii": "Nēnē",
"Ohio": "Northern Cardinal"
}
for (bird in birds) {
System.print("The state bird of %(bird.key) is %(bird.value)")
}
</pre>
**Iterating using the keys**
You can also iterate over the keys and use each to look up its value:
<pre class="snippet">
var birds = {
@ -132,15 +158,10 @@ var birds = {
}
for (state in birds.keys) {
System.print("The state bird of " + state + " is " + birds[state])
System.print("The state bird of %(state) is " + birds[state])
}
</pre>
This program prints the three states and their birds. However, the *order*
that they are printed isn't defined. Wren makes no promises about what order
keys and values are iterated in when you use these methods. All it promises is
that every entry will appear exactly once.
<br><hr>
<a class="right" href="method-calls.html">Method Calls &rarr;</a>
<a href="lists.html">&larr; Lists</a>

View File

@ -52,7 +52,9 @@ In a language like Python or JavaScript, these would both call a single `int()`
method, which has some kind of "optional" parameter. The body of the method
figures out how many arguments were passed and uses control flow to handle the
two different behaviors. That means first parameter represents "max unless
another parameter was passed, in which case it's min". Kind of gross.
another parameter was passed, in which case it's min".
This type of 'variadic' code isn't ideal, so Wren doesn't encourage it.
In Wren, these are calls to two entirely separate methods, `int(_,_)` and
`int(_)`. This makes it easier to define "overloads" like this since you don't
@ -163,7 +165,7 @@ like mocks or proxies where you want an object to masquerade as a certain class.
## Subscripts
Another familiar syntax from math class is *subscripting* using square brackets
Another familiar syntax from math is *subscripting* using square brackets
(`[]`). It's handy for working with collection-like objects. For example:
<pre class="snippet">

View File

@ -149,7 +149,7 @@ put a newline in there:
</pre>
Using an initial newline after the `{` does feel a little weird or magical, but
newlines are already significant in Wren, so it's not totally crazy. The nice
newlines are already significant in Wren, so it's not totally unreasonable. The nice
thing about this syntax as opposed to something like `=>` is that the *end* of
the block has an explicit delimiter. That helps when chaining:

View File

@ -199,12 +199,16 @@ This creates a range from four to six *not* including six itself. Ranges are
commonly used for [iterating](control-flow.html#for-statements) over a
sequences of numbers, but are useful in other places too. You can pass them to
a [list](lists.html)'s subscript operator to return a subset of the list, for
example:
example, or on a String, the substring in that range:
<pre class="snippet">
var list = ["a", "b", "c", "d", "e"]
var slice = list[1..3]
System.print(slice) //> [b, c, d]
var string = "hello wren"
var wren = string[-4..-1]
System.print(wren) //> wren
</pre>
Their class is [Range][].

View File

@ -54,7 +54,7 @@ var a = "again" //! "a" is already declared.
## Assignment
After a variable has been declared, you can assign to it using `=`:
After a variable has been declared, you can assign to it using `=`
<pre class="snippet">
var a = 123