From d38c047a5abd554ade0f171fdb58aa66c013186a Mon Sep 17 00:00:00 2001 From: ruby0x1 Date: Wed, 7 Apr 2021 20:54:18 -0700 Subject: [PATCH] documentation revisions and missing pieces --- doc/site/classes.markdown | 2 +- doc/site/contributing.markdown | 6 +- doc/site/control-flow.markdown | 4 +- doc/site/functions.markdown | 279 ++++++++++++++++++++------------- doc/site/lists.markdown | 30 ++-- doc/site/maps.markdown | 73 ++++++--- doc/site/method-calls.markdown | 6 +- doc/site/syntax.markdown | 2 +- doc/site/values.markdown | 6 +- doc/site/variables.markdown | 2 +- 10 files changed, 250 insertions(+), 160 deletions(-) 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.
 
 
-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 { }
+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 @@ -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! + +

Classes → ← Variables diff --git a/doc/site/lists.markdown b/doc/site/lists.markdown index 1f471c4f..28a1a5e5 100644 --- a/doc/site/lists.markdown +++ b/doc/site/lists.markdown @@ -20,23 +20,23 @@ element you want. Like most languages, indexes start at zero: [subscript operator]: method-calls.html#subscripts
-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
 
Negative 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]) //> oak
 
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:
-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]) //> spruce
 
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:
-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) //> []
 


diff --git a/doc/site/maps.markdown b/doc/site/maps.markdown index 0b4b056d..2bbc0e82 100644 --- a/doc/site/maps.markdown +++ b/doc/site/maps.markdown @@ -9,22 +9,26 @@ curly braces. Each entry is a key and a value separated by a colon:
 {
-  "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. -

Method Calls → ← Lists diff --git a/doc/site/method-calls.markdown b/doc/site/method-calls.markdown index cbc604d6..d5b7eda8 100644 --- a/doc/site/method-calls.markdown +++ b/doc/site/method-calls.markdown @@ -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:
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) //> wren
 
Their 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