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 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 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. 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 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. If there are no failures, you're good to go.
2. **[Fork the repo][fork] so you can change it locally.** Please make your 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 changes in separate [feature branches][] to make things a little easier.
me.
3. **Change the code.** Please follow the style of the surrounding code. That 3. **Change the code.** Please follow the style of the surrounding code. That
basically means `camelCase` names, `{` on the next line, keep within 80 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.** 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 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 fun open source project!
landed smoothly.
## Getting help ## Getting help

View File

@ -65,7 +65,7 @@ if (ready) {
Unlike most other [operators][] in Wren which are just a special syntax for Unlike most other [operators][] in Wren which are just a special syntax for
[method calls][], the `&&` and `||` operators are special. This is because they [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 [operators]: method-calls.html#operators
[method calls]: method-calls.html [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 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 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"> <pre class="snippet">
System.print(1 != 2 ? "math is sane" : "math is not sane!") System.print(1 != 2 ? "math is sane" : "math is not sane!")

View File

@ -1,129 +1,43 @@
^title Functions ^title Functions
No self-respecting language today can get by without functions&mdash;first Like many languages today, functions in Wren are little bundles of code
class little bundles of code. Since Wren is object-oriented, most of your code you can store in a variable, or pass as an argument to a method.
will live in methods on classes, but free-floating functions are still
eminently handy. 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` Functions are objects like everything else in Wren, instances of the `Fn`
class. class.
## Block arguments ## Creating a function
Most of the time you create a function just to pass it to some method. For To create a function, we call `Fn.new`, which takes a block to execute.
example, if you want to filter a [list](lists.html) by some criteria, you'll To call the function, we use `.call()` on the function instance.
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:
<pre class="snippet"> <pre class="snippet">
blondie.callMe { var sayHello = Fn.new { System.print("hello") }
System.print("This is the body!")
} sayHello.call() //> hello
</pre> </pre>
Here we're invoking the `callMe` method on `blondie`. We're passing one Note that we'll see a shorthand syntax for creating a function below.
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>
## Function parameters ## Function parameters
Of course, functions aren't very useful if you can't pass values to them. The 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 function above takes no arguments. To change that, you can provide a parameter
provide a parameter list surrounded by `|` immediately after the opening brace list surrounded by `|` immediately after the opening brace of the body.
of the body, like so:
To pass arguments to the function, pass them to the `call` method:
<pre class="snippet"> <pre class="snippet">
blondie.callMe {|first, last| var sayMessage {|recipient, message|
System.print("Hi, " + first + " " + last + "!") System.print("message for %(recipient): %(message)")
} }
</pre>
Here we're passing a function to `callMe` that takes two parameters, `first` and sayMessage.call("Bob", "Good day!")
`last`. They are passed to the function when it's called:
<pre class="snippet">
class Blondie {
callMe(fn) {
fn.call("Debbie", "Harry")
}
}
</pre> </pre>
It's an error to call a function with fewer arguments than its parameter list It's an error to call a function with fewer arguments than its parameter list
@ -148,6 +62,14 @@ Fn.new {
} }
</pre> </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 ## Closures
As you expect, functions are closures&mdash;they can access variables defined 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 System.print(counter.call()) //> 3
</pre> </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> <br><hr>
<a class="right" href="classes.html">Classes &rarr;</a> <a class="right" href="classes.html">Classes &rarr;</a>
<a href="variables.html">&larr; Variables</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 [subscript operator]: method-calls.html#subscripts
<pre class="snippet"> <pre class="snippet">
var hirsute = ["sideburns", "porkchops", "'stache", "goatee"] var trees = ["cedar", "birch", "oak", "willow"]
System.print(hirsute[0]) //> sideburns System.print(trees[0]) //> cedar
System.print(hirsute[1]) //> porkchops System.print(trees[1]) //> birch
</pre> </pre>
Negative indices counts backwards from the end: Negative indices counts backwards from the end:
<pre class="snippet"> <pre class="snippet">
System.print(hirsute[-1]) //> goatee System.print(trees[-1]) //> willow
System.print(hirsute[-2]) //> 'stache System.print(trees[-2]) //> oak
</pre> </pre>
It's a runtime error to pass an index outside of the bounds of the list. If you 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: don't know what those bounds are, you can find out using count:
<pre class="snippet"> <pre class="snippet">
System.print(hirsute.count) //> 4 System.print(trees.count) //> 4
</pre> </pre>
## Slices and ranges ## 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: passing a [range](values.html#ranges) to the subscript operator, like so:
<pre class="snippet"> <pre class="snippet">
System.print(hirsute[1..2]) //> [porkchops, 'stache] System.print(trees[1..2]) //> [birch, oak]
</pre> </pre>
This returns a new list containing the elements of the original list whose 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: a list, you can just do:
<pre class="snippet"> <pre class="snippet">
hirsute[0..-1] trees[0..-1]
</pre> </pre>
## Adding elements ## 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: existing element in the list using the subscript setter:
<pre class="snippet"> <pre class="snippet">
hirsute[1] = "muttonchops" trees[1] = "spruce"
System.print(hirsute[1]) //> muttonchops System.print(trees[1]) //> spruce
</pre> </pre>
It's an error to set an element that's out of bounds. To grow a list, you can 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: use `add` to append a single item to the end:
<pre class="snippet"> <pre class="snippet">
hirsute.add("goatee") trees.add("maple")
System.print(hirsute.count) //> 5 System.print(trees.count) //> 5
</pre> </pre>
You can insert a new element at a specific position using `insert`: You can insert a new element at a specific position using `insert`:
<pre class="snippet"> <pre class="snippet">
hirsute.insert(2, "soul patch") trees.insert(2, "hickory")
</pre> </pre>
The first argument is the index to insert at, and the second is the value to 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: If you want to remove everything from the list, you can clear it:
<pre class="snippet"> <pre class="snippet">
hirsute.clear() trees.clear()
System.print(hirsute) //> [] System.print(trees) //> []
</pre> </pre>
<br><hr> <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"> <pre class="snippet">
{ {
"George": "Harrison", "maple": "Sugar Maple (Acer Saccharum)",
"John": "Lennon", "larch": "Alpine Larch (Larix Lyallii)",
"Paul": "McCartney", "oak": "Red Oak (Quercus Rubra)",
"Ringo": "Starr" "fir": "Fraser Fir (Abies Fraseri)"
} }
</pre> </pre>
This creates a map that associates the first name of each Beatle with his last This creates a map that associates a type of tree (key) to a specific
name. Syntactically, in a map literal, keys can be any literal, a variable tree within that family (value). Syntactically, in a map literal, keys
name, or a parenthesized expression. Values can be any expression. Here, we're can be any literal, a variable name, or a parenthesized expression.
using string literals for both keys and values. 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 *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`. [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 [value types]: values.html
[class object]: classes.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()`: To tell definitively if a key exists, you can call `containsKey()`:
<pre class="snippet"> <pre class="snippet">
var belief = {"nihilism": null} var capitals = {"Georgia": null}
System.print(belief["nihilism"]) //> null (though key exists) System.print(capitals["Georgia"]) //> null (though key exists)
System.print(belief["solipsism"]) //> null System.print(capitals["Idaho"]) //> null
System.print(belief.containsKey("nihilism")) //> true System.print(capitals.containsKey("Georgia")) //> true
System.print(belief.containsKey("solipsism")) //> false System.print(capitals.containsKey("Idaho")) //> false
</pre> </pre>
You can see how many entries a map contains using `count`: 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 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. 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 The `keys` method on a map returns a [Sequence][] that [iterates][] over all of
map, and the second returns one that iterates over the values. the keys in the map, and the `values` method returns one that iterates over the values.
[sequence]: modules/core/sequence.html [sequence]: modules/core/sequence.html
[iterates]: control-flow.html#the-iterator-protocol [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 Regardless of how you iterate, the *order* that things are iterated in
iterate over the keys and use each to look up its value: 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"> <pre class="snippet">
var birds = { var birds = {
@ -132,15 +158,10 @@ var birds = {
} }
for (state in birds.keys) { 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> </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> <br><hr>
<a class="right" href="method-calls.html">Method Calls &rarr;</a> <a class="right" href="method-calls.html">Method Calls &rarr;</a>
<a href="lists.html">&larr; Lists</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 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 figures out how many arguments were passed and uses control flow to handle the
two different behaviors. That means first parameter represents "max unless 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 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 `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 ## 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: (`[]`). It's handy for working with collection-like objects. For example:
<pre class="snippet"> <pre class="snippet">

View File

@ -149,7 +149,7 @@ put a newline in there:
</pre> </pre>
Using an initial newline after the `{` does feel a little weird or magical, but 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 thing about this syntax as opposed to something like `=>` is that the *end* of
the block has an explicit delimiter. That helps when chaining: 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 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 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 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"> <pre class="snippet">
var list = ["a", "b", "c", "d", "e"] var list = ["a", "b", "c", "d", "e"]
var slice = list[1..3] var slice = list[1..3]
System.print(slice) //> [b, c, d] System.print(slice) //> [b, c, d]
var string = "hello wren"
var wren = string[-4..-1]
System.print(wren) //> wren
</pre> </pre>
Their class is [Range][]. Their class is [Range][].

View File

@ -54,7 +54,7 @@ var a = "again" //! "a" is already declared.
## Assignment ## 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"> <pre class="snippet">
var a = 123 var a = 123