From 330c7fd1725e1b54b2dc5690ab9e46d15d3c6937 Mon Sep 17 00:00:00 2001 From: Joe Eli McIlvain Date: Thu, 13 Jul 2017 19:14:04 -0700 Subject: [PATCH] Update tutorial for explicit partial calls. See https://github.com/ponylang/rfcs/pull/82. --- appendices/examples.md | 16 +++++++--------- appendices/serialisation.md | 8 ++++---- c-ffi/c-abi.md | 6 +++--- c-ffi/calling-c.md | 4 ++-- capabilities/combining-capabilities.md | 2 +- capabilities/recovering-capabilities.md | 12 ++++++------ expressions/control-structures.md | 2 +- expressions/exceptions.md | 17 +++++++++++------ expressions/methods.md | 2 +- expressions/object-literals.md | 4 ++-- gotchas/scheduling.md | 2 +- pattern-matching/as.md | 2 +- pattern-matching/case-functions.md | 20 ++++++++++---------- testing/ponytest.md | 4 ++-- 14 files changed, 52 insertions(+), 49 deletions(-) diff --git a/appendices/examples.md b/appendices/examples.md index 1f9920cc..73271bc2 100644 --- a/appendices/examples.md +++ b/appendices/examples.md @@ -91,7 +91,6 @@ end ## How to access command line arguments ```pony actor Main - new create(env: Env) => // The no of arguments env.out.print(env.args.size().string()) @@ -99,13 +98,12 @@ actor Main env.out.print(value) end // Access the arguments the first one will always be the the appication name - try env.out.print(env.args(0)) end + try env.out.print(env.args(0)?) end ``` ## How to use options ```pony actor Main - new create(env: Env) => var options = Options(env) @@ -195,11 +193,11 @@ fun op_xor(othr: A): A ```pony class Test fun alpha() => - """ - """ + """ + """ fun beta() => - """ - """ + """ + """ ``` ## How to create Arrays with values @@ -207,8 +205,8 @@ class Test Single values can be separated by semicolon or newline. ```pony -let dice: Array[U32] = [1; 2; 3 - 4 +let dice: Array[U32] = [1; 2; 3 + 4 5 6 ] diff --git a/appendices/serialisation.md b/appendices/serialisation.md index 56f08ffa..1b7f9e5a 100644 --- a/appendices/serialisation.md +++ b/appendices/serialisation.md @@ -39,7 +39,7 @@ actor Main let foo1 = Foo("abc", 123) // serialisation - let sfoo = Serialised(serialise, foo1) + let sfoo = Serialised(serialise, foo1)? let bytes_foo: Array[U8] val = sfoo.output(output) env.out.print("serialised representation is " + @@ -48,7 +48,7 @@ actor Main // deserialisation let dfoo = Serialised.input(input, bytes_foo) - let foo2 = dfoo(deserialise) as Foo + let foo2 = dfoo(deserialise)? as Foo env.out.print("(foo1 == foo2) is " + (foo1 == foo2).string()) else @@ -128,8 +128,8 @@ actor Main let serialise = SerialiseAuth(ambient) let deserialise = DeserialiseAuth(ambient) - let sx = Serialised(serialise, csw) - let y = sx(deserialise) as CStringWrapper + let sx = Serialised(serialise, csw)? + let y = sx(deserialise)? as CStringWrapper y.print() else env.err.print("there was an error") diff --git a/c-ffi/c-abi.md b/c-ffi/c-abi.md index bc67ef82..c745603d 100644 --- a/c-ffi/c-abi.md +++ b/c-ffi/c-abi.md @@ -105,9 +105,9 @@ actor Main var random = MT for i in Range[U64](1, 20) do - let r: U64 = random.next() - let hash = @jch_chash(i, bucket_size) - _env.out.print(i.string() + ": " + hash.string()) + let r: U64 = random.next() + let hash = @jch_chash(i, bucket_size) + _env.out.print(i.string() + ": " + hash.string()) end ``` diff --git a/c-ffi/calling-c.md b/c-ffi/calling-c.md index 19105f07..52c5aaa2 100644 --- a/c-ffi/calling-c.md +++ b/c-ffi/calling-c.md @@ -128,10 +128,10 @@ FFI calls to functions that __might__ raise an error __must__ mark it as such by @os_send[U64](_event, data.cstring(), data.size()) ? // May raise an error ``` -If a signature declaration is used then that must be marked as possibly raising an error in the same way. The FFI call site then does not have to mark it as well, although doing so is allowed. +If a signature declaration is used then that must be marked as possibly raising an error in the same way. The FFI call site must mark it as well. ```pony use @os_send[U64](ev: Event, buf: Pointer[U8] tag, len: U64) ? -@os_send(_event, data.cstring(), data.size()) // May raise an error +@os_send(_event, data.cstring(), data.size())? // May raise an error ``` diff --git a/capabilities/combining-capabilities.md b/capabilities/combining-capabilities.md index 795d1654..66007b51 100644 --- a/capabilities/combining-capabilities.md +++ b/capabilities/combining-capabilities.md @@ -31,7 +31,7 @@ class Foo class Bar fun f() => - var y: Foo trn = getTrnFoo() + var y: Foo trn = get_foo_trn() var z: String box = y.x ``` diff --git a/capabilities/recovering-capabilities.md b/capabilities/recovering-capabilities.md index 24837295..2146e808 100644 --- a/capabilities/recovering-capabilities.md +++ b/capabilities/recovering-capabilities.md @@ -22,29 +22,29 @@ Here's a more complicated example from the standard library: ```pony recover - var s = String((prec' + 1).max(width.max(31))) + var s = String((prec + 1).max(width.max(31))) var value = x try if value == 0 then - s.push(table(0)) + s.push(table(0)?) else while value != 0 do - let index = (value = value / base) - (value * base) - s.push(table(index.usize())) + let index = ((value = value / base) - (value * base)) + s.push(table(index.usize())?) end end end - s.append(typestring) _extend_digits(s, prec') + s.append(typestring) s.append(prestring) _pad(s, width, align, fill) s end ``` -That's from `ToString`. It creates a `String ref`, does a bunch of stuff with it, and finally returns it as a `String iso`. +That's from `format/_FormatInt`. It creates a `String ref`, does a bunch of stuff with it, and finally returns it as a `String iso`. Both of those examples use the default reference capability for a `recover` expression, since they don't specify one. The default for any mutable reference capability is `iso` and the default for any immutable reference capability is `val`. You can also give an explicit one: diff --git a/expressions/control-structures.md b/expressions/control-structures.md index 49488df8..634fbca6 100644 --- a/expressions/control-structures.md +++ b/expressions/control-structures.md @@ -191,7 +191,7 @@ You can think of the above example as being equivalent to: ```pony let iterator = ["Bob"; "Fred"; "Sarah"].values() while iterator.has_next() do - let name = iterator.next() + let name = iterator.next()? env.out.print(name) end ``` diff --git a/expressions/exceptions.md b/expressions/exceptions.md index 4445254e..b8a61e68 100644 --- a/expressions/exceptions.md +++ b/expressions/exceptions.md @@ -16,9 +16,9 @@ else end ``` -In the above code callA() will always be executed and so will callB(). If the result of callB() is true then we will proceed to callC() in the normal fashion and callD() will not then be executed. +In the above code `callA()` will always be executed and so will `callB()`. If the result of `callB()` is true then we will proceed to `callC()` in the normal fashion and `callD()` will not then be executed. -However, if callB() returns false, then an error will be raised. At this point, execution will stop and the nearest enclosing error handler will be found and executed. In this example that is, our else block and so callD() will be executed. +However, if `callB()` returns false, then an error will be raised. At this point, execution will stop and the nearest enclosing error handler will be found and executed. In this example that is, our else block and so `callD()` will be executed. In either case, execution will then carry on with whatever code comes after the try `end`. @@ -28,15 +28,18 @@ If you want to do something that might raise an error, but you don't care if it ```pony try - call() // May raise an error + // Do something that may raise an error end ``` __Is there anything my error handler has to do?__ No. If you provide an error handler then it must contain some code, but it is entirely up to you what it does. +__What's the resulting value of a try block?__ The result of a try block is the value of the last statement in the `try` block, or the the value of the last statement in the `else` clause if an error was raised. If an error was raised and there was no `else` clause provided, the result value will be `None`. + + ## Partial functions -Pony does not require that all errors are handled immediately as in our previous examples. Instead, functions can raise errors that are handled by whatever code calls them. These are called partial functions (this is a mathematical term meaning a function that does not have a defined result for all possible inputs, i.e. arguments). Partial functions __must__ be marked as such in Pony with a `?`. +Pony does not require that all errors are handled immediately as in our previous examples. Instead, functions can raise errors that are handled by whatever code calls them. These are called partial functions (this is a mathematical term meaning a function that does not have a defined result for all possible inputs, i.e. arguments). Partial functions __must__ be marked as such in Pony with a `?`, both in the function signature (after the return type) and at the call site (after the closing parentheses). For example, a somewhat contrived version of the factorial function that accepts a signed integer will error if given a negative input. It's only partially defined over its valid input type. @@ -46,12 +49,14 @@ fun factorial(x: I32): I32 ? => if x == 0 then 1 else - x * factorial(x - 1) + x * factorial(x - 1)? end ``` Everywhere that an error can be generated in Pony (an error command, a call to a partial function, or certain built-in language constructs) must appear within a try block or a function that is marked as partial. This is checked at compile time, ensuring that an error cannot escape handling and crash the program. +Prior to Pony 0.16.0, call sites of partial functions were not required to be marked with a '?'. This often led to confusion about the possibilities for control flow when reading code. Having every partial function call site clearly marked makes it very easy for the reader to immediately understand everywhere that a block of code may jump away to the nearest error handler, making the possible control flow paths more obvious and explicit. + ## Partial constructors and behaviours Constructors may also be marked as partial. If a constructor raises an error then the construction is considered to have failed and the object under construction is discarded without ever being returned to the caller. @@ -124,7 +129,7 @@ The value of a `with` expression is the value of the last expression in the bloc ## Language constructs that can raise errors -The only language construct that can raise an error, other than the error command or calling a partial method, is the `as` command. This converts the given value to the specified type if it can be. If it can't then an error is raised. This means that the `as` command can only be used inside a try block or a partial method. +The only language construct that can raise an error, other than the `error` command or calling a partial method, is the `as` command. This converts the given value to the specified type if it can be. If it can't then an error is raised. This means that the `as` command can only be used inside a try block or a partial method. ## Comparison to exceptions in other languages diff --git a/expressions/methods.md b/expressions/methods.md index fdcf4c20..6031ff32 100644 --- a/expressions/methods.md +++ b/expressions/methods.md @@ -37,7 +37,7 @@ fun factorial(x: I32): I32 ? => if x == 0 then 1 else - x * factorial(x - 1) + x * factorial(x - 1)? end ``` The exact requirements to qualify for this optimization depends on the version of the LLVM compiler. diff --git a/expressions/object-literals.md b/expressions/object-literals.md index 40eae5b8..766759d4 100644 --- a/expressions/object-literals.md +++ b/expressions/object-literals.md @@ -133,7 +133,7 @@ actor Main fun reduce(l: List[U32], acc: U32, f: {(U32, U32): U32} val): U32 => try - let acc' = f(acc, l.shift()) + let acc' = f(acc, l.shift()?) reduce(l, acc', f) else acc @@ -159,7 +159,7 @@ actor Main fun for_each(l: List[String], f: {ref(String)} ref) => try - f(l.shift()) + f(l.shift()?) for_each(l, f) end ``` diff --git a/gotchas/scheduling.md b/gotchas/scheduling.md index 205c280e..763861e7 100644 --- a/gotchas/scheduling.md +++ b/gotchas/scheduling.md @@ -12,7 +12,7 @@ Another way to monopolize a scheduler thread is to write a behavior that never e ```pony be bad_citizen() => - while (true) do + while true do _env.out.print("Never gonna give you up. Really gonna make you cry") end ``` diff --git a/pattern-matching/as.md b/pattern-matching/as.md index 5d6d1d9a..a4def485 100644 --- a/pattern-matching/as.md +++ b/pattern-matching/as.md @@ -19,7 +19,7 @@ actor Main let jd: JsonDoc = JsonDoc for arg in env.args.slice(1).values() do try - jd.parse(arg) + jd.parse(arg)? jsonSum = jsonSum + (jd.data as I64) end end diff --git a/pattern-matching/case-functions.md b/pattern-matching/case-functions.md index 4889d0b7..891e00f6 100644 --- a/pattern-matching/case-functions.md +++ b/pattern-matching/case-functions.md @@ -23,16 +23,16 @@ primitive Factorial // dispatch based on argument match fun fac_case(0): U64 => 1 - fun fac_case(n: U64): U64 ? => n * (fac_case(n - 1) as U64) + fun fac_case(n: U64): U64 ? => n * (fac_case(n - 1)? as U64) // dispatch based on guard function fun fac_guard(n: U64): U64 if n == 0 => 1 - fun fac_guard(n: U64): U64 ? => n * (fac_guard(n - 1) as U64) + fun fac_guard(n: U64): U64 ? => n * (fac_guard(n - 1)? as U64) actor Main new create(env: Env) => let n: U64 = try - env.args(1).read_int[U64]()._1 + env.args(1)?.read_int[U64]()?._1 else 13 end @@ -41,9 +41,9 @@ actor Main env.out.print(n.string().add("! = ") .add(Factorial.fac_conditional(n).string())) env.out.print(n.string().add("! = ") - .add((Factorial.fac_case(n) as U64).string())) + .add((Factorial.fac_case(n)? as U64).string())) env.out.print(n.string().add("! = ") - .add((Factorial.fac_guard(n) as U64).string())) + .add((Factorial.fac_guard(n)? as U64).string())) end ``` @@ -66,20 +66,20 @@ class FizzBuzz // parameters are different, return types are different fun fizz_buzz(x: U64): String ? => _fizz_buzz(x, x % 3, x % 5) as String - fun fizz_buzz(x: Range[U64]): Array[String] => + fun fizz_buzz(x: Range[U64]): Array[String] ? => let acc = Array[String] for z in x do - acc.push(fizz_buzz(z) as String) + acc.push(fizz_buzz(z)? as String) end actor Main new create(env: Env) => try for x in Range[U64](1, 101) do - env.out.print(FizzBuzz.fizz_buzz(x) as String) + env.out.print(FizzBuzz.fizz_buzz(x)? as String) end - let fizz_buzzes = FizzBuzz.fizz_buzz(Range[U64](1, 101)) as Array[String] - env.out.print("\n".join(fizz_buzzes)) + let lines = FizzBuzz.fizz_buzz(Range[U64](1, 101))? as Array[String] + env.out.print("\n".join(lines)) end ``` diff --git a/testing/ponytest.md b/testing/ponytest.md index 24c52380..b9768ef7 100644 --- a/testing/ponytest.md +++ b/testing/ponytest.md @@ -29,13 +29,13 @@ actor Main is TestList test(_TestSub) class iso _TestAdd is UnitTest - fun name():String => "addition" + fun name(): String => "addition" fun apply(h: TestHelper) => h.assert_eq[U32](4, 2 + 2) class iso _TestSub is UnitTest - fun name():String => "subtraction" + fun name(): String => "subtraction" fun apply(h: TestHelper) => h.assert_eq[U32](2, 4 - 2)