Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions appendices/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,19 @@ 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())
for value in env.args.values() do
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)

Expand Down Expand Up @@ -195,20 +193,20 @@ fun op_xor(othr: A): A
```pony
class Test
fun alpha() =>
"""
"""
"""
"""
fun beta() =>
"""
"""
"""
"""
```

## How to create Arrays with values

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
]
Expand Down
8 changes: 4 additions & 4 deletions appendices/serialisation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 " +
Expand All @@ -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
Expand Down Expand Up @@ -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")
Expand Down
6 changes: 3 additions & 3 deletions c-ffi/c-abi.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
4 changes: 2 additions & 2 deletions c-ffi/calling-c.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
2 changes: 1 addition & 1 deletion capabilities/combining-capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
12 changes: 6 additions & 6 deletions capabilities/recovering-capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion expressions/control-structures.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
17 changes: 11 additions & 6 deletions expressions/exceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand All @@ -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.

Expand All @@ -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.
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion expressions/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions expressions/object-literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
```
Expand Down
2 changes: 1 addition & 1 deletion gotchas/scheduling.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
2 changes: 1 addition & 1 deletion pattern-matching/as.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 10 additions & 10 deletions pattern-matching/case-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
```

Expand All @@ -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
```

Expand Down
4 changes: 2 additions & 2 deletions testing/ponytest.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down