diff --git a/doc/site/classes.markdown b/doc/site/classes.markdown index b8f98240..7a75caf7 100644 --- a/doc/site/classes.markdown +++ b/doc/site/classes.markdown @@ -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 diff --git a/doc/site/contributing.markdown b/doc/site/contributing.markdown index 1aa42e91..b4ebfc96 100644 --- a/doc/site/contributing.markdown +++ b/doc/site/contributing.markdown @@ -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 diff --git a/doc/site/control-flow.markdown b/doc/site/control-flow.markdown index 554caf31..4647ffae 100644 --- a/doc/site/control-flow.markdown +++ b/doc/site/control-flow.markdown @@ -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.
System.print(1 != 2 ? "math is sane" : "math is not sane!") diff --git a/doc/site/functions.markdown b/doc/site/functions.markdown index de9616d8..4927c71e 100644 --- a/doc/site/functions.markdown +++ b/doc/site/functions.markdown @@ -1,129 +1,43 @@ ^title Functions -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. +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.+The return value is handed back to you when using `call`: + +-blondie.callMe { - System.print("This is the body!") -} +var sayHello = Fn.new { System.print("hello") } + +sayHello.call() //> hello-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. - -Methods that take a block argument receive it as a normal parameter. `callMe` -could be defined like so: - --class Blondie { - callMe(fn) { - // Call it... - } -} - -var blondie = Blondie.new() -- -A method can take other arguments in addition to the block. They appear before -the block just like a regular argument list. For example: - --blondie.callMeAt(867, 5309) { - System.print("This is the body!") -} -- -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: - --var someFn = // Get a function... -blondie.callMe(someFn) -- -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: - --var someFn = Fn.new { - System.print("Hi!") -} -- -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: - --class Blondie { - callMe(fn) { - fn.call() - } -} -- -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. - --class FakeFn { - call() { - System.print("I'm feeling functional!") - } -} - -blondie.callMe(FakeFn.new()) -+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:-blondie.callMe {|first, last| - System.print("Hi, " + first + " " + last + "!") +var sayMessage {|recipient, message| + System.print("message for %(recipient): %(message)") } --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: - --class Blondie { - callMe(fn) { - fn.call("Debbie", "Harry") - } -} +sayMessage.call("Bob", "Good day!")It's an error to call a function with fewer arguments than its parameter list @@ -148,6 +62,14 @@ Fn.new { }
+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
@@ -175,6 +97,149 @@ 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](lists.html) 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.
+
+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.
+
+
+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!
+
+
-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]) //> birchNegative indices counts backwards from the end:
-System.print(hirsute[-1]) //> goatee -System.print(hirsute[-2]) //> 'stache +System.print(trees[-1]) //> willow +System.print(trees[-2]) //> oakIt'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:
-System.print(hirsute.count) //> 4 +System.print(trees.count) //> 4## 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:
-System.print(hirsute[1..2]) //> [porkchops, 'stache] +System.print(trees[1..2]) //> [birch, oak]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:
-hirsute[0..-1] +trees[0..-1]## 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:
-hirsute[1] = "muttonchops" -System.print(hirsute[1]) //> muttonchops +trees[1] = "spruce" +System.print(trees[1]) //> spruceIt'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:
-hirsute.add("goatee")
-System.print(hirsute.count) //> 5
+trees.add("maple")
+System.print(trees.count) //> 5
You can insert a new element at a specific position using `insert`:
-hirsute.insert(2, "soul patch") +trees.insert(2, "hickory")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:
-hirsute.clear() -System.print(hirsute) //> [] +trees.clear() +System.print(trees) //> []
{
- "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)"
}
-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()`:
-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
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.
+
+
+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)")
+}
+
+
+**Iterating using the keys**
+
+You can also iterate over the keys and use each to look up its value:
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])
}
-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.
-
diff --git a/doc/site/syntax.markdown b/doc/site/syntax.markdown index f0d6fd0a..3fd880a3 100644 --- a/doc/site/syntax.markdown +++ b/doc/site/syntax.markdown @@ -149,7 +149,7 @@ put a newline in there: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: diff --git a/doc/site/values.markdown b/doc/site/values.markdown index faacc101..5e11683f 100644 --- a/doc/site/values.markdown +++ b/doc/site/values.markdown @@ -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:
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) //> wrenTheir class is [Range][]. diff --git a/doc/site/variables.markdown b/doc/site/variables.markdown index 92b5f628..2862e4b8 100644 --- a/doc/site/variables.markdown +++ b/doc/site/variables.markdown @@ -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 `=`
var a = 123