diff --git a/.gitignore b/.gitignore index f4a0e96f..e79f599c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ /site # Caches and logs -*.log \ No newline at end of file +*.log + +# vscode +/.vscode \ No newline at end of file diff --git a/code-samples/actors-behaviors.pony b/code-samples/actors-behaviors.pony new file mode 100644 index 00000000..0227d445 --- /dev/null +++ b/code-samples/actors-behaviors.pony @@ -0,0 +1,9 @@ +actor Aardvark + let name: String + var _hunger_level: U64 = 0 + + new create(name': String) => + name = name' + + be eat(amount: U64) => + _hunger_level = _hunger_level - amount.min(_hunger_level) \ No newline at end of file diff --git a/code-samples/actors-sequential.pony b/code-samples/actors-sequential.pony new file mode 100644 index 00000000..49915fdf --- /dev/null +++ b/code-samples/actors-sequential.pony @@ -0,0 +1,7 @@ +actor Main + new create(env: Env) => + call_me_later(env) + env.out.print("This is printed first") + + be call_me_later(env: Env) => + env.out.print("This is printed last") \ No newline at end of file diff --git a/code-samples/aliasing-alias-types.pony b/code-samples/aliasing-alias-types.pony new file mode 100644 index 00000000..f32ed869 --- /dev/null +++ b/code-samples/aliasing-alias-types.pony @@ -0,0 +1,2 @@ +fun test(a: A) => + var b: A! = a \ No newline at end of file diff --git a/code-samples/aliasing-ephemeral-types.pony b/code-samples/aliasing-ephemeral-types.pony new file mode 100644 index 00000000..557500ed --- /dev/null +++ b/code-samples/aliasing-ephemeral-types.pony @@ -0,0 +1,2 @@ +fun test(a: Wombat iso): Wombat iso^ => + consume a \ No newline at end of file diff --git a/code-samples/aliasing-iso-to-tag.pony b/code-samples/aliasing-iso-to-tag.pony new file mode 100644 index 00000000..c7202e93 --- /dev/null +++ b/code-samples/aliasing-iso-to-tag.pony @@ -0,0 +1,2 @@ +fun test(a: Wombat iso) => + var b: Wombat tag = a // Allowed! \ No newline at end of file diff --git a/code-samples/aliasing-multiple-references-to-an-iso-object.pony b/code-samples/aliasing-multiple-references-to-an-iso-object.pony new file mode 100644 index 00000000..8acb51c5 --- /dev/null +++ b/code-samples/aliasing-multiple-references-to-an-iso-object.pony @@ -0,0 +1,2 @@ +fun test(a: Wombat iso) => + var b: Wombat iso = a // Not allowed! \ No newline at end of file diff --git a/code-samples/aliasing-trn-to-box.pony b/code-samples/aliasing-trn-to-box.pony new file mode 100644 index 00000000..443a799b --- /dev/null +++ b/code-samples/aliasing-trn-to-box.pony @@ -0,0 +1,2 @@ +fun test(a: Wombat trn) => + var b: Wombat box = a // Allowed! \ No newline at end of file diff --git a/code-samples/appendices-annotations-empty-with-nosupertype-annotation.pony b/code-samples/appendices-annotations-empty-with-nosupertype-annotation.pony new file mode 100644 index 00000000..10d96d82 --- /dev/null +++ b/code-samples/appendices-annotations-empty-with-nosupertype-annotation.pony @@ -0,0 +1,7 @@ +class \nosupertype\ Empty + +class Foo + fun foo[A: Any](a: (A | Empty val)) => + match consume a + | let a': A => None + end \ No newline at end of file diff --git a/code-samples/appendices-annotations-empty-without-nosupertype-annotation.pony b/code-samples/appendices-annotations-empty-without-nosupertype-annotation.pony new file mode 100644 index 00000000..b68d1487 --- /dev/null +++ b/code-samples/appendices-annotations-empty-without-nosupertype-annotation.pony @@ -0,0 +1,7 @@ +class Empty + +class Foo + fun foo[A: Any](a: (A | Empty val)) => + match consume a + | let a': A => None + end \ No newline at end of file diff --git a/code-samples/appendices-annotations-likely-and-unlikely-annotations.pony b/code-samples/appendices-annotations-likely-and-unlikely-annotations.pony new file mode 100644 index 00000000..ea00d0af --- /dev/null +++ b/code-samples/appendices-annotations-likely-and-unlikely-annotations.pony @@ -0,0 +1,16 @@ +if \likely\ cond then + foo +end + +while \unlikely\ cond then + bar +end + +repeat + baz +until \likely\ cond end + +match obj +| \likely\ expr => foo +| \unlikely\ let capt: T => bar +end \ No newline at end of file diff --git a/code-samples/appendices-annotations-nodoc-annotation.pony b/code-samples/appendices-annotations-nodoc-annotation.pony new file mode 100644 index 00000000..03f51477 --- /dev/null +++ b/code-samples/appendices-annotations-nodoc-annotation.pony @@ -0,0 +1,4 @@ +class \nodoc\Foo + """ + We don't want this class and its methods to appear in generated documentation + """ \ No newline at end of file diff --git a/code-samples/appendices-annotations-packed-annotation.pony b/code-samples/appendices-annotations-packed-annotation.pony new file mode 100644 index 00000000..e13d8926 --- /dev/null +++ b/code-samples/appendices-annotations-packed-annotation.pony @@ -0,0 +1,3 @@ +struct \packed\ MyPackedStruct + var x: U8 + var y: U32 \ No newline at end of file diff --git a/code-samples/appendices-annotations-syntax.pony b/code-samples/appendices-annotations-syntax.pony new file mode 100644 index 00000000..242e4690 --- /dev/null +++ b/code-samples/appendices-annotations-syntax.pony @@ -0,0 +1 @@ +\annotation1, annotation2\ \ No newline at end of file diff --git a/code-samples/appendices-examples-access-command-line-arguments.pony b/code-samples/appendices-examples-access-command-line-arguments.pony new file mode 100644 index 00000000..d82416a3 --- /dev/null +++ b/code-samples/appendices-examples-access-command-line-arguments.pony @@ -0,0 +1,9 @@ +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 application name + try env.out.print(env.args(0)?) end \ No newline at end of file diff --git a/code-samples/appendices-examples-create-arrays-with-values.pony b/code-samples/appendices-examples-create-arrays-with-values.pony new file mode 100644 index 00000000..905c3859 --- /dev/null +++ b/code-samples/appendices-examples-create-arrays-with-values.pony @@ -0,0 +1,5 @@ +let dice: Array[U32] = [1; 2; 3 + 4 + 5 + 6 +] \ No newline at end of file diff --git a/code-samples/appendices-examples-empty-class-functions.pony b/code-samples/appendices-examples-empty-class-functions.pony new file mode 100644 index 00000000..85cec992 --- /dev/null +++ b/code-samples/appendices-examples-empty-class-functions.pony @@ -0,0 +1,7 @@ +class Test + fun alpha() => + """ + """ + fun beta() => + """ + """ \ No newline at end of file diff --git a/code-samples/appendices-examples-enumeration-with-values-with-namespace.pony b/code-samples/appendices-examples-enumeration-with-values-with-namespace.pony new file mode 100644 index 00000000..35f4c222 --- /dev/null +++ b/code-samples/appendices-examples-enumeration-with-values-with-namespace.pony @@ -0,0 +1,3 @@ +primitive Colours + fun black(): U32 => 0xFF000000 + fun red(): U32 => 0xFFFF0000 \ No newline at end of file diff --git a/code-samples/appendices-examples-enumeration-with-values.pony b/code-samples/appendices-examples-enumeration-with-values.pony new file mode 100644 index 00000000..1be5b0e2 --- /dev/null +++ b/code-samples/appendices-examples-enumeration-with-values.pony @@ -0,0 +1,2 @@ +primitive Black fun apply(): U32 => 0xFF000000 +primitive Red fun apply(): U32 => 0xFFFF0000 \ No newline at end of file diff --git a/code-samples/appendices-examples-iterable-enumerations.pony b/code-samples/appendices-examples-iterable-enumerations.pony new file mode 100644 index 00000000..573b969b --- /dev/null +++ b/code-samples/appendices-examples-iterable-enumerations.pony @@ -0,0 +1,13 @@ +primitive Black +primitive Blue +primitive Red +primitive Yellow + +type Colour is (Black | Blue | Red | Yellow) + +primitive ColourList + fun tag apply(): Array[Colour] => + [Black; Blue; Red; Yellow] + +for colour in ColourList().values() do +end \ No newline at end of file diff --git a/code-samples/appendices-examples-modify-a-lexically-captured-variable-in-a-closure.pony b/code-samples/appendices-examples-modify-a-lexically-captured-variable-in-a-closure.pony new file mode 100644 index 00000000..202e501f --- /dev/null +++ b/code-samples/appendices-examples-modify-a-lexically-captured-variable-in-a-closure.pony @@ -0,0 +1,16 @@ +actor Main + fun foo(n:U32): {ref(U32): U32} => + var s: Array[U32] = Array[U32].init(n, 1) + {ref(i:U32)(s): U32 => + try + s(0)? = s(0)? + i + s(0)? + else + 0 + end + } + + new create(env:Env) => + var f = foo(5) + env.out.print(f(10).string()) + env.out.print(f(20).string()) \ No newline at end of file diff --git a/code-samples/appendices-examples-operator-overloading.pony b/code-samples/appendices-examples-operator-overloading.pony new file mode 100644 index 00000000..4a45dd6d --- /dev/null +++ b/code-samples/appendices-examples-operator-overloading.pony @@ -0,0 +1,17 @@ +fun add(other: A): A +fun sub(other: A): A +fun mul(other: A): A +fun div(other: A): A +fun rem(other: A): A +fun mod(other: A): A +fun eq(other: A): Bool +fun ne(other: A): Bool +fun lt(other: A): Bool +fun le(other: A): Bool +fun ge(other: A): Bool +fun gt(other: A): Bool +fun shl(other: A): A +fun shr(other: A): A +fun op_and(other:A): A +fun op_or(other: A): A +fun op_xor(othr: A): A \ No newline at end of file diff --git a/code-samples/appendices-examples-pass-array-of-values-to-ffi.pony b/code-samples/appendices-examples-pass-array-of-values-to-ffi.pony new file mode 100644 index 00000000..387570c9 --- /dev/null +++ b/code-samples/appendices-examples-pass-array-of-values-to-ffi.pony @@ -0,0 +1,18 @@ +use @eglChooseConfig[U32](disp: Pointer[_EGLDisplayHandle], attrs: Pointer[U16] tag, + config: Pointer[_EGLConfigHandle], config_size: U32, num_config: Pointer[U32]) + +primitive _EGLConfigHandle +let a = Array[U16](8) +a.push(0x3040) +a.push(0x4) +a.push(0x3033) +a.push(0x4) +a.push(0x3022) +a.push(0x8) +a.push(0x3023) +a.push(0x8) +a.push(0x3024) +let config = Pointer[_EGLConfigHandle] +if @eglChooseConfig(e_dpy, a.cpointer(), config, U32(1), Pointer[U32]) == 0 then + env.out.print("eglChooseConfig failed") +end \ No newline at end of file diff --git a/code-samples/appendices-examples-test-helper.pony b/code-samples/appendices-examples-test-helper.pony new file mode 100644 index 00000000..bdb0a603 --- /dev/null +++ b/code-samples/appendices-examples-test-helper.pony @@ -0,0 +1,15 @@ +fun tag log(msg: String, verbose: Bool = false) +be fail() => +be assert_failed(msg: String) => +fun tag assert_true(actual: Bool, msg: String = "") ? +fun tag expect_true(actual: Bool, msg: String = ""): Bool +fun tag assert_false(actual: Bool, msg: String = "") ? +fun tag expect_false(actual: Bool, msg: String = ""): Bool +fun tag assert_error(test: ITest, msg: String = "") ? +fun tag expect_error(test: ITest box, msg: String = ""): Bool +fun tag assert_is (expect: Any, actual: Any, msg: String = "") ? +fun tag expect_is (expect: Any, actual: Any, msg: String = ""): Bool +fun tag assert_eq[A: (Equatable[A] #read & Stringable)] + (expect: A, actual: A, msg: String = "") ? +fun tag expect_eq[A: (Equatable[A] #read & Stringable)] + (expect: A, actual: A, msg: String = ""): Bool \ No newline at end of file diff --git a/code-samples/appendices-examples-use-cli-package-to-parse-command-line-arguments.pony b/code-samples/appendices-examples-use-cli-package-to-parse-command-line-arguments.pony new file mode 100644 index 00000000..496500a5 --- /dev/null +++ b/code-samples/appendices-examples-use-cli-package-to-parse-command-line-arguments.pony @@ -0,0 +1,32 @@ +use "cli" + +actor Main + new create(env: Env) => + let command_spec = + try + CommandSpec.leaf( + "pony-embed", + "sample program", + [ OptionSpec.string("output", "output filename", 'o') ], + [ ArgSpec.string("input", "source of input" where default' = "-") ] + )? .> add_help()? + else + env.exitcode(1) + return + end + let command = + match CommandParser(command_spec).parse(env.args, env.vars) + | let c: Command => c + | let ch: CommandHelp => + ch.print_help(env.out) + env.exitcode(0) + return + | let se: SyntaxError => + env.err.print(se.string()) + env.exitcode(1) + return + end + let input_source = command.arg("input").string() + let output_filename = command.option("output").string() + env.out.print("Loading data from " + input_source + ". Writing output to " + output_filename) + // ... \ No newline at end of file diff --git a/code-samples/appendices-examples-write-tests.pony b/code-samples/appendices-examples-write-tests.pony new file mode 100644 index 00000000..4b9e4bbf --- /dev/null +++ b/code-samples/appendices-examples-write-tests.pony @@ -0,0 +1,17 @@ +use "pony_test" + +actor Main is TestList + new create(env: Env) => PonyTest(env, this) + new make() => None + + fun tag tests(test: PonyTest) => + test(_TestAddition) + +class iso _TestAddition is UnitTest + """ + Adding 2 numbers + """ + fun name(): String => "u32/add" + + fun apply(h: TestHelper) => + h.assert_eq[U32](2 + 2, 4) \ No newline at end of file diff --git a/code-samples/appendices-platform-dependent-code.pony b/code-samples/appendices-platform-dependent-code.pony new file mode 100644 index 00000000..fe53562e --- /dev/null +++ b/code-samples/appendices-platform-dependent-code.pony @@ -0,0 +1,2 @@ +use "foo" if linux +use "bar" if (windows and debug) \ No newline at end of file diff --git a/code-samples/appendices-serialization-compare-original-object-with-deserialized-object.pony b/code-samples/appendices-serialization-compare-original-object-with-deserialized-object.pony new file mode 100644 index 00000000..4f1db8e0 --- /dev/null +++ b/code-samples/appendices-serialization-compare-original-object-with-deserialized-object.pony @@ -0,0 +1,40 @@ +use "serialise" + +class Foo is Equatable[Foo box] + let _s: String + let _u: U32 + + new create(s: String, u: U32) => + _s = s + _u = u + + fun eq(foo: Foo box): Bool => + (_s == foo._s) and (_u == foo._u) + +actor Main + new create(env: Env) => + try + // get serialization authorities + let serialise = SerialiseAuth(env.root) + let output = OutputSerialisedAuth(env.root) + let deserialise = DeserialiseAuth(env.root) + let input = InputSerialisedAuth(env.root) + + let foo1 = Foo("abc", 123) + + // serialisation + let sfoo = Serialised(serialise, foo1)? + let bytes_foo: Array[U8] val = sfoo.output(output) + + env.out.print("serialised representation is " + + bytes_foo.size().string() + + " bytes long") + + // deserialisation + let dfoo = Serialised.input(input, bytes_foo) + let foo2 = dfoo(deserialise)? as Foo + + env.out.print("(foo1 == foo2) is " + (foo1 == foo2).string()) + else + env.err.print("there was an error") + end \ No newline at end of file diff --git a/code-samples/appendices-serialization-custom-serialization.pony b/code-samples/appendices-serialization-custom-serialization.pony new file mode 100644 index 00000000..1d809baa --- /dev/null +++ b/code-samples/appendices-serialization-custom-serialization.pony @@ -0,0 +1,42 @@ +use "serialise" + +use "lib:custser" + +use @get_string[Pointer[U8]]() +use @serialise_space[USize](s: Pointer[U8] tag) +use @serialise[None](bytes: Pointer[U8] tag, str: Pointer[U8] tag) +use @deserialise[Pointer[U8] tag](bytes: Pointer[U8] tag) +use @printf[I32](fmt: Pointer[U8] tag, ...) + +class CStringWrapper + var _cstr: Pointer[U8] tag + + new create(cstr: Pointer[U8] tag) => + _cstr = cstr + + fun _serialise_space(): USize => + @serialise_space(_cstr) + + fun _serialise(bytes: Pointer[U8] tag) => + @serialise(bytes, _cstr) + + fun ref _deserialise(bytes: Pointer[U8] tag) => + _cstr = @deserialise(bytes) + + fun print() => + @printf(_cstr) + +actor Main + new create(env: Env) => + let csw = CStringWrapper(@get_string()) + csw.print() + try + let serialise = SerialiseAuth(env.root) + let deserialise = DeserialiseAuth(env.root) + + let sx = Serialised(serialise, csw)? + let y = sx(deserialise)? as CStringWrapper + y.print() + else + env.err.print("there was an error") + end \ No newline at end of file diff --git a/code-samples/appendices-whitespace-comments.pony b/code-samples/appendices-whitespace-comments.pony new file mode 100644 index 00000000..6eb9b84a --- /dev/null +++ b/code-samples/appendices-whitespace-comments.pony @@ -0,0 +1,3 @@ +// This is a line comment. +/* This is a block comment. */ +/* This block comment /* has another block comment */ inside of it. */ \ No newline at end of file diff --git a/code-samples/appendices-whitespace-do-a-then-do-a-unary-negation-of-b.pony b/code-samples/appendices-whitespace-do-a-then-do-a-unary-negation-of-b.pony new file mode 100644 index 00000000..045ea728 --- /dev/null +++ b/code-samples/appendices-whitespace-do-a-then-do-a-unary-negation-of-b.pony @@ -0,0 +1,2 @@ +a +-b \ No newline at end of file diff --git a/code-samples/appendices-whitespace-docstrings.pony b/code-samples/appendices-whitespace-docstrings.pony new file mode 100644 index 00000000..f8a4c9ed --- /dev/null +++ b/code-samples/appendices-whitespace-docstrings.pony @@ -0,0 +1,21 @@ +actor Main + """ + This is documentation for my Main actor + """ + + var count: USize = 0 + """ + This is documentation for my count field + """ + + new create(env: Env) => + """ + This is documentation for my create method + """ + None + +trait Readable + fun val read() + """ + This is documentation for my unimplemented read method + """ \ No newline at end of file diff --git a/code-samples/appendices-whitespace-subtract-b-from-a.pony b/code-samples/appendices-whitespace-subtract-b-from-a.pony new file mode 100644 index 00000000..f76d71bf --- /dev/null +++ b/code-samples/appendices-whitespace-subtract-b-from-a.pony @@ -0,0 +1 @@ +a - b \ No newline at end of file diff --git a/code-samples/arithmetic-default-integer-arithmetic.pony b/code-samples/arithmetic-default-integer-arithmetic.pony new file mode 100644 index 00000000..2fcf5241 --- /dev/null +++ b/code-samples/arithmetic-default-integer-arithmetic.pony @@ -0,0 +1,5 @@ +// unsigned wrap-around on overflow +U32.max_value() + 1 == 0 + +// signed wrap-around on overflow/underflow +I32.min_value() - 1 == I32.max_value() \ No newline at end of file diff --git a/code-samples/arithmetic-explicit-numeric-conversion.pony b/code-samples/arithmetic-explicit-numeric-conversion.pony new file mode 100644 index 00000000..903a406c --- /dev/null +++ b/code-samples/arithmetic-explicit-numeric-conversion.pony @@ -0,0 +1,2 @@ +// converting an I32 to a 32 bit floating point +I32(12).f32() \ No newline at end of file diff --git a/code-samples/arithmetic-partial-and-check-arithmetic.pony b/code-samples/arithmetic-partial-and-check-arithmetic.pony new file mode 100644 index 00000000..14ed8b78 --- /dev/null +++ b/code-samples/arithmetic-partial-and-check-arithmetic.pony @@ -0,0 +1,17 @@ +// partial arithmetic +let result = + try + USize.max_value() +? env.args.size() + else + env.out.print("overflow detected") + end + +// checked arithmetic +let result = + match USize.max_value().addc(env.args.size()) + | (let result: USize, false) => + // use result + ... + | (_, true) => + env.out.print("overflow detected") + end \ No newline at end of file diff --git a/code-samples/arithmetic-unsafe-conversion.pony b/code-samples/arithmetic-unsafe-conversion.pony new file mode 100644 index 00000000..6d7d2aae --- /dev/null +++ b/code-samples/arithmetic-unsafe-conversion.pony @@ -0,0 +1,8 @@ +// converting an I32 to a 32 bit floating point, the unsafe way +I32(12).f32_unsafe() + +// an example for an undefined unsafe conversion +I64.max_value().f32_unsafe() + +// an example for an undefined unsafe conversion, that is actually safe +I64(1).u8_unsafe() \ No newline at end of file diff --git a/code-samples/arrow-types-box.pony b/code-samples/arrow-types-box.pony new file mode 100644 index 00000000..7479efcf --- /dev/null +++ b/code-samples/arrow-types-box.pony @@ -0,0 +1,3 @@ +interface Comparable[A: Comparable[A] box] + fun eq(that: box->A): Bool => this is that + fun ne(that: box->A): Bool => not eq(that) \ No newline at end of file diff --git a/code-samples/arrow-types-this.pony b/code-samples/arrow-types-this.pony new file mode 100644 index 00000000..45e273f7 --- /dev/null +++ b/code-samples/arrow-types-this.pony @@ -0,0 +1,4 @@ +class Wombat + var _friend: Wombat + + fun friend(): this->Wombat => _friend \ No newline at end of file diff --git a/code-samples/arrow-types-type-parameter.pony b/code-samples/arrow-types-type-parameter.pony new file mode 100644 index 00000000..c6ee44b8 --- /dev/null +++ b/code-samples/arrow-types-type-parameter.pony @@ -0,0 +1 @@ +class ListValues[A, N: ListNode[A] box] is Iterator[N->A] \ No newline at end of file diff --git a/code-samples/as-operator-array-literal.pony b/code-samples/as-operator-array-literal.pony new file mode 100644 index 00000000..00c23c4a --- /dev/null +++ b/code-samples/as-operator-array-literal.pony @@ -0,0 +1,9 @@ +actor Main + fun foo(xs: (Array[U32] ref | Array[U64] ref)): Bool => + // do something boring here + true + + new create(env: Env) => + foo([as U32: 1; 2; 3]) + // the compiler would complain about this: + // foo([1; 2; 3]) \ No newline at end of file diff --git a/code-samples/as-operator-match-statement-comparison.pony b/code-samples/as-operator-match-statement-comparison.pony new file mode 100644 index 00000000..c3cd6a45 --- /dev/null +++ b/code-samples/as-operator-match-statement-comparison.pony @@ -0,0 +1,13 @@ +actor Main + new create(env: Env) => + let anys = Array[Any ref].>push(Wombat).>push(Capybara) + for any in anys.values() do + try + match any + | let critter: Critter => + env.out.print(critter.wash()) + else + error + end + end + end \ No newline at end of file diff --git a/code-samples/as-operator-match-statement-without-try.pony b/code-samples/as-operator-match-statement-without-try.pony new file mode 100644 index 00000000..16d6bb73 --- /dev/null +++ b/code-samples/as-operator-match-statement-without-try.pony @@ -0,0 +1,9 @@ +actor Main + new create(env: Env) => + let anys = Array[Any ref].>push(Wombat).>push(Capybara) + for any in anys.values() do + match any + | let critter: Critter => + env.out.print(critter.wash()) + end + end \ No newline at end of file diff --git a/code-samples/as-operator-more-specific-interface-with-reference-capability.pony b/code-samples/as-operator-more-specific-interface-with-reference-capability.pony new file mode 100644 index 00000000..30960f93 --- /dev/null +++ b/code-samples/as-operator-more-specific-interface-with-reference-capability.pony @@ -0,0 +1,8 @@ +actor Main + new create(env: Env) => + let anys = Array[Any ref].>push(Wombat).>push(Capybara) + for any in anys.values() do + try + env.out.print((any as Critter).wash()) + end + end \ No newline at end of file diff --git a/code-samples/as-operator-more-specific-interface.pony b/code-samples/as-operator-more-specific-interface.pony new file mode 100644 index 00000000..5c2a0b01 --- /dev/null +++ b/code-samples/as-operator-more-specific-interface.pony @@ -0,0 +1,19 @@ +interface Critter + fun wash(): String + +class Wombat is Critter + fun wash(): String => "I'm a clean wombat!" + +class Capybara is Critter + fun wash(): String => "I feel squeaky clean!" + fun swim(): String => "I'm swimming like a fish!" + +actor Main + new create(env: Env) => + let critters = Array[Critter].>push(Wombat).>push(Capybara) + for critter in critters.values() do + env.out.print(critter.wash()) + try + env.out.print((critter as Capybara).swim()) + end + end \ No newline at end of file diff --git a/code-samples/as-operator-more-specific-type.pony b/code-samples/as-operator-more-specific-type.pony new file mode 100644 index 00000000..79df9108 --- /dev/null +++ b/code-samples/as-operator-more-specific-type.pony @@ -0,0 +1,12 @@ + class Cat + fun pet() => + ... + + type Animal is (Cat | Fish | Snake) + + fun pet(animal: Animal) => + try + // raises error if not a Cat + let cat: Cat = animal as Cat + cat.pet() + end \ No newline at end of file diff --git a/code-samples/as-operator-unrelated-type.pony b/code-samples/as-operator-unrelated-type.pony new file mode 100644 index 00000000..5102043a --- /dev/null +++ b/code-samples/as-operator-unrelated-type.pony @@ -0,0 +1,10 @@ + trait Alive + + trait Well + + class Person is (Alive & Well) + + class LifeSigns + fun is_all_good(alive: Alive)? => + // if the instance 'alive' is also of type 'Well' (such as a Person instance). raises error if not possible + let well: Well = alive as Well \ No newline at end of file diff --git a/code-samples/c-abi-jump-consistent-hashing.pony b/code-samples/c-abi-jump-consistent-hashing.pony new file mode 100644 index 00000000..dbb9920c --- /dev/null +++ b/code-samples/c-abi-jump-consistent-hashing.pony @@ -0,0 +1,15 @@ +// Jump consistent hashing in Pony, with an inline pseudo random generator +// https://arxiv.org/abs/1406.2294 + +fun jch(key: U64, buckets: U32): I32 => + var k = key + var b = I64(0) + var j = I64(0) + + while j < buckets.i64() do + b = j + k = (k * 2862933555777941757) + 1 + j = ((b + 1).f64() * (I64(1 << 31).f64() / ((k >> 33) + 1).f64())).i64() + end + + b.i32() \ No newline at end of file diff --git a/code-samples/c-abi-pony-use-native-jump-consistent-hashing-c-implementation.pony b/code-samples/c-abi-pony-use-native-jump-consistent-hashing-c-implementation.pony new file mode 100644 index 00000000..68aee7d1 --- /dev/null +++ b/code-samples/c-abi-pony-use-native-jump-consistent-hashing-c-implementation.pony @@ -0,0 +1,41 @@ +""" +This is an example of Pony integrating with native code via the built-in FFI +support +""" + +use "lib:jch" +use "collections" +use @jch_chash[I32](hash: U64, bucket_size: U32) + +actor Main + var _env: Env + + new create(env: Env) => + _env = env + + let bucket_size: U32 = 1000000 + + _env.out.print("C implementation:") + for i in Range[U64](1, 20) do + let hash = @jch_chash(i, bucket_size) + _env.out.print(i.string() + ": " + hash.string()) + end + + _env.out.print("Pony implementation:") + for i in Range[U64](1, 20) do + let hash = jch(i, bucket_size) + _env.out.print(i.string() + ": " + hash.string()) + end + + fun jch(key: U64, buckets: U32): I32 => + var k = key + var b = I64(0) + var j = I64(0) + + while j < buckets.i64() do + b = j + k = (k * 2862933555777941757) + 1 + j = ((b + 1).f64() * (I64(1 << 31).f64() / ((k >> 33) + 1).f64())).i64() + end + + b.i32() \ No newline at end of file diff --git a/code-samples/c-ffi-callbacks-bare-functions-pass-to-c-api.pony b/code-samples/c-ffi-callbacks-bare-functions-pass-to-c-api.pony new file mode 100644 index 00000000..c51ee961 --- /dev/null +++ b/code-samples/c-ffi-callbacks-bare-functions-pass-to-c-api.pony @@ -0,0 +1 @@ +@setup_callback(addressof C.callback) \ No newline at end of file diff --git a/code-samples/c-ffi-callbacks-bare-functions.pony b/code-samples/c-ffi-callbacks-bare-functions.pony new file mode 100644 index 00000000..dbd52e58 --- /dev/null +++ b/code-samples/c-ffi-callbacks-bare-functions.pony @@ -0,0 +1,3 @@ +class C + fun @callback() => + ... \ No newline at end of file diff --git a/code-samples/c-ffi-callbacks-bare-lambda-callback.pony b/code-samples/c-ffi-callbacks-bare-lambda-callback.pony new file mode 100644 index 00000000..b501e412 --- /dev/null +++ b/code-samples/c-ffi-callbacks-bare-lambda-callback.pony @@ -0,0 +1,2 @@ +let callback = @{() => ... } +@setup_callback(callback) \ No newline at end of file diff --git a/code-samples/c-ffi-callbacks-bare-lambda-struct.pony b/code-samples/c-ffi-callbacks-bare-lambda-struct.pony new file mode 100644 index 00000000..ee18f9e5 --- /dev/null +++ b/code-samples/c-ffi-callbacks-bare-lambda-struct.pony @@ -0,0 +1,2 @@ +struct S + var fun_ptr: @{()} \ No newline at end of file diff --git a/code-samples/c-ffi-callbacks-sqlite3-callback-2.pony b/code-samples/c-ffi-callbacks-sqlite3-callback-2.pony new file mode 100644 index 00000000..ba50d9c4 --- /dev/null +++ b/code-samples/c-ffi-callbacks-sqlite3-callback-2.pony @@ -0,0 +1,16 @@ +use @sqlite3_exec[I32](db: Pointer[None] tag, sql: Pointer[U8] tag, + callback: Pointer[None], data: Pointer[None], err_msg: Pointer[Pointer[U8] tag] tag) + +class SQLiteClient + fun client_code() => + ... + let lambda_callback = + @{(client: SQLiteClient, argc: I32, argv: Pointer[Pointer[U8]], + azColName: Pointer[Pointer[U8]]): I32 + => + ... + } + + @sqlite3_exec(db, sql.cstring(), lambda_callback, this, + addressof zErrMsg) + ... \ No newline at end of file diff --git a/code-samples/c-ffi-callbacks-sqlite3-callback.pony b/code-samples/c-ffi-callbacks-sqlite3-callback.pony new file mode 100644 index 00000000..8b414e7d --- /dev/null +++ b/code-samples/c-ffi-callbacks-sqlite3-callback.pony @@ -0,0 +1,14 @@ +use @sqlite3_exec[I32](db: Pointer[None] tag, sql: Pointer[U8] tag, + callback: Pointer[None], data: Pointer[None], err_msg: Pointer[Pointer[U8] tag] tag) + +class SQLiteClient + fun client_code() => + ... + @sqlite3_exec(db, sql.cstring(), addressof this.method_callback, + this, addressof zErrMsg) + ... + + fun @method_callback(client: SQLiteClient, argc: I32, + argv: Pointer[Pointer[U8]], azColName: Pointer[Pointer[U8]]): I32 + => + ... \ No newline at end of file diff --git a/code-samples/calling-c-access-list-entry-with-explicit-return-type.pony b/code-samples/calling-c-access-list-entry-with-explicit-return-type.pony new file mode 100644 index 00000000..b0c4842e --- /dev/null +++ b/code-samples/calling-c-access-list-entry-with-explicit-return-type.pony @@ -0,0 +1,7 @@ +// OK +let point = @list_pop[Point](list_of_points) +let x_coord = point.x + +// OK +let pointer = @list_pop[Pointer[U8]](list_of_strings) +let data = String.from_cstring(pointer) \ No newline at end of file diff --git a/code-samples/calling-c-access-list-entry-without-return-type.pony b/code-samples/calling-c-access-list-entry-without-return-type.pony new file mode 100644 index 00000000..bcc07e9f --- /dev/null +++ b/code-samples/calling-c-access-list-entry-without-return-type.pony @@ -0,0 +1,6 @@ +// Compiler error: couldn't find 'x' in 'Pointer' +let point_x = @list_pop(list_of_points) +point.x + +// Compiler error: wanted Pointer[U8 val] ref^, got Pointer[None val] ref +let head = String.from_cstring(@list_pop(list_of_strings)) \ No newline at end of file diff --git a/code-samples/calling-c-addressof.pony b/code-samples/calling-c-addressof.pony new file mode 100644 index 00000000..d7f28aaa --- /dev/null +++ b/code-samples/calling-c-addressof.pony @@ -0,0 +1,4 @@ +use @frexp[F64](value: F64, exponent: Pointer[U32]) +// ... +var exponent: U32 = 0 +var mantissa = @frexp(this, addressof exponent) \ No newline at end of file diff --git a/code-samples/calling-c-default-method-in-primitive.pony b/code-samples/calling-c-default-method-in-primitive.pony new file mode 100644 index 00000000..91cc0962 --- /dev/null +++ b/code-samples/calling-c-default-method-in-primitive.pony @@ -0,0 +1,14 @@ +use @printf[I32](fmt: Pointer[None] tag, ...) + +trait Foo + fun apply() => + // OK + Printf("Hello from trait Foo\n") + +primitive Printf + fun apply(str: String) => + @printf(str.cstring()) + +actor Main is Foo + new create(env: Env) => + this.apply() \ No newline at end of file diff --git a/code-samples/calling-c-default-method-in-trait.pony b/code-samples/calling-c-default-method-in-trait.pony new file mode 100644 index 00000000..5275c105 --- /dev/null +++ b/code-samples/calling-c-default-method-in-trait.pony @@ -0,0 +1,10 @@ +use @printf[I32](fmt: Pointer[None] tag, ...) + +trait Foo + fun apply() => + // Error: Can't call an FFI function in a default method or behavior + @printf("Hello from trait Foo\n".cstring()) + +actor Main is Foo + new create(env: Env) => + this.apply() \ No newline at end of file diff --git a/code-samples/calling-c-different-types-of-lists.pony b/code-samples/calling-c-different-types-of-lists.pony new file mode 100644 index 00000000..e6424092 --- /dev/null +++ b/code-samples/calling-c-different-types-of-lists.pony @@ -0,0 +1,9 @@ +struct Point + var x: U64 = 0 + var y: U64 = 0 + +let list_of_points = @list_create() +@list_push(list_of_points, NullablePointer[Point].create(Point)) + +let list_of_strings = @list_create() +@list_push(list_of_strings, "some data".cstring()) \ No newline at end of file diff --git a/code-samples/calling-c-ffi-functions-raising-errors.pony b/code-samples/calling-c-ffi-functions-raising-errors.pony new file mode 100644 index 00000000..e86d7a30 --- /dev/null +++ b/code-samples/calling-c-ffi-functions-raising-errors.pony @@ -0,0 +1,4 @@ +use @pony_os_send[USize](event: AsioEventID, buffer: Pointer[U8] tag, size: USize) ? +// ... +// May raise an error +@pony_os_send(_event, data.cpointer(), data.size()) ? \ No newline at end of file diff --git a/code-samples/calling-c-file-path.pony b/code-samples/calling-c-file-path.pony new file mode 100644 index 00000000..285a5f95 --- /dev/null +++ b/code-samples/calling-c-file-path.pony @@ -0,0 +1,11 @@ +use @_mkdir[I32](dir: Pointer[U8] tag) if windows +use @mkdir[I32](path: Pointer[U8] tag, mode: U32) if not windows + +class val FilePath + fun val mkdir(must_create: Bool = false): Bool => + // ... + let r = ifdef windows then + @_mkdir(element.cstring()) + else + @mkdir(element.cstring(), 0x1FF) + end \ No newline at end of file diff --git a/code-samples/calling-c-from-c-struct.pony b/code-samples/calling-c-from-c-struct.pony new file mode 100644 index 00000000..fccbeb2b --- /dev/null +++ b/code-samples/calling-c-from-c-struct.pony @@ -0,0 +1,5 @@ +use @from_c[Rect]() + +struct Rect + var length: U16 + var width: U16 \ No newline at end of file diff --git a/code-samples/calling-c-generic-list.pony b/code-samples/calling-c-generic-list.pony new file mode 100644 index 00000000..8dcae902 --- /dev/null +++ b/code-samples/calling-c-generic-list.pony @@ -0,0 +1,7 @@ +use @list_create[Pointer[_List]]() +use @list_free[None](list: Pointer[_List]) + +use @list_push[None](list: Pointer[_List], data: Pointer[None]) +use @list_pop[Pointer[None]](list: Pointer[_List]) + +primitive _List \ No newline at end of file diff --git a/code-samples/calling-c-ioctl-struct.pony b/code-samples/calling-c-ioctl-struct.pony new file mode 100644 index 00000000..b7debc8b --- /dev/null +++ b/code-samples/calling-c-ioctl-struct.pony @@ -0,0 +1,13 @@ +use @ioctl[I32](fd: I32, req: U32, ...) + +struct Winsize + var height: U16 = 0 + var width: U16 = 0 + + new create() => None + +let size = Winsize + +@ioctl(0, 21523, NullablePointer[Winsize](size)) + +env.out.print(size.height.string()) \ No newline at end of file diff --git a/code-samples/calling-c-memcpy.pony b/code-samples/calling-c-memcpy.pony new file mode 100644 index 00000000..7591d6be --- /dev/null +++ b/code-samples/calling-c-memcpy.pony @@ -0,0 +1,11 @@ +// The C type is void* memcpy(void *restrict dst, const void *restrict src, size_t n); +use @memcpy[Pointer[U8]](dst: Pointer[None] tag, src: Pointer[None] tag, n: USize) + +// Now we can use memcpy with any Pointer type +let out: Pointer[Pointer[U8] tag] tag = // ... +let outlen: Pointer[U8] tag = // ... +let ptr: Pointer[U8] tag = // ... +let size: USize = // ... +// ... +@memcpy(out, addressof ptr, size.bitwidth() / 8) +@memcpy(outlen, addressof size, 1) \ No newline at end of file diff --git a/code-samples/calling-c-pointer-to-opaque-c-type.pony b/code-samples/calling-c-pointer-to-opaque-c-type.pony new file mode 100644 index 00000000..9e99ed6d --- /dev/null +++ b/code-samples/calling-c-pointer-to-opaque-c-type.pony @@ -0,0 +1,15 @@ +use @XOpenDisplay[Pointer[_XDisplayHandle]](name: Pointer[U8] tag) +use @eglGetDisplay[Pointer[_EGLDisplayHandle]](disp: Pointer[_XDisplayHandle]) + +primitive _XDisplayHandle +primitive _EGLDisplayHandle + +let x_dpy = @XOpenDisplay(Pointer[U8]) +if x_dpy.is_null() then + env.out.print("XOpenDisplay failed") +end + +let e_dpy = @eglGetDisplay(x_dpy) +if e_dpy.is_null() then + env.out.print("eglGetDisplay failed") +end \ No newline at end of file diff --git a/code-samples/calling-c-type-signature-compatibility.pony b/code-samples/calling-c-type-signature-compatibility.pony new file mode 100644 index 00000000..025f9bcc --- /dev/null +++ b/code-samples/calling-c-type-signature-compatibility.pony @@ -0,0 +1,5 @@ +// In library lib_a +use @memcmp[I32](dst: Pointer[None] tag, src: Pointer[None] tag, len: USize) + +// In library lib_b +use @memcmp[I32](dst: Pointer[None] tag, src: USize, len: U64) \ No newline at end of file diff --git a/code-samples/calling-c-variadic-c-functions.pony b/code-samples/calling-c-variadic-c-functions.pony new file mode 100644 index 00000000..f8e59f50 --- /dev/null +++ b/code-samples/calling-c-variadic-c-functions.pony @@ -0,0 +1,5 @@ +use @printf[I32](fmt: Pointer[U8] tag, ...) +// ... +let run_ns: I64 = _current_t - _last_t +let rate: I64 = (_partial_count.i64() * 1_000_000_000) / run_ns +@printf("Elapsed: %lld,%lld\n".cstring(), run_ns, rate) \ No newline at end of file diff --git a/code-samples/calling-c-writev-struct.pony b/code-samples/calling-c-writev-struct.pony new file mode 100644 index 00000000..125be5b2 --- /dev/null +++ b/code-samples/calling-c-writev-struct.pony @@ -0,0 +1,17 @@ +// In C: ssize_t writev(int fd, const struct iovec *iov, int iovcnt) +use @writev[USize](fd: U32, iov: IOVec tag, iovcnt: I32) + +// In C: +// struct iovec { +// void *iov_base; /* Starting address */ +// size_t iov_len; /* Number of bytes to transfer */ +// }; +struct IOVec + var base: Pointer[U8] tag = Pointer[U8] + var len: USize = 0 + +let data = "Hello from Pony!" +var iov = IOVec +iov.base = data.cpointer() +iov.len = data.size() +@writev(1, iov, 1) // Will print "Hello from Pony!" \ No newline at end of file diff --git a/code-samples/calling-c-writev-tuple.pony b/code-samples/calling-c-writev-tuple.pony new file mode 100644 index 00000000..531f5965 --- /dev/null +++ b/code-samples/calling-c-writev-tuple.pony @@ -0,0 +1,5 @@ +use @writev[USize](fd: U32, iov: Pointer[(Pointer[U8] tag, USize)] tag, iovcnt: I32) + +let data = "Hello from Pony!" +var iov = (data.cpointer(), data.size()) +@writev(1, addressof iov, 1) // Will print "Hello from Pony!" \ No newline at end of file diff --git a/code-samples/classes-swap-values-sugar.pony b/code-samples/classes-swap-values-sugar.pony new file mode 100644 index 00000000..9c8eb05c --- /dev/null +++ b/code-samples/classes-swap-values-sugar.pony @@ -0,0 +1 @@ +a = b = a \ No newline at end of file diff --git a/code-samples/classes-swap-values.pony b/code-samples/classes-swap-values.pony new file mode 100644 index 00000000..97937f3c --- /dev/null +++ b/code-samples/classes-swap-values.pony @@ -0,0 +1,3 @@ +var temp = a +a = b +b = temp \ No newline at end of file diff --git a/code-samples/classes-wombat-constructor-invocation.pony b/code-samples/classes-wombat-constructor-invocation.pony new file mode 100644 index 00000000..ed12e112 --- /dev/null +++ b/code-samples/classes-wombat-constructor-invocation.pony @@ -0,0 +1,2 @@ +let defaultWombat = Wombat("Fantastibat") // Invokes the create method by default +let hungryWombat = Wombat.hungry("Nomsbat", 12) // Invokes the hungry method \ No newline at end of file diff --git a/code-samples/classes-wombat-constructors.pony b/code-samples/classes-wombat-constructors.pony new file mode 100644 index 00000000..4e9c3863 --- /dev/null +++ b/code-samples/classes-wombat-constructors.pony @@ -0,0 +1,11 @@ +class Wombat + let name: String + var _hunger_level: U64 + + new create(name': String) => + name = name' + _hunger_level = 0 + + new hungry(name': String, hunger': U64) => + name = name' + _hunger_level = hunger' \ No newline at end of file diff --git a/code-samples/classes-wombat.pony b/code-samples/classes-wombat.pony new file mode 100644 index 00000000..1c647876 --- /dev/null +++ b/code-samples/classes-wombat.pony @@ -0,0 +1,16 @@ +class Wombat + let name: String + var _hunger_level: U64 + var _thirst_level: U64 = 1 + + new create(name': String) => + name = name' + _hunger_level = 0 + + new hungry(name': String, hunger': U64) => + name = name' + _hunger_level = hunger' + + fun hunger(): U64 => _hunger_level + + fun ref set_hunger(to: U64 = 0): U64 => _hunger_level = to \ No newline at end of file diff --git a/code-samples/classes-zero-argument-constructors-invocation.pony b/code-samples/classes-zero-argument-constructors-invocation.pony new file mode 100644 index 00000000..67b82087 --- /dev/null +++ b/code-samples/classes-zero-argument-constructors-invocation.pony @@ -0,0 +1,3 @@ +class Forest + let _owl: Owl = Owl + let _hawk: Hawk = Hawk \ No newline at end of file diff --git a/code-samples/classes-zero-argument-constructors.pony b/code-samples/classes-zero-argument-constructors.pony new file mode 100644 index 00000000..58521846 --- /dev/null +++ b/code-samples/classes-zero-argument-constructors.pony @@ -0,0 +1,8 @@ +class Hawk + var _hunger_level: U64 = 0 + +class Owl + var _hunger_level: U64 + + new create() => + _hunger_level = 42 \ No newline at end of file diff --git a/code-samples/consume-and-destructive-read-consuming-a-variable-and-change-its-reference-capability.pony b/code-samples/consume-and-destructive-read-consuming-a-variable-and-change-its-reference-capability.pony new file mode 100644 index 00000000..81db33a8 --- /dev/null +++ b/code-samples/consume-and-destructive-read-consuming-a-variable-and-change-its-reference-capability.pony @@ -0,0 +1,2 @@ +fun test(a: AnIncrediblyLongTypeName iso) => + var b = consume val a \ No newline at end of file diff --git a/code-samples/consume-and-destructive-read-consuming-a-variable.pony b/code-samples/consume-and-destructive-read-consuming-a-variable.pony new file mode 100644 index 00000000..c4134247 --- /dev/null +++ b/code-samples/consume-and-destructive-read-consuming-a-variable.pony @@ -0,0 +1,3 @@ +fun test(a: Wombat iso) => + var b: Wombat iso = consume a // Allowed! + var c: Wombat tag = a // Not allowed! \ No newline at end of file diff --git a/code-samples/consume-and-destructive-read-moving-a-value.pony b/code-samples/consume-and-destructive-read-moving-a-value.pony new file mode 100644 index 00000000..fc6958a1 --- /dev/null +++ b/code-samples/consume-and-destructive-read-moving-a-value.pony @@ -0,0 +1,8 @@ +class Aardvark + var buddy: Wombat iso + + new create() => + buddy = recover Wombat end + + fun ref test(a: Wombat iso) => + var b: Wombat iso = buddy = consume a // Allowed! \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-expression-implicit-none.pony b/code-samples/control-structures-conditionals-expression-implicit-none.pony new file mode 100644 index 00000000..bb9d0837 --- /dev/null +++ b/code-samples/control-structures-conditionals-expression-implicit-none.pony @@ -0,0 +1,4 @@ +var x: (String | None) = + if friendly then + "Hello" + end \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-expression-union-type.pony b/code-samples/control-structures-conditionals-expression-union-type.pony new file mode 100644 index 00000000..ee2ea45a --- /dev/null +++ b/code-samples/control-structures-conditionals-expression-union-type.pony @@ -0,0 +1,6 @@ +var x: (String | Bool) = + if friendly then + "Hello" + else + false + end \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-expressions.pony b/code-samples/control-structures-conditionals-expressions.pony new file mode 100644 index 00000000..fb1ca3e1 --- /dev/null +++ b/code-samples/control-structures-conditionals-expressions.pony @@ -0,0 +1 @@ +x = 1 + if lots then 100 else 2 end \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-if-else.pony b/code-samples/control-structures-conditionals-if-else.pony new file mode 100644 index 00000000..14033612 --- /dev/null +++ b/code-samples/control-structures-conditionals-if-else.pony @@ -0,0 +1,5 @@ +if a > b then + env.out.print("a is bigger") +else + env.out.print("a is not bigger") +end \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-if-elseif-else.pony b/code-samples/control-structures-conditionals-if-elseif-else.pony new file mode 100644 index 00000000..f9d0a01f --- /dev/null +++ b/code-samples/control-structures-conditionals-if-elseif-else.pony @@ -0,0 +1,7 @@ +if a == b then + env.out.print("they are the same") +elseif a > b then + env.out.print("a is bigger") +else + env.out.print("b bigger") +end \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-if.pony b/code-samples/control-structures-conditionals-if.pony new file mode 100644 index 00000000..a2a672f2 --- /dev/null +++ b/code-samples/control-structures-conditionals-if.pony @@ -0,0 +1,3 @@ +if a > b then + env.out.print("a is bigger") +end \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-nested-if-else.pony b/code-samples/control-structures-conditionals-nested-if-else.pony new file mode 100644 index 00000000..df7477bd --- /dev/null +++ b/code-samples/control-structures-conditionals-nested-if-else.pony @@ -0,0 +1,9 @@ +if a == b then + env.out.print("they are the same") +else + if a > b then + env.out.print("a is bigger") + else + env.out.print("b bigger") + end +end \ No newline at end of file diff --git a/code-samples/control-structures-iterator-methods.pony b/code-samples/control-structures-iterator-methods.pony new file mode 100644 index 00000000..2c80ff74 --- /dev/null +++ b/code-samples/control-structures-iterator-methods.pony @@ -0,0 +1,2 @@ + fun has_next(): Bool + fun next(): T? \ No newline at end of file diff --git a/code-samples/control-structures-loop-expression-else.pony b/code-samples/control-structures-loop-expression-else.pony new file mode 100644 index 00000000..db09c37d --- /dev/null +++ b/code-samples/control-structures-loop-expression-else.pony @@ -0,0 +1,9 @@ +actor Main + new create(env: Env) => + var x: String = + for name in Array[String].values() do + name + else + "no names!" + end + env.out.print("x is " + x) \ No newline at end of file diff --git a/code-samples/control-structures-loop-expression-none.pony b/code-samples/control-structures-loop-expression-none.pony new file mode 100644 index 00000000..1436ab7a --- /dev/null +++ b/code-samples/control-structures-loop-expression-none.pony @@ -0,0 +1,10 @@ +actor Main + new create(env: Env) => + var x: (String | None) = + for name in Array[String].values() do + name + end + match x + | let s: String => env.out.print("x is " + s) + | None => env.out.print("x is None") + end \ No newline at end of file diff --git a/code-samples/control-structures-loop-expression.pony b/code-samples/control-structures-loop-expression.pony new file mode 100644 index 00000000..c1a222d9 --- /dev/null +++ b/code-samples/control-structures-loop-expression.pony @@ -0,0 +1,10 @@ +actor Main + new create(env: Env) => + var x: (String | None) = + for name in ["Bob"; "Fred"; "Sarah"].values() do + name + end + match x + | let s: String => env.out.print("x is " + s) + | None => env.out.print("x is None") + end \ No newline at end of file diff --git a/code-samples/control-structures-loops-for-while-comparison.pony b/code-samples/control-structures-loops-for-while-comparison.pony new file mode 100644 index 00000000..91d327cb --- /dev/null +++ b/code-samples/control-structures-loops-for-while-comparison.pony @@ -0,0 +1,5 @@ +let iterator = ["Bob"; "Fred"; "Sarah"].values() +while iterator.has_next() do + let name = iterator.next()? + env.out.print(name) +end \ No newline at end of file diff --git a/code-samples/control-structures-loops-for.pony b/code-samples/control-structures-loops-for.pony new file mode 100644 index 00000000..64d97f3a --- /dev/null +++ b/code-samples/control-structures-loops-for.pony @@ -0,0 +1,3 @@ +for name in ["Bob"; "Fred"; "Sarah"].values() do + env.out.print(name) +end \ No newline at end of file diff --git a/code-samples/control-structures-loops-repeat.pony b/code-samples/control-structures-loops-repeat.pony new file mode 100644 index 00000000..2dc76a8f --- /dev/null +++ b/code-samples/control-structures-loops-repeat.pony @@ -0,0 +1,7 @@ +actor Main + new create(env: Env) => + var counter = U64(1) + repeat + env.out.print("hello!") + counter = counter + 1 + until counter > 7 end \ No newline at end of file diff --git a/code-samples/control-structures-loops-while-break-else.pony b/code-samples/control-structures-loops-while-break-else.pony new file mode 100644 index 00000000..357cd5c0 --- /dev/null +++ b/code-samples/control-structures-loops-while-break-else.pony @@ -0,0 +1,10 @@ +var name = + while moreNames() do + var name' = getName() + if name' == "Jack" or name' == "Jill" then + break name' + end + name' + else + "Herbert" + end \ No newline at end of file diff --git a/code-samples/control-structures-loops-while.pony b/code-samples/control-structures-loops-while.pony new file mode 100644 index 00000000..881ef4b5 --- /dev/null +++ b/code-samples/control-structures-loops-while.pony @@ -0,0 +1,6 @@ +var count: U32 = 1 + +while count <= 10 do + env.out.print(count.string()) + count = count + 1 +end \ No newline at end of file diff --git a/code-samples/derived-authority-authority-hierarchies.pony b/code-samples/derived-authority-authority-hierarchies.pony new file mode 100644 index 00000000..65975fa4 --- /dev/null +++ b/code-samples/derived-authority-authority-hierarchies.pony @@ -0,0 +1,23 @@ +primitive NetAuth + new create(from: AmbientAuth) => + None + +primitive DNSAuth + new create(from: (AmbientAuth | NetAuth)) => + None + +primitive UDPAuth + new create(from: (AmbientAuth | NetAuth)) => + None + +primitive TCPAuth + new create(from: (AmbientAuth | NetAuth)) => + None + +primitive TCPListenAuth + new create(from: (AmbientAuth | NetAuth | TCPAuth)) => + None + +primitive TCPConnectAuth + new create(from: (AmbientAuth | NetAuth | TCPAuth)) => + None \ No newline at end of file diff --git a/code-samples/derived-authority-delegating-and-restricting-authority.pony b/code-samples/derived-authority-delegating-and-restricting-authority.pony new file mode 100644 index 00000000..b4440815 --- /dev/null +++ b/code-samples/derived-authority-delegating-and-restricting-authority.pony @@ -0,0 +1,22 @@ +use "net" + +class MyTCPConnectionNotify is TCPConnectionNotify + let _out: OutStream + + new iso create(out: OutStream) => + _out = out + + fun ref connected(conn: TCPConnection ref) => + _out.print("connected") + conn.close() + + fun ref connect_failed(conn: TCPConnection ref) => + _out.print("connect_failed") + +actor Connect + new create(out: OutStream, auth: TCPConnectAuth) => + TCPConnection(auth, MyTCPConnectionNotify(out), "example.com", "80") + +actor Main + new create(env: Env) => + Connect(env.out, TCPConnectAuth(env.root)) \ No newline at end of file diff --git a/code-samples/derived-authority-restrict-then-delegate-your-authority.pony b/code-samples/derived-authority-restrict-then-delegate-your-authority.pony new file mode 100644 index 00000000..9239f4f7 --- /dev/null +++ b/code-samples/derived-authority-restrict-then-delegate-your-authority.pony @@ -0,0 +1,7 @@ +actor Connect + new create(out: OutStream, auth: TCPConnectAuth) => + TCPConnection(auth, MyTCPConnectionNotify(out), "example.com", "80") + +actor Main + new create(env: Env) => + try Connect(env.out, TCPConnectAuth(env.root)) end \ No newline at end of file diff --git a/code-samples/divide-by-zero-floats.pony b/code-samples/divide-by-zero-floats.pony new file mode 100644 index 00000000..f608e0da --- /dev/null +++ b/code-samples/divide-by-zero-floats.pony @@ -0,0 +1 @@ +let x = F64(1.5) /~ F64(0.5) \ No newline at end of file diff --git a/code-samples/divide-by-zero-partial.pony b/code-samples/divide-by-zero-partial.pony new file mode 100644 index 00000000..38df575c --- /dev/null +++ b/code-samples/divide-by-zero-partial.pony @@ -0,0 +1,6 @@ +let x = + try + I64(1) /? I64(0) + else + // handle division by zero + end \ No newline at end of file diff --git a/code-samples/divide-by-zero-unsafe.pony b/code-samples/divide-by-zero-unsafe.pony new file mode 100644 index 00000000..10ca229d --- /dev/null +++ b/code-samples/divide-by-zero-unsafe.pony @@ -0,0 +1,2 @@ +// the value of x is undefined +let x = I64(1) /~ I64(0) \ No newline at end of file diff --git a/code-samples/divide-by-zero.pony b/code-samples/divide-by-zero.pony new file mode 100644 index 00000000..b0f8757f --- /dev/null +++ b/code-samples/divide-by-zero.pony @@ -0,0 +1 @@ +let x = I64(1) / I64(0) \ No newline at end of file diff --git a/code-samples/equality-equatable-default-implementation.pony b/code-samples/equality-equatable-default-implementation.pony new file mode 100644 index 00000000..fedc322a --- /dev/null +++ b/code-samples/equality-equatable-default-implementation.pony @@ -0,0 +1,3 @@ +interface Equatable[A: Equatable[A] #read] + fun eq(that: box->A): Bool => this is that + fun ne(that: box->A): Bool => not eq(that) \ No newline at end of file diff --git a/code-samples/equality-identity-equality.pony b/code-samples/equality-identity-equality.pony new file mode 100644 index 00000000..8028ab59 --- /dev/null +++ b/code-samples/equality-identity-equality.pony @@ -0,0 +1,16 @@ +if None is None then + // TRUE! + // There is only 1 None so the identity is the same +end + +let a = Foo("hi") +let b = Foo("hi") + +if a is b then + // NOPE. THIS IS FALSE +end + +let c = a +if a is c then + // YUP! TRUE! +end \ No newline at end of file diff --git a/code-samples/equality-primitives.pony b/code-samples/equality-primitives.pony new file mode 100644 index 00000000..899a7d22 --- /dev/null +++ b/code-samples/equality-primitives.pony @@ -0,0 +1,7 @@ +if None is None then + // this is always true +end + +if None == None then + // this is also always true +end \ No newline at end of file diff --git a/code-samples/equality-structural-equality.pony b/code-samples/equality-structural-equality.pony new file mode 100644 index 00000000..5359c494 --- /dev/null +++ b/code-samples/equality-structural-equality.pony @@ -0,0 +1,29 @@ +class Foo + let _a: String + + new create(a: String) => + _a = a + + fun eq(that: box->Foo): Bool => + this._a == that._a + +actor Main + new create(e: Env) => + let a = Foo("hi") + let b = Foo("bye") + let c = Foo("hi") + + if a == b then + // won't print + e.out.print("1") + end + + if a == c then + // will print + e.out.print("2") + end + + if a is c then + // won't print + e.out.print("3") + end \ No newline at end of file diff --git a/code-samples/error-messages-left-side-is-immutable.pony b/code-samples/error-messages-left-side-is-immutable.pony new file mode 100644 index 00000000..c59cb172 --- /dev/null +++ b/code-samples/error-messages-left-side-is-immutable.pony @@ -0,0 +1,4 @@ +class Wombat + var color: String = "brown" + fun dye(new_color: String) => + color = new_color \ No newline at end of file diff --git a/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony b/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony new file mode 100644 index 00000000..84c692c2 --- /dev/null +++ b/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony @@ -0,0 +1,4 @@ +actor Main + let x: I64 = 0 + new create(env: Env) => + x = 12 \ No newline at end of file diff --git a/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type.pony b/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type.pony new file mode 100644 index 00000000..6260dcf5 --- /dev/null +++ b/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type.pony @@ -0,0 +1,4 @@ +class Rainbow + let colors: Array[String] = Array[String] + fun add_stripe(color: String) => + colors.push(color) \ No newline at end of file diff --git a/code-samples/errors-dispose-multiple.pony b/code-samples/errors-dispose-multiple.pony new file mode 100644 index 00000000..fc162f66 --- /dev/null +++ b/code-samples/errors-dispose-multiple.pony @@ -0,0 +1,3 @@ +with obj = SomeObjectThatNeedsDisposing(), other = SomeOtherDisposableObject() do + // use obj and other +end \ No newline at end of file diff --git a/code-samples/errors-dispose.pony b/code-samples/errors-dispose.pony new file mode 100644 index 00000000..c934a658 --- /dev/null +++ b/code-samples/errors-dispose.pony @@ -0,0 +1,5 @@ +class SomeObjectThatNeedsDisposing + // constructor, other functions + + fun dispose() => + // release resources \ No newline at end of file diff --git a/code-samples/errors-partial-functions.pony b/code-samples/errors-partial-functions.pony new file mode 100644 index 00000000..54dccb3b --- /dev/null +++ b/code-samples/errors-partial-functions.pony @@ -0,0 +1,7 @@ +fun factorial(x: I32): I32 ? => + if x < 0 then error end + if x == 0 then + 1 + else + x * factorial(x - 1)? + end \ No newline at end of file diff --git a/code-samples/errors-try-else.pony b/code-samples/errors-try-else.pony new file mode 100644 index 00000000..551688d3 --- /dev/null +++ b/code-samples/errors-try-else.pony @@ -0,0 +1,7 @@ +try + callA() + if not callB() then error end + callC() +else + callD() +end \ No newline at end of file diff --git a/code-samples/errors-try-then.pony b/code-samples/errors-try-then.pony new file mode 100644 index 00000000..3e3db5f0 --- /dev/null +++ b/code-samples/errors-try-then.pony @@ -0,0 +1,9 @@ +try + callA() + if not callB() then error end + callC() +else + callD() +then + callE() +end \ No newline at end of file diff --git a/code-samples/errors-try-without-else.pony b/code-samples/errors-try-without-else.pony new file mode 100644 index 00000000..1e858957 --- /dev/null +++ b/code-samples/errors-try-without-else.pony @@ -0,0 +1,3 @@ +try + // Do something that may raise an error +end \ No newline at end of file diff --git a/code-samples/errors-with-blocks.pony b/code-samples/errors-with-blocks.pony new file mode 100644 index 00000000..92a4400d --- /dev/null +++ b/code-samples/errors-with-blocks.pony @@ -0,0 +1,3 @@ +with obj = SomeObjectThatNeedsDisposing() do + // use obj +end \ No newline at end of file diff --git a/code-samples/function-call-side-effects.pony b/code-samples/function-call-side-effects.pony new file mode 100644 index 00000000..300f4159 --- /dev/null +++ b/code-samples/function-call-side-effects.pony @@ -0,0 +1,10 @@ +class Foo + fun fn(x: U64) => None + +actor Main + new create(env: Env) => + var x: U64 = 0 + try foo()?.fn(x = 42) end + env.out.print(x.string()) + + fun foo(): Foo ? => error \ No newline at end of file diff --git a/code-samples/garbage-collection.pony b/code-samples/garbage-collection.pony new file mode 100644 index 00000000..1701accb --- /dev/null +++ b/code-samples/garbage-collection.pony @@ -0,0 +1,7 @@ +use "collections" + +actor Main + new create(env: Env) => + for i in Range(1, 2_000_000) do + ... something that uses up heap ... + end \ No newline at end of file diff --git a/code-samples/generic-constraints-foo-any-read.pony b/code-samples/generic-constraints-foo-any-read.pony new file mode 100644 index 00000000..96a2df37 --- /dev/null +++ b/code-samples/generic-constraints-foo-any-read.pony @@ -0,0 +1,17 @@ +class Foo[A: Any #read] + var _c: A + + new create(c: A) => + _c = c + + fun get(): this->A => _c + + fun ref set(c: A) => _c = c + +actor Main + new create(env:Env) => + let a = Foo[String ref](recover ref "hello".clone() end) + env.out.print(a.get().string()) + + let b = Foo[String val]("World") + env.out.print(b.get().string()) \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-accept-any-reference-capability.pony b/code-samples/generics-and-reference-capabilities-accept-any-reference-capability.pony new file mode 100644 index 00000000..270694f8 --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-accept-any-reference-capability.pony @@ -0,0 +1,17 @@ +// Note - this won't compile +class Foo[A] + var _c: A + + new create(c: A) => + _c = c + + fun get(): A => _c + + fun ref set(c: A) => _c = c + +actor Main + new create(env: Env) => + let a = Foo[U32](42) + env.out.print(a.get().string()) + a.set(21) + env.out.print(a.get().string()) \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-capability-generic-class.pony b/code-samples/generics-and-reference-capabilities-capability-generic-class.pony new file mode 100644 index 00000000..253f89f8 --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-capability-generic-class.pony @@ -0,0 +1,20 @@ +class Foo[A] + var _c: A + + new create(c: A) => + _c = consume c + + fun get(): this->A => _c + + fun ref set(c: A) => _c = consume c + +actor Main + new create(env: Env) => + let a = Foo[String iso]("Hello".clone()) + env.out.print(a.get().string()) + + let b = Foo[String ref](recover ref "World".clone() end) + env.out.print(b.get().string()) + + let c = Foo[U8](42) + env.out.print(c.get().string()) \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-default-capability-and-constraint.pony b/code-samples/generics-and-reference-capabilities-default-capability-and-constraint.pony new file mode 100644 index 00000000..c5344ec6 --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-default-capability-and-constraint.pony @@ -0,0 +1 @@ +class Foo[A] \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-explicit-constraint-and-default-capability.pony b/code-samples/generics-and-reference-capabilities-explicit-constraint-and-default-capability.pony new file mode 100644 index 00000000..19e2d296 --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-explicit-constraint-and-default-capability.pony @@ -0,0 +1 @@ +class Foo[A: Any] \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-foo-iso-consume-iso-constructor-parameter.pony b/code-samples/generics-and-reference-capabilities-foo-iso-consume-iso-constructor-parameter.pony new file mode 100644 index 00000000..c0545f37 --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-foo-iso-consume-iso-constructor-parameter.pony @@ -0,0 +1,16 @@ +class Foo + var _c: String iso + + new create(c: String iso) => + _c = consume c + + fun get(): this->String iso => _c + + fun ref set(c: String iso) => _c = consume c + +actor Main + new create(env: Env) => + let a = Foo(recover iso String end) + env.out.print(a.get().string()) + a.set(recover iso String end) + env.out.print(a.get().string()) \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-foo-iso-consume-iso-function-parameter.pony b/code-samples/generics-and-reference-capabilities-foo-iso-consume-iso-function-parameter.pony new file mode 100644 index 00000000..2a642e14 --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-foo-iso-consume-iso-function-parameter.pony @@ -0,0 +1 @@ +fun set(c: String iso) => _c = consume c \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-foo-iso-error-message.txt b/code-samples/generics-and-reference-capabilities-foo-iso-error-message.txt new file mode 100644 index 00000000..4c372132 --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-foo-iso-error-message.txt @@ -0,0 +1,7 @@ +main.pony:5:8: right side must be a subtype of left side + _c = c + ^ + Info: + main.pony:4:17: String iso! is not a subtype of String iso: iso! is not a subtype of iso + new create(c: String iso) => + ^ \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-foo-iso.pony b/code-samples/generics-and-reference-capabilities-foo-iso.pony new file mode 100644 index 00000000..488f9eeb --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-foo-iso.pony @@ -0,0 +1,17 @@ +// Note - this won't compile +class Foo + var _c: String iso + + new create(c: String iso) => + _c = c + + fun get(): this->String iso => _c + + fun ref set(c: String iso) => _c = c + +actor Main + new create(env: Env) => + let a = Foo(recover iso String end) + env.out.print(a.get().string()) + a.set(recover iso String end) + env.out.print(a.get().string()) \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-foo-ref-and-this-ref.pony b/code-samples/generics-and-reference-capabilities-foo-ref-and-this-ref.pony new file mode 100644 index 00000000..bfeb59af --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-foo-ref-and-this-ref.pony @@ -0,0 +1,16 @@ +class Foo + var _c: String ref + + new create(c: String ref) => + _c = c + + fun get(): this->String ref => _c + + fun ref set(c: String ref) => _c = c + +actor Main + new create(env: Env) => + let a = Foo(recover ref String end) + env.out.print(a.get().string()) + a.set(recover ref String end) + env.out.print(a.get().string()) \ No newline at end of file diff --git a/code-samples/generics-and-reference-capabilities-foo-ref.pony b/code-samples/generics-and-reference-capabilities-foo-ref.pony new file mode 100644 index 00000000..74677f05 --- /dev/null +++ b/code-samples/generics-and-reference-capabilities-foo-ref.pony @@ -0,0 +1,17 @@ +// Note - this also won't compile +class Foo + var _c: String ref + + new create(c: String ref) => + _c = c + + fun get(): String ref => _c + + fun ref set(c: String ref) => _c = c + +actor Main + new create(env: Env) => + let a = Foo(recover ref String end) + env.out.print(a.get().string()) + a.set(recover ref String end) + env.out.print(a.get().string()) \ No newline at end of file diff --git a/code-samples/generics-foo-non-generic.pony b/code-samples/generics-foo-non-generic.pony new file mode 100644 index 00000000..d228ce51 --- /dev/null +++ b/code-samples/generics-foo-non-generic.pony @@ -0,0 +1,16 @@ +class Foo + var _c: U32 + + new create(c: U32) => + _c = c + + fun get(): U32 => _c + + fun ref set(c: U32) => _c = c + +actor Main + new create(env:Env) => + let a = Foo(42) + env.out.print(a.get().string()) + a.set(21) + env.out.print(a.get().string()) \ No newline at end of file diff --git a/code-samples/generics-foo-string.pony b/code-samples/generics-foo-string.pony new file mode 100644 index 00000000..4d4fa9c5 --- /dev/null +++ b/code-samples/generics-foo-string.pony @@ -0,0 +1,9 @@ +class FooString + var _c: String val + + new create(c: String val) => + _c = c + + fun get(): String val => _c + + fun ref set(c: String val) => _c = c \ No newline at end of file diff --git a/code-samples/generics-foo-with-any-val.pony b/code-samples/generics-foo-with-any-val.pony new file mode 100644 index 00000000..b13b82ac --- /dev/null +++ b/code-samples/generics-foo-with-any-val.pony @@ -0,0 +1,22 @@ +class Foo[A: Any val] + var _c: A + + new create(c: A) => + _c = c + + fun get(): A => _c + + fun ref set(c: A) => _c = c + +actor Main + new create(env:Env) => + let a = Foo[U32](42) + env.out.print(a.get().string()) + a.set(21) + + env.out.print(a.get().string()) + let b = Foo[F32](1.5) + env.out.print(b.get().string()) + + let c = Foo[String]("Hello") + env.out.print(c.get().string()) \ No newline at end of file diff --git a/code-samples/generics-generic-class-initialization.pony b/code-samples/generics-generic-class-initialization.pony new file mode 100644 index 00000000..fd97d742 --- /dev/null +++ b/code-samples/generics-generic-class-initialization.pony @@ -0,0 +1,3 @@ +let a = Foo[U32](42) +let b = Foo[F32](1.5) +let c = Foo[String]("Hello") \ No newline at end of file diff --git a/code-samples/generics-generic-methods.pony b/code-samples/generics-generic-methods.pony new file mode 100644 index 00000000..02c7c763 --- /dev/null +++ b/code-samples/generics-generic-methods.pony @@ -0,0 +1,11 @@ +primitive Foo + fun bar[A: Stringable val](a: A): String => + a.string() + +actor Main + new create(env:Env) => + let a = Foo.bar[U32](10) + env.out.print(a.string()) + + let b = Foo.bar[String]("Hello") + env.out.print(b.string()) \ No newline at end of file diff --git a/code-samples/generics-type-parameter-defaults-definition.pony b/code-samples/generics-type-parameter-defaults-definition.pony new file mode 100644 index 00000000..c6f4ac5d --- /dev/null +++ b/code-samples/generics-type-parameter-defaults-definition.pony @@ -0,0 +1,9 @@ +class Bar[A: Any box = USize val] + var _c: A + + new create(c: A) => + _c = c + + fun get(): A => _c + + fun ref set(c: A) => _c = c \ No newline at end of file diff --git a/code-samples/generics-type-parameter-defaults-initialization.pony b/code-samples/generics-type-parameter-defaults-initialization.pony new file mode 100644 index 00000000..94ccad6c --- /dev/null +++ b/code-samples/generics-type-parameter-defaults-initialization.pony @@ -0,0 +1,3 @@ +let a = Bar(42) +let b = Bar[USize](42) +let c = Bar[F32](1.5) \ No newline at end of file diff --git a/code-samples/hello-world-main.pony b/code-samples/hello-world-main.pony new file mode 100644 index 00000000..38b6be89 --- /dev/null +++ b/code-samples/hello-world-main.pony @@ -0,0 +1,3 @@ +actor Main + new create(env: Env) => + env.out.print("Hello, world!") \ No newline at end of file diff --git a/code-samples/linking-c-use-lib-foo.pony b/code-samples/linking-c-use-lib-foo.pony new file mode 100644 index 00000000..efe27551 --- /dev/null +++ b/code-samples/linking-c-use-lib-foo.pony @@ -0,0 +1 @@ +use "lib:foo" \ No newline at end of file diff --git a/code-samples/linking-c-use-with-condition.pony b/code-samples/linking-c-use-with-condition.pony new file mode 100644 index 00000000..440f0c4b --- /dev/null +++ b/code-samples/linking-c-use-with-condition.pony @@ -0,0 +1,16 @@ +use "path:/usr/local/opt/libressl/lib" if osx +use "lib:ssl" if not windows +use "lib:crypto" if not windows +use "lib:libssl-32" if windows +use "lib:libcrypto-32" if windows + +use @SSL_load_error_strings[None]() +use @SSL_library_init[I32]() + +primitive _SSLInit + """ + This initialises SSL when the program begins. + """ + fun _init() => + @SSL_load_error_strings() + @SSL_library_init() \ No newline at end of file diff --git a/code-samples/literals-array-literals.pony b/code-samples/literals-array-literals.pony new file mode 100644 index 00000000..dd3bf10d --- /dev/null +++ b/code-samples/literals-array-literals.pony @@ -0,0 +1,5 @@ +let my_literal_array = + [ + "first"; "second" + "third one on a new line" + ] \ No newline at end of file diff --git a/code-samples/literals-as-expression.pony b/code-samples/literals-as-expression.pony new file mode 100644 index 00000000..829ae0c1 --- /dev/null +++ b/code-samples/literals-as-expression.pony @@ -0,0 +1,6 @@ +let my_as_array = + [ as Stringable: + U64(0xFFEF) + "0xFFEF" + U64(1 + 1) + ] \ No newline at end of file diff --git a/code-samples/literals-character-literals.pony b/code-samples/literals-character-literals.pony new file mode 100644 index 00000000..f2ebc19b --- /dev/null +++ b/code-samples/literals-character-literals.pony @@ -0,0 +1,3 @@ +let big_a: U8 = 'A' // 65 +let hex_escaped_big_a: U8 = '\x41' // 65 +let newline: U32 = '\n' // 10 \ No newline at end of file diff --git a/code-samples/literals-floats.pony b/code-samples/literals-floats.pony new file mode 100644 index 00000000..5c7eedde --- /dev/null +++ b/code-samples/literals-floats.pony @@ -0,0 +1,2 @@ +let my_double_precision_float: F64 = 0.009999999776482582092285156250 +let my_scientific_float: F32 = 42.12e-4 \ No newline at end of file diff --git a/code-samples/literals-multi-line-string-literals.pony b/code-samples/literals-multi-line-string-literals.pony new file mode 100644 index 00000000..4fc0e41b --- /dev/null +++ b/code-samples/literals-multi-line-string-literals.pony @@ -0,0 +1,5 @@ +let stacked_ponies = " +🐎 +🐎 +🐎 +" \ No newline at end of file diff --git a/code-samples/literals-multibyte-character-literals.pony b/code-samples/literals-multibyte-character-literals.pony new file mode 100644 index 00000000..b461ce71 --- /dev/null +++ b/code-samples/literals-multibyte-character-literals.pony @@ -0,0 +1 @@ +let multiByte: U64 = 'ABCD' // 0x41424344 \ No newline at end of file diff --git a/code-samples/literals-number-types.pony b/code-samples/literals-number-types.pony new file mode 100644 index 00000000..bd9fa719 --- /dev/null +++ b/code-samples/literals-number-types.pony @@ -0,0 +1,3 @@ +let my_decimal_int: I32 = 1024 +let my_hexadecimal_int: I32 = 0x400 +let my_binary_int: I32 = 0b10000000000 \ No newline at end of file diff --git a/code-samples/literals-numeric-typing.pony b/code-samples/literals-numeric-typing.pony new file mode 100644 index 00000000..5d284f2c --- /dev/null +++ b/code-samples/literals-numeric-typing.pony @@ -0,0 +1,3 @@ +let my_explicit_unsigned: U32 = 42_000 +let my_constructor_unsigned = U8(1) +let my_constructor_float = F64(1.234) \ No newline at end of file diff --git a/code-samples/literals-string-literals-encoding.pony b/code-samples/literals-string-literals-encoding.pony new file mode 100644 index 00000000..b16d5df6 --- /dev/null +++ b/code-samples/literals-string-literals-encoding.pony @@ -0,0 +1 @@ +let u_umlaut = "ü" \ No newline at end of file diff --git a/code-samples/literals-string-literals-instances.pony b/code-samples/literals-string-literals-instances.pony new file mode 100644 index 00000000..ff1cbb42 --- /dev/null +++ b/code-samples/literals-string-literals-instances.pony @@ -0,0 +1,5 @@ +let pony = "🐎" +let another_pony = "🐎" +if pony is another_pony then + // True, therefore this line will run. +end \ No newline at end of file diff --git a/code-samples/literals-string-literals.pony b/code-samples/literals-string-literals.pony new file mode 100644 index 00000000..520e09dd --- /dev/null +++ b/code-samples/literals-string-literals.pony @@ -0,0 +1,13 @@ +use "format" + +actor Main + new create(env: Env) => + + let pony = "🐎" + let pony_hex_escaped = "p\xF6n\xFF" + let pony_unicode_escape = "\U01F40E" + + env.out.print(pony + " " + pony_hex_escaped + " " + pony_unicode_escape) + for b in pony.values() do + env.out.print(Format.int[U8](b, FormatHex)) + end \ No newline at end of file diff --git a/code-samples/literals-triple-quoted-string-literals.pony b/code-samples/literals-triple-quoted-string-literals.pony new file mode 100644 index 00000000..84e90e7b --- /dev/null +++ b/code-samples/literals-triple-quoted-string-literals.pony @@ -0,0 +1,15 @@ +let triple_quoted_string_docs = + """ + Triple quoted strings are the way to go for long multi-line text. + They are extensively used as docstrings which are turned into api documentation. + + They get some special treatment, in order to keep Pony code readable: + + * The string literal starts on the line after the opening triple quote. + * Common indentation is removed from the string literal + so it can be conveniently aligned with the enclosing indentation + e.g. each line of this literal will get its first two whitespaces removed + * Whitespace after the opening and before the closing triple quote will be + removed as well. The first line will be completely removed if it only + contains whitespace. e.g. this strings first character is `T` not `\n`. + """ \ No newline at end of file diff --git a/code-samples/literals-type-inference-coercion.pony b/code-samples/literals-type-inference-coercion.pony new file mode 100644 index 00000000..2a0f9509 --- /dev/null +++ b/code-samples/literals-type-inference-coercion.pony @@ -0,0 +1,5 @@ +let my_stringable_array: Array[Stringable] ref = + [ + U64(0xA) + "0xA" + ] \ No newline at end of file diff --git a/code-samples/literals-type-inference-reference-capabilities.pony b/code-samples/literals-type-inference-reference-capabilities.pony new file mode 100644 index 00000000..a0330761 --- /dev/null +++ b/code-samples/literals-type-inference-reference-capabilities.pony @@ -0,0 +1,5 @@ +let my_immutable_array: Array[Stringable] val = + [ + U64(0xBEEF) + "0xBEEF" + ] \ No newline at end of file diff --git a/code-samples/literals-type-inference-union.pony b/code-samples/literals-type-inference-union.pony new file mode 100644 index 00000000..f03886d8 --- /dev/null +++ b/code-samples/literals-type-inference-union.pony @@ -0,0 +1,6 @@ +let my_heterogenous_array = + [ + U64(42) + "42" + U64.min_value() + ] \ No newline at end of file diff --git a/code-samples/match-capabilities-only.pony b/code-samples/match-capabilities-only.pony new file mode 100644 index 00000000..2e3eb265 --- /dev/null +++ b/code-samples/match-capabilities-only.pony @@ -0,0 +1,15 @@ +class A + fun ref sendable() => + None + +actor Main + var _x: (A iso | A ref | None) + + new create(env: Env) => + _x = None + + be f() => + match (_x = None) + | let a1: A iso => None + | let a2: A ref => None + end \ No newline at end of file diff --git a/code-samples/match-capabilities.pony b/code-samples/match-capabilities.pony new file mode 100644 index 00000000..86767701 --- /dev/null +++ b/code-samples/match-capabilities.pony @@ -0,0 +1,19 @@ +class A + fun ref sendable() => + None + +class B + fun ref update() => + None + +actor Main + var _x: (A iso | B ref | None) + + new create(env: Env) => + _x = None + + be f(a': A iso) => + match (_x = None) // type of this expression: (A iso^ | B ref | None) + | let a: A iso => f(consume a) + | let b: B ref => b.update() + end \ No newline at end of file diff --git a/code-samples/match-captures.pony b/code-samples/match-captures.pony new file mode 100644 index 00000000..2d96a6ba --- /dev/null +++ b/code-samples/match-captures.pony @@ -0,0 +1,8 @@ +fun f(x: (U32 | String | None)): String => + match x + | None => "none" + | 2 => "two" + | 3 => "three" + | let u: U32 => "other integer" + | let s: String => s + end \ No newline at end of file diff --git a/code-samples/match-custom-eq-operand.pony b/code-samples/match-custom-eq-operand.pony new file mode 100644 index 00000000..7b3c7167 --- /dev/null +++ b/code-samples/match-custom-eq-operand.pony @@ -0,0 +1,22 @@ +class Foo + var _x: U32 + + new create(x: U32) => + _x = x + + fun eq(that: Foo): Bool => + _x == that._x + +actor Main + new create(env: Env) => + None + + fun f(x: Foo): String => + match x + | Foo(1) => "one" + | Foo(2) => "two" + | Foo(3) => "three" + | Foo(5) => "not four" + else + "something else" + end \ No newline at end of file diff --git a/code-samples/match-expression.pony b/code-samples/match-expression.pony new file mode 100644 index 00000000..b3718144 --- /dev/null +++ b/code-samples/match-expression.pony @@ -0,0 +1,7 @@ +match x +| 2 => "int" +| 2.0 => "float" +| "2" => "string" +else + "something else" +end \ No newline at end of file diff --git a/code-samples/match-guards.pony b/code-samples/match-guards.pony new file mode 100644 index 00000000..50799738 --- /dev/null +++ b/code-samples/match-guards.pony @@ -0,0 +1,10 @@ +fun f(x: (String | None), y: U32): String => + match (x, y) + | (None, _) => "none" + | (let s: String, 2) => s + " two" + | (let s: String, 3) => s + " three" + | (let s: String, let u: U32) if u > 14 => s + " other big integer" + | (let s: String, _) => s + " other small integer" + else + "something else" + end \ No newline at end of file diff --git a/code-samples/match-tuples-ignore-elements.pony b/code-samples/match-tuples-ignore-elements.pony new file mode 100644 index 00000000..34f7057d --- /dev/null +++ b/code-samples/match-tuples-ignore-elements.pony @@ -0,0 +1,9 @@ +fun f(x: (String | None), y: U32): String => + match (x, y) + | (None, _) => "none" + | (let s: String, 2) => s + " two" + | (let s: String, 3) => s + " three" + | (let s: String, _) => s + " other integer" + else + "something else" + end \ No newline at end of file diff --git a/code-samples/match-tuples.pony b/code-samples/match-tuples.pony new file mode 100644 index 00000000..74e0bd0d --- /dev/null +++ b/code-samples/match-tuples.pony @@ -0,0 +1,9 @@ +fun f(x: (String | None), y: U32): String => + match (x, y) + | (None, let u: U32) => "none" + | (let s: String, 2) => s + " two" + | (let s: String, 3) => s + " three" + | (let s: String, let u: U32) => s + " other integer" + else + "something else" + end \ No newline at end of file diff --git a/code-samples/match-type-and-value.pony b/code-samples/match-type-and-value.pony new file mode 100644 index 00000000..1a4f01ad --- /dev/null +++ b/code-samples/match-type-and-value.pony @@ -0,0 +1,9 @@ +fun f(x: (U32 | String | None)): String => + match x + | None => "none" + | 2 => "two" + | 3 => "three" + | "5" => "not four" + else + "something else" + end \ No newline at end of file diff --git a/code-samples/match-value-pattern-matching-vs-type-check.pony b/code-samples/match-value-pattern-matching-vs-type-check.pony new file mode 100644 index 00000000..15fbdf34 --- /dev/null +++ b/code-samples/match-value-pattern-matching-vs-type-check.pony @@ -0,0 +1,14 @@ +class Foo is Equatable[Foo] + +actor Main + + fun f(x: (Foo | None)): String => + match x + | Foo => "foo" + | None => "bar" + else + "" + end + + new create(env: Env) => + f(Foo) \ No newline at end of file diff --git a/code-samples/match-values.pony b/code-samples/match-values.pony new file mode 100644 index 00000000..12050157 --- /dev/null +++ b/code-samples/match-values.pony @@ -0,0 +1,9 @@ +fun f(x: U32): String => + match x + | 1 => "one" + | 2 => "two" + | 3 => "three" + | 5 => "not four" + else + "something else" + end \ No newline at end of file diff --git a/code-samples/methods-anonymous-methods.pony b/code-samples/methods-anonymous-methods.pony new file mode 100644 index 00000000..7961ad7f --- /dev/null +++ b/code-samples/methods-anonymous-methods.pony @@ -0,0 +1,9 @@ +use "collections" + +actor Main + new create(env: Env) => + let list_of_numbers = List[U32].from([1; 2; 3; 4]) + let is_odd = {(n: U32): Bool => (n % 2) == 1} + for odd_number in list_of_numbers.filter(is_odd).values() do + env.out.print(odd_number.string()) + end \ No newline at end of file diff --git a/code-samples/methods-chaining-return-value.pony b/code-samples/methods-chaining-return-value.pony new file mode 100644 index 00000000..895f8703 --- /dev/null +++ b/code-samples/methods-chaining-return-value.pony @@ -0,0 +1,10 @@ +interface Factory + fun add_option(o: Option) + fun make_object(): Object + +primitive Foo + fun object_wrong(f: Factory, o1: Option, o2: Option): Object => + f.>add_option(o1).>add_option(o2).>make_object() // Error! The expression returns a Factory + + fun object_right(f: Factory, o1: Option, o2: Option): Object => + f.>add_option(o1).>add_option(o2).make_object() // Works. The expression returns an Object \ No newline at end of file diff --git a/code-samples/methods-chaining.pony b/code-samples/methods-chaining.pony new file mode 100644 index 00000000..09c8a5d0 --- /dev/null +++ b/code-samples/methods-chaining.pony @@ -0,0 +1,7 @@ +primitive Printer + fun print_two_strings(out: StdStream, s1: String, s2: String) => + out.>print(s1).>print(s2) + // Equivalent to: + out.print(s1) + out.print(s2) + out \ No newline at end of file diff --git a/code-samples/methods-constructors-calling-on-expression.pony b/code-samples/methods-constructors-calling-on-expression.pony new file mode 100644 index 00000000..0f261fb3 --- /dev/null +++ b/code-samples/methods-constructors-calling-on-expression.pony @@ -0,0 +1,13 @@ +class Foo + var _x: U32 + + new create() => + _x = 0 + + new from_int(x: U32) => + _x = x + +class Bar + fun f() => + var a: Foo = Foo.create() + var b: Foo = a.from_int(3) \ No newline at end of file diff --git a/code-samples/methods-constructors-calling-reuse-variable-name.pony b/code-samples/methods-constructors-calling-reuse-variable-name.pony new file mode 100644 index 00000000..022e666e --- /dev/null +++ b/code-samples/methods-constructors-calling-reuse-variable-name.pony @@ -0,0 +1,3 @@ +class Bar + fun f() => + var a: Foo = a.create() \ No newline at end of file diff --git a/code-samples/methods-constructors-calling.pony b/code-samples/methods-constructors-calling.pony new file mode 100644 index 00000000..99ee8cf4 --- /dev/null +++ b/code-samples/methods-constructors-calling.pony @@ -0,0 +1,13 @@ +class Foo + var _x: U32 + + new create() => + _x = 0 + + new from_int(x: U32) => + _x = x + +class Bar + fun f() => + var a: Foo = Foo.create() + var b: Foo = Foo.from_int(3) \ No newline at end of file diff --git a/code-samples/methods-constructors.pony b/code-samples/methods-constructors.pony new file mode 100644 index 00000000..0bedcff0 --- /dev/null +++ b/code-samples/methods-constructors.pony @@ -0,0 +1,8 @@ +class Foo + var _x: U32 + + new create() => + _x = 0 + + new from_int(x: U32) => + _x = x \ No newline at end of file diff --git a/code-samples/methods-default-arguments.pony b/code-samples/methods-default-arguments.pony new file mode 100644 index 00000000..b4de77c7 --- /dev/null +++ b/code-samples/methods-default-arguments.pony @@ -0,0 +1,13 @@ +class Coord + var _x: U32 + var _y: U32 + + new create(x: U32 = 0, y: U32 = 0) => + _x = x + _y = y + +class Bar + fun f() => + var a: Coord = Coord.create() // Contains (0, 0) + var b: Coord = Coord.create(3) // Contains (3, 0) + var c: Coord = Coord.create(3, 4) // Contains (3, 4) \ No newline at end of file diff --git a/code-samples/methods-functions-calling-implicit-this.pony b/code-samples/methods-functions-calling-implicit-this.pony new file mode 100644 index 00000000..5cbffed5 --- /dev/null +++ b/code-samples/methods-functions-calling-implicit-this.pony @@ -0,0 +1,20 @@ +class Foo + var _x: U32 + + new create() => + _x = 0 + + new from_int(x: U32) => + _x = x + + fun get(): U32 => + _x + +class Bar + fun f() => + var a: Foo = Foo.from_int(3) + var b: U32 = a.get() + var c: U32 = g(b) + + fun g(x: U32): U32 => + x + 1 \ No newline at end of file diff --git a/code-samples/methods-functions-calling.pony b/code-samples/methods-functions-calling.pony new file mode 100644 index 00000000..8b63f28d --- /dev/null +++ b/code-samples/methods-functions-calling.pony @@ -0,0 +1,6 @@ +class Foo + fun hello(name: String): String => + "hello " + name + + fun f() => + let a = hello("Fred") \ No newline at end of file diff --git a/code-samples/methods-functions.pony b/code-samples/methods-functions.pony new file mode 100644 index 00000000..8909482e --- /dev/null +++ b/code-samples/methods-functions.pony @@ -0,0 +1,6 @@ +class C + fun add(x: U32, y: U32): U32 => + x + y + + fun nop() => + add(1, 2) // Pointless, we ignore the result \ No newline at end of file diff --git a/code-samples/methods-named-and-positional-arguments-combined.pony b/code-samples/methods-named-and-positional-arguments-combined.pony new file mode 100644 index 00000000..b7a74562 --- /dev/null +++ b/code-samples/methods-named-and-positional-arguments-combined.pony @@ -0,0 +1,8 @@ +class Foo + fun f(a: U32 = 1, b: U32 = 2, c: U32 = 3, d: U32 = 4, e: U32 = 5): U32 => + 0 + + fun g() => + f(6, 7 where d = 8) + // Equivalent to: + f(6, 7, 3, 8, 5) \ No newline at end of file diff --git a/code-samples/methods-named-arguments.pony b/code-samples/methods-named-arguments.pony new file mode 100644 index 00000000..f0a80b26 --- /dev/null +++ b/code-samples/methods-named-arguments.pony @@ -0,0 +1,12 @@ +class Coord + var _x: U32 + var _y: U32 + + new create(x: U32 = 0, y: U32 = 0) => + _x = x + _y = y + +class Bar + fun f() => + var a: Coord = Coord.create(3, 4) // Contains (3, 4) + var b: Coord = Coord.create(where y = 4, x = 3) // Contains (3, 4) \ No newline at end of file diff --git a/code-samples/object-literals-actor-literal.pony b/code-samples/object-literals-actor-literal.pony new file mode 100644 index 00000000..d914ddf5 --- /dev/null +++ b/code-samples/object-literals-actor-literal.pony @@ -0,0 +1,3 @@ +object + be apply() => env.out.print("hi") +end \ No newline at end of file diff --git a/code-samples/object-literals-closing-over-values.pony b/code-samples/object-literals-closing-over-values.pony new file mode 100644 index 00000000..908c47c8 --- /dev/null +++ b/code-samples/object-literals-closing-over-values.pony @@ -0,0 +1,8 @@ +use "collections" + +class Foo + fun foo(str: String): Hashable iso^ => + object iso is Hashable + fun apply(): String => str + fun hash(): USize => str.hash() + end \ No newline at end of file diff --git a/code-samples/object-literals-fields-assignment.pony b/code-samples/object-literals-fields-assignment.pony new file mode 100644 index 00000000..d83fd3db --- /dev/null +++ b/code-samples/object-literals-fields-assignment.pony @@ -0,0 +1,9 @@ +use "collections" + +class Foo + fun foo(str: String): Hashable => + object is Hashable + let s: String = str + fun apply(): String => s + fun hash(): USize => s.hash() + end \ No newline at end of file diff --git a/code-samples/object-literals-lambda-as-explicit-object-literal.pony b/code-samples/object-literals-lambda-as-explicit-object-literal.pony new file mode 100644 index 00000000..069efd52 --- /dev/null +++ b/code-samples/object-literals-lambda-as-explicit-object-literal.pony @@ -0,0 +1,3 @@ +object + fun apply(s: String): String => "lambda: " + s +end \ No newline at end of file diff --git a/code-samples/object-literals-lambda-capture-and-rename-values.pony b/code-samples/object-literals-lambda-capture-and-rename-values.pony new file mode 100644 index 00000000..ea8bd637 --- /dev/null +++ b/code-samples/object-literals-lambda-capture-and-rename-values.pony @@ -0,0 +1,2 @@ + new create(env: Env) => + foo({(s: String)(myenv = env) => myenv.out.print(s) }) \ No newline at end of file diff --git a/code-samples/object-literals-lambda-capture-values.pony b/code-samples/object-literals-lambda-capture-values.pony new file mode 100644 index 00000000..86e5734d --- /dev/null +++ b/code-samples/object-literals-lambda-capture-values.pony @@ -0,0 +1,6 @@ +class Foo + new create(env: Env) => + foo({(s: String)(env) => env.out.print(s) }) + + fun foo(f: {(String)}) => + f("Hello World") \ No newline at end of file diff --git a/code-samples/object-literals-lambda-reference-capabilities-2.pony b/code-samples/object-literals-lambda-reference-capabilities-2.pony new file mode 100644 index 00000000..972fc030 --- /dev/null +++ b/code-samples/object-literals-lambda-reference-capabilities-2.pony @@ -0,0 +1,19 @@ +use "collections" + +actor Main + new create(env: Env) => + let l = List[String] + l.>push("hello").push("world") + var count = U32(0) + for_each(l, {ref(s:String) => + env.out.print(s) + count = count + 1 + }) + // Displays '0' as the count + env.out.print("Count: " + count.string()) + + fun for_each(l: List[String], f: {ref(String)} ref) => + try + f(l.shift()?) + for_each(l, f) + end \ No newline at end of file diff --git a/code-samples/object-literals-lambda-reference-capabilities.pony b/code-samples/object-literals-lambda-reference-capabilities.pony new file mode 100644 index 00000000..3b763c0a --- /dev/null +++ b/code-samples/object-literals-lambda-reference-capabilities.pony @@ -0,0 +1,16 @@ +use "collections" + +actor Main + new create(env: Env) => + let l = List[U32] + l.>push(10).>push(20).>push(30).push(40) + let r = reduce(l, 0, {(a:U32, b:U32): U32 => a + b }) + env.out.print("Result: " + r.string()) + + fun reduce(l: List[U32], acc: U32, f: {(U32, U32): U32} val): U32 => + try + let acc' = f(acc, l.shift()?) + reduce(l, acc', f) + else + acc + end \ No newline at end of file diff --git a/code-samples/object-literals-lambda-with-reference-capability-as-explicit-object-literal.pony b/code-samples/object-literals-lambda-with-reference-capability-as-explicit-object-literal.pony new file mode 100644 index 00000000..b318098e --- /dev/null +++ b/code-samples/object-literals-lambda-with-reference-capability-as-explicit-object-literal.pony @@ -0,0 +1,3 @@ +object iso + fun apply(s: String): String => "lambda: " + s +end \ No newline at end of file diff --git a/code-samples/object-literals-lambda-with-reference-capability.pony b/code-samples/object-literals-lambda-with-reference-capability.pony new file mode 100644 index 00000000..96f7d281 --- /dev/null +++ b/code-samples/object-literals-lambda-with-reference-capability.pony @@ -0,0 +1 @@ +{(s: String): String => "lambda: " + s } iso \ No newline at end of file diff --git a/code-samples/object-literals-lambda.pony b/code-samples/object-literals-lambda.pony new file mode 100644 index 00000000..7d74078f --- /dev/null +++ b/code-samples/object-literals-lambda.pony @@ -0,0 +1 @@ +{(s: String): String => "lambda: " + s } \ No newline at end of file diff --git a/code-samples/object-literals-object-literal-with-interface.pony b/code-samples/object-literals-object-literal-with-interface.pony new file mode 100644 index 00000000..1a27f264 --- /dev/null +++ b/code-samples/object-literals-object-literal-with-interface.pony @@ -0,0 +1,4 @@ +object is Hashable + fun apply(): String => "hi" + fun hash(): USize => this().hash() +end \ No newline at end of file diff --git a/code-samples/object-literals-object-literal.pony b/code-samples/object-literals-object-literal.pony new file mode 100644 index 00000000..602c3eab --- /dev/null +++ b/code-samples/object-literals-object-literal.pony @@ -0,0 +1,3 @@ +object + fun apply(): String => "hi" +end \ No newline at end of file diff --git a/code-samples/object-literals-reference-capability.pony b/code-samples/object-literals-reference-capability.pony new file mode 100644 index 00000000..99522c1e --- /dev/null +++ b/code-samples/object-literals-reference-capability.pony @@ -0,0 +1,9 @@ +use "collections" + +class Foo + fun foo(str: String): Hashable iso^ => + object iso is Hashable + let s: String = str + fun apply(): String => s + fun hash(): USize => s.hash() + end \ No newline at end of file diff --git a/code-samples/operators-add.pony b/code-samples/operators-add.pony new file mode 100644 index 00000000..b577cac8 --- /dev/null +++ b/code-samples/operators-add.pony @@ -0,0 +1,19 @@ +// Define a suitable type +class Pair + var _x: U32 = 0 + var _y: U32 = 0 + + new create(x: U32, y: U32) => + _x = x + _y = y + + // Define a + function + fun add(other: Pair): Pair => + Pair(_x + other._x, _y + other._y) + +// Now let's use it +class Foo + fun foo() => + var x = Pair(1, 2) + var y = Pair(3, 4) + var z = x + y \ No newline at end of file diff --git a/code-samples/operators-infix-operator.pony b/code-samples/operators-infix-operator.pony new file mode 100644 index 00000000..547eca1b --- /dev/null +++ b/code-samples/operators-infix-operator.pony @@ -0,0 +1,2 @@ +1 + 2 +a < b \ No newline at end of file diff --git a/code-samples/operators-operator-aliasing.pony b/code-samples/operators-operator-aliasing.pony new file mode 100644 index 00000000..7736d64f --- /dev/null +++ b/code-samples/operators-operator-aliasing.pony @@ -0,0 +1,2 @@ +x + y +x.add(y) \ No newline at end of file diff --git a/code-samples/operators-precedence-infix-and-unary-operators-with-parentheses.pony b/code-samples/operators-precedence-infix-and-unary-operators-with-parentheses.pony new file mode 100644 index 00000000..58f2305b --- /dev/null +++ b/code-samples/operators-precedence-infix-and-unary-operators-with-parentheses.pony @@ -0,0 +1 @@ +1 + (2 * -3) // -5 \ No newline at end of file diff --git a/code-samples/operators-precedence-infix-and-unary-operators-without-parentheses.pony b/code-samples/operators-precedence-infix-and-unary-operators-without-parentheses.pony new file mode 100644 index 00000000..f338bc68 --- /dev/null +++ b/code-samples/operators-precedence-infix-and-unary-operators-without-parentheses.pony @@ -0,0 +1 @@ +1 + 2 * -3 // Compilation failed. \ No newline at end of file diff --git a/code-samples/operators-precedence-single-operator.pony b/code-samples/operators-precedence-single-operator.pony new file mode 100644 index 00000000..584825fc --- /dev/null +++ b/code-samples/operators-precedence-single-operator.pony @@ -0,0 +1 @@ +1 + 2 + 3 // 6 \ No newline at end of file diff --git a/code-samples/operators-precedence-unary-operator-with-parentheses.pony b/code-samples/operators-precedence-unary-operator-with-parentheses.pony new file mode 100644 index 00000000..07d94232 --- /dev/null +++ b/code-samples/operators-precedence-unary-operator-with-parentheses.pony @@ -0,0 +1 @@ +1 + -(2 * -3) // 7 \ No newline at end of file diff --git a/code-samples/operators-precedence-with-parentheses.pony b/code-samples/operators-precedence-with-parentheses.pony new file mode 100644 index 00000000..38108a3d --- /dev/null +++ b/code-samples/operators-precedence-with-parentheses.pony @@ -0,0 +1 @@ +1 + (2 * 3) // 7 \ No newline at end of file diff --git a/code-samples/operators-precedence-without-parentheses.pony b/code-samples/operators-precedence-without-parentheses.pony new file mode 100644 index 00000000..6d6b2bf8 --- /dev/null +++ b/code-samples/operators-precedence-without-parentheses.pony @@ -0,0 +1 @@ +1 + 2 * 3 // Compilation failed. \ No newline at end of file diff --git a/code-samples/operators-unary-operators.pony b/code-samples/operators-unary-operators.pony new file mode 100644 index 00000000..16d67594 --- /dev/null +++ b/code-samples/operators-unary-operators.pony @@ -0,0 +1,2 @@ +-x +x.neg() \ No newline at end of file diff --git a/code-samples/partial-application-callback-function-with-all-arguments-bound.pony b/code-samples/partial-application-callback-function-with-all-arguments-bound.pony new file mode 100644 index 00000000..c463c3e9 --- /dev/null +++ b/code-samples/partial-application-callback-function-with-all-arguments-bound.pony @@ -0,0 +1,2 @@ +let f = foo~addmul(3, 4) +f() \ No newline at end of file diff --git a/code-samples/partial-application-callback-function-with-no-arguments-bound.pony b/code-samples/partial-application-callback-function-with-no-arguments-bound.pony new file mode 100644 index 00000000..910ca620 --- /dev/null +++ b/code-samples/partial-application-callback-function-with-no-arguments-bound.pony @@ -0,0 +1,2 @@ +let f = foo~addmul() +f(3, 4) \ No newline at end of file diff --git a/code-samples/partial-application-callback-function-with-out-of-order-arguments.pony b/code-samples/partial-application-callback-function-with-out-of-order-arguments.pony new file mode 100644 index 00000000..50c6483d --- /dev/null +++ b/code-samples/partial-application-callback-function-with-out-of-order-arguments.pony @@ -0,0 +1,2 @@ +let f = foo~addmul(where mul = 4) +f(3) \ No newline at end of file diff --git a/code-samples/partial-application-callback-function-with-some-arguments-bound.pony b/code-samples/partial-application-callback-function-with-some-arguments-bound.pony new file mode 100644 index 00000000..a8ac5581 --- /dev/null +++ b/code-samples/partial-application-callback-function-with-some-arguments-bound.pony @@ -0,0 +1,11 @@ +class Foo + var _f: F64 = 0 + + fun ref addmul(add: F64, mul: F64): F64 => + _f = (_f + add) * mul + +class Bar + fun apply() => + let foo: Foo = Foo + let f = foo~addmul(3) + f(4) \ No newline at end of file diff --git a/code-samples/partial-application-partially-applying-a-partial-application.pony b/code-samples/partial-application-partially-applying-a-partial-application.pony new file mode 100644 index 00000000..eb0875ec --- /dev/null +++ b/code-samples/partial-application-partially-applying-a-partial-application.pony @@ -0,0 +1,3 @@ +let f = foo~addmul() +let f2 = f~apply(where mul = 4) +f2(3) \ No newline at end of file diff --git a/code-samples/ponycheck-ponytest-for-all.pony b/code-samples/ponycheck-ponytest-for-all.pony new file mode 100644 index 00000000..320106c6 --- /dev/null +++ b/code-samples/ponycheck-ponytest-for-all.pony @@ -0,0 +1,14 @@ +class _ListReverseProperties is UnitTest + fun name(): String => "list/properties" + + fun apply(h: TestHelper) ? => + let gen1 = Generators.seq_of[USize, Array[USize]](Generators.usize()) + PonyCheck.for_all[Array[USize]](gen1, h)({ + (arg1: Array[USize], ph: PropertyHelper) => + ph.assert_array_eq[USize](arg1, arg1.reverse().reverse()) + }) + let gen2 = Generators.seq_of[USize, Array[USize]](1, Generators.usize()) + PonyCheck.for_all[Array[USize]](gen2, h)({ + (arg1: Array[USize], ph: PropertyHelper) => + ph.assert_array_eq[USize](arg1, arg1.reverse()) + }) \ No newline at end of file diff --git a/code-samples/ponycheck-ponytest.pony b/code-samples/ponycheck-ponytest.pony new file mode 100644 index 00000000..961a19ae --- /dev/null +++ b/code-samples/ponycheck-ponytest.pony @@ -0,0 +1,9 @@ +use "pony_test" +use "pony_check" + +actor Main is TestList + new create(env: Env) => + PonyTest(env, this) + + fun tag tests(test: PonyTest) => + test(Property1UnitTest[String](_MyFirstProperty)) \ No newline at end of file diff --git a/code-samples/ponycheck-usage-quickcheck.pony b/code-samples/ponycheck-usage-quickcheck.pony new file mode 100644 index 00000000..d1b0b5f8 --- /dev/null +++ b/code-samples/ponycheck-usage-quickcheck.pony @@ -0,0 +1,20 @@ +use "pony_check" +use "collections" + +class _ListReverseProperty is Property1[Array[USize]] + fun name(): String => "list/reverse" + + fun gen(): Generator[Array[USize]] => + Generators.seq_of[USize, Array[USize]](Generators.usize()) + + fun property(arg1: Array[USize], ph: PropertyHelper) => + ph.assert_array_eq[USize](arg1, arg1.reverse().reverse()) + +class _ListReverseOneProperty is Property1[Array[USize]] + fun name(): String => "list/reverse/one" + + fun gen(): Generator[Array[USize]] => + Generators.seq_of[USize, Array[USize]](Generators.usize() where min = 1, max = 1) + + fun property(arg1: Array[USize], ph: PropertyHelper) => + ph.assert_array_eq[USize](arg1, arg1.reverse()) \ No newline at end of file diff --git a/code-samples/ponycheck-usage.pony b/code-samples/ponycheck-usage.pony new file mode 100644 index 00000000..abb56168 --- /dev/null +++ b/code-samples/ponycheck-usage.pony @@ -0,0 +1,11 @@ +use "pony_test" + +class _MyFirstProperty is Property1[String] + fun name(): String => + "my_first_property" + + fun gen(): Generator[String] => + Generators.ascii() + + fun property(arg1: String, ph: PropertyHelper) => + ph.assert_eq[String](arg1, arg1) \ No newline at end of file diff --git a/code-samples/ponytest-aggregation.pony b/code-samples/ponytest-aggregation.pony new file mode 100644 index 00000000..56383cc9 --- /dev/null +++ b/code-samples/ponytest-aggregation.pony @@ -0,0 +1,14 @@ +use "pony_test" +use foo = "foo" +use bar = "bar" + +actor Main is TestList + new create(env: Env) => + PonyTest(env, this) + + new make() => + None + + fun tag tests(test: PonyTest) => + foo.Main.make().tests(test) + bar.Main.make().tests(test) \ No newline at end of file diff --git a/code-samples/ponytest-example.pony b/code-samples/ponytest-example.pony new file mode 100644 index 00000000..64298e57 --- /dev/null +++ b/code-samples/ponytest-example.pony @@ -0,0 +1,24 @@ +use "pony_test" + +actor Main is TestList + new create(env: Env) => + PonyTest(env, this) + + new make() => + None + + fun tag tests(test: PonyTest) => + test(_TestAdd) + test(_TestSub) + +class iso _TestAdd is UnitTest + 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 apply(h: TestHelper) => + h.assert_eq[U32](2, 4 - 2) \ No newline at end of file diff --git a/code-samples/primitives-doors.pony b/code-samples/primitives-doors.pony new file mode 100644 index 00000000..8bf7f222 --- /dev/null +++ b/code-samples/primitives-doors.pony @@ -0,0 +1,24 @@ +// 2 "marker values" +primitive OpenedDoor +primitive ClosedDoor + +// An "enumeration" type +type DoorState is (OpenedDoor | ClosedDoor) + +// A collection of functions +primitive BasicMath + fun add(a: U64, b: U64): U64 => + a + b + + fun multiply(a: U64, b: U64): U64 => + a * b + +actor Main + new create(env: Env) => + let doorState : DoorState = ClosedDoor + let isDoorOpen : Bool = match doorState + | OpenedDoor => true + | ClosedDoor => false + end + env.out.print("Is door open? " + isDoorOpen.string()) + env.out.print("2 + 3 = " + BasicMath.add(2,3).string()) \ No newline at end of file diff --git a/code-samples/recovering-capabilities-format-int.pony b/code-samples/recovering-capabilities-format-int.pony new file mode 100644 index 00000000..8e978a1d --- /dev/null +++ b/code-samples/recovering-capabilities-format-int.pony @@ -0,0 +1,21 @@ +recover + var s = String((prec + 1).max(width.max(31))) + var value = x + + try + if value == 0 then + s.push(table(0)?) + else + while value != 0 do + let index = ((value = value / base) - (value * base)) + s.push(table(index.usize())?) + end + end + end + + _extend_digits(s, prec') + s.append(typestring) + s.append(prestring) + _pad(s, width, align, fill) + s +end \ No newline at end of file diff --git a/code-samples/recovering-capabilities-ref-to-iso.pony b/code-samples/recovering-capabilities-ref-to-iso.pony new file mode 100644 index 00000000..b1ec9c0e --- /dev/null +++ b/code-samples/recovering-capabilities-ref-to-iso.pony @@ -0,0 +1 @@ +recover Array[String].create() end \ No newline at end of file diff --git a/code-samples/recovering-capabilities-string-append.pony b/code-samples/recovering-capabilities-string-append.pony new file mode 100644 index 00000000..7fd53cbf --- /dev/null +++ b/code-samples/recovering-capabilities-string-append.pony @@ -0,0 +1,2 @@ +let s = recover String end +s.append("hi") \ No newline at end of file diff --git a/code-samples/recovering-capabilities-with-explicit-reference-capability.pony b/code-samples/recovering-capabilities-with-explicit-reference-capability.pony new file mode 100644 index 00000000..7a72b9b9 --- /dev/null +++ b/code-samples/recovering-capabilities-with-explicit-reference-capability.pony @@ -0,0 +1 @@ +let key = recover val line.substring(0, i).>strip() end \ No newline at end of file diff --git a/code-samples/recursion.pony b/code-samples/recursion.pony new file mode 100644 index 00000000..d2e92ce9 --- /dev/null +++ b/code-samples/recursion.pony @@ -0,0 +1,13 @@ +fun recursive_factorial(x: U32): U32 => + if x == 0 then + 1 + else + x * recursive_factorial(x - 1) + end + +fun tail_recursive_factorial(x: U32, y: U32): U32 => + if x == 0 then + y + else + tail_recursive_factorial(x - 1, x * y) + end \ No newline at end of file diff --git a/code-samples/reference-capabilities-constructors-for-different-capabilities.pony b/code-samples/reference-capabilities-constructors-for-different-capabilities.pony new file mode 100644 index 00000000..4ac96877 --- /dev/null +++ b/code-samples/reference-capabilities-constructors-for-different-capabilities.pony @@ -0,0 +1,8 @@ +class Foo + let x: U32 + + new val create_val(x': U32) => + x = x' + + new ref create_ref(x': U32) => + x = x' \ No newline at end of file diff --git a/code-samples/reference-capabilities-default-vs-explicit.pony b/code-samples/reference-capabilities-default-vs-explicit.pony new file mode 100644 index 00000000..6b67322b --- /dev/null +++ b/code-samples/reference-capabilities-default-vs-explicit.pony @@ -0,0 +1,2 @@ +let a: String val = "Hello, world!" +let b: String = "I'm a wombat!" // Also a String val \ No newline at end of file diff --git a/code-samples/reference-capabilities-string-capabilities.pony b/code-samples/reference-capabilities-string-capabilities.pony new file mode 100644 index 00000000..fa236f3b --- /dev/null +++ b/code-samples/reference-capabilities-string-capabilities.pony @@ -0,0 +1,6 @@ +String iso // An isolated string +String trn // A transition string +String ref // A string reference +String val // A string value +String box // A string box +String tag // A string tag \ No newline at end of file diff --git a/code-samples/reference-capabilities-string-default.pony b/code-samples/reference-capabilities-string-default.pony new file mode 100644 index 00000000..ae8259a0 --- /dev/null +++ b/code-samples/reference-capabilities-string-default.pony @@ -0,0 +1 @@ +class val String \ No newline at end of file diff --git a/code-samples/reference-capability-specificy-a-capability-other-than-the-default.pony b/code-samples/reference-capability-specificy-a-capability-other-than-the-default.pony new file mode 100644 index 00000000..2a45c23a --- /dev/null +++ b/code-samples/reference-capability-specificy-a-capability-other-than-the-default.pony @@ -0,0 +1,5 @@ +class Foo + let x: U32 + + new val create(x': U32) => + x = x' \ No newline at end of file diff --git a/code-samples/scheduling.pony b/code-samples/scheduling.pony new file mode 100644 index 00000000..d8e8a3e9 --- /dev/null +++ b/code-samples/scheduling.pony @@ -0,0 +1,4 @@ +be bad_citizen() => + while true do + _env.out.print("Never gonna give you up. Really gonna make you cry") + end \ No newline at end of file diff --git a/code-samples/structs-constructors.pony b/code-samples/structs-constructors.pony new file mode 100644 index 00000000..8e6be66d --- /dev/null +++ b/code-samples/structs-constructors.pony @@ -0,0 +1,17 @@ +struct Pointer[A] + """ + A Pointer[A] is a raw memory pointer. It has no descriptor and thus can't be + included in a union or intersection, or be a subtype of any interface. Most + functions on a Pointer[A] are private to maintain memory safety. + """ + new create() => + """ + A null pointer. + """ + compile_intrinsic + + new _alloc(len: USize) => + """ + Space for len instances of A. + """ + compile_intrinsic \ No newline at end of file diff --git a/code-samples/structs-fields.pony b/code-samples/structs-fields.pony new file mode 100644 index 00000000..2b868a73 --- /dev/null +++ b/code-samples/structs-fields.pony @@ -0,0 +1,6 @@ +struct Inner + var x: I32 = 0 + +struct Outer + embed inner_embed: Inner = Inner + var inner_var: Inner = Inner \ No newline at end of file diff --git a/code-samples/sugar-apply-explicit.pony b/code-samples/sugar-apply-explicit.pony new file mode 100644 index 00000000..8e5e4a2e --- /dev/null +++ b/code-samples/sugar-apply-explicit.pony @@ -0,0 +1,2 @@ +var foo = Foo.create() +foo.apply() \ No newline at end of file diff --git a/code-samples/sugar-apply-implicit.pony b/code-samples/sugar-apply-implicit.pony new file mode 100644 index 00000000..98e59094 --- /dev/null +++ b/code-samples/sugar-apply-implicit.pony @@ -0,0 +1,2 @@ +var foo = Foo.create() +foo() \ No newline at end of file diff --git a/code-samples/sugar-apply-with-arguments-explicit.pony b/code-samples/sugar-apply-with-arguments-explicit.pony new file mode 100644 index 00000000..a4c0682b --- /dev/null +++ b/code-samples/sugar-apply-with-arguments-explicit.pony @@ -0,0 +1,2 @@ +var foo = Foo.create() +foo.apply(x, 37 where crash = false) \ No newline at end of file diff --git a/code-samples/sugar-apply-with-arguments-implicit.pony b/code-samples/sugar-apply-with-arguments-implicit.pony new file mode 100644 index 00000000..5c8d8d27 --- /dev/null +++ b/code-samples/sugar-apply-with-arguments-implicit.pony @@ -0,0 +1,2 @@ +var foo = Foo.create() +foo(x, 37 where crash = false) \ No newline at end of file diff --git a/code-samples/sugar-create-apply-combined-explicit.pony b/code-samples/sugar-create-apply-combined-explicit.pony new file mode 100644 index 00000000..82ab7633 --- /dev/null +++ b/code-samples/sugar-create-apply-combined-explicit.pony @@ -0,0 +1,2 @@ +var foo = Foo.create().apply() +var bar = Bar.create().apply(x, 37 where crash = false) \ No newline at end of file diff --git a/code-samples/sugar-create-apply-combined-implicit.pony b/code-samples/sugar-create-apply-combined-implicit.pony new file mode 100644 index 00000000..2770e48f --- /dev/null +++ b/code-samples/sugar-create-apply-combined-implicit.pony @@ -0,0 +1,2 @@ +var foo = Foo() +var bar = Bar(x, 37 where crash = false) \ No newline at end of file diff --git a/code-samples/sugar-create-explicit.pony b/code-samples/sugar-create-explicit.pony new file mode 100644 index 00000000..d4e27f7f --- /dev/null +++ b/code-samples/sugar-create-explicit.pony @@ -0,0 +1 @@ +var foo = Foo.create() \ No newline at end of file diff --git a/code-samples/sugar-create-implicit.pony b/code-samples/sugar-create-implicit.pony new file mode 100644 index 00000000..75e13c75 --- /dev/null +++ b/code-samples/sugar-create-implicit.pony @@ -0,0 +1 @@ +var foo = Foo \ No newline at end of file diff --git a/code-samples/sugar-create-with-arguments-explicit.pony b/code-samples/sugar-create-with-arguments-explicit.pony new file mode 100644 index 00000000..516a10aa --- /dev/null +++ b/code-samples/sugar-create-with-arguments-explicit.pony @@ -0,0 +1 @@ +var foo = Foo.create(x, 37 where crash = false) \ No newline at end of file diff --git a/code-samples/sugar-create-with-arguments-implicit.pony b/code-samples/sugar-create-with-arguments-implicit.pony new file mode 100644 index 00000000..d1ecbc3f --- /dev/null +++ b/code-samples/sugar-create-with-arguments-implicit.pony @@ -0,0 +1 @@ +var foo = Foo(x, 37 where crash = false) \ No newline at end of file diff --git a/code-samples/sugar-update-additional-parameters.pony b/code-samples/sugar-update-additional-parameters.pony new file mode 100644 index 00000000..d638542f --- /dev/null +++ b/code-samples/sugar-update-additional-parameters.pony @@ -0,0 +1,3 @@ +foo1(2, 3) = x +foo2() = x +foo3(37, "Hello", 3.5 where a = 2, b = 3) = x \ No newline at end of file diff --git a/code-samples/sugar-update-explicit.pony b/code-samples/sugar-update-explicit.pony new file mode 100644 index 00000000..4c25764c --- /dev/null +++ b/code-samples/sugar-update-explicit.pony @@ -0,0 +1 @@ +foo.update(37 where value = x) \ No newline at end of file diff --git a/code-samples/sugar-update-implicit.pony b/code-samples/sugar-update-implicit.pony new file mode 100644 index 00000000..5e1c6311 --- /dev/null +++ b/code-samples/sugar-update-implicit.pony @@ -0,0 +1 @@ +foo(37) = x \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-marker-methods.pony b/code-samples/traits-and-interfaces-marker-methods.pony new file mode 100644 index 00000000..2bdd71b9 --- /dev/null +++ b/code-samples/traits-and-interfaces-marker-methods.pony @@ -0,0 +1,5 @@ +interface Color + fun is_color(): None + +primitive Red + fun is_color(): None => None \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-multiple-traits.pony b/code-samples/traits-and-interfaces-multiple-traits.pony new file mode 100644 index 00000000..88b000ba --- /dev/null +++ b/code-samples/traits-and-interfaces-multiple-traits.pony @@ -0,0 +1,7 @@ +trait Named + fun name(): String => "Bob" + +trait Bald + fun hair(): Bool => false + +class Bob is (Named & Bald) \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-nested-traits.pony b/code-samples/traits-and-interfaces-nested-traits.pony new file mode 100644 index 00000000..332cb4be --- /dev/null +++ b/code-samples/traits-and-interfaces-nested-traits.pony @@ -0,0 +1,7 @@ +trait Named + fun name(): String => "Bob" + +trait Bald is Named + fun hair(): Bool => false + +class Bob is Bald \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-nominal-and-structural-subtyping.pony b/code-samples/traits-and-interfaces-nominal-and-structural-subtyping.pony new file mode 100644 index 00000000..da7a2655 --- /dev/null +++ b/code-samples/traits-and-interfaces-nominal-and-structural-subtyping.pony @@ -0,0 +1,7 @@ +interface HasName + fun name(): String => "Bob" + +class Bob is HasName + +class Larry + fun name(): String => "Larry" \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-nominal-subtyping-in-pony.pony b/code-samples/traits-and-interfaces-nominal-subtyping-in-pony.pony new file mode 100644 index 00000000..e1972c2b --- /dev/null +++ b/code-samples/traits-and-interfaces-nominal-subtyping-in-pony.pony @@ -0,0 +1,2 @@ +class Larry + fun name(): String => "Larry" \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-nominal-subtyping.pony b/code-samples/traits-and-interfaces-nominal-subtyping.pony new file mode 100644 index 00000000..d3917a8b --- /dev/null +++ b/code-samples/traits-and-interfaces-nominal-subtyping.pony @@ -0,0 +1 @@ +class Name is Stringable \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-open-world-enumerations.pony b/code-samples/traits-and-interfaces-open-world-enumerations.pony new file mode 100644 index 00000000..a51074de --- /dev/null +++ b/code-samples/traits-and-interfaces-open-world-enumerations.pony @@ -0,0 +1,4 @@ +trait Color + +primitive Red is Color +primitive Blue is Color \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-open-world-interface.pony b/code-samples/traits-and-interfaces-open-world-interface.pony new file mode 100644 index 00000000..6abe430e --- /dev/null +++ b/code-samples/traits-and-interfaces-open-world-interface.pony @@ -0,0 +1 @@ +interface Color \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-open-world-typing.pony b/code-samples/traits-and-interfaces-open-world-typing.pony new file mode 100644 index 00000000..c25cd0d7 --- /dev/null +++ b/code-samples/traits-and-interfaces-open-world-typing.pony @@ -0,0 +1,17 @@ +interface Compactable + fun ref compact() + fun size(): USize + +class Compactor + """ + Compacts data structures when their size crosses a threshold + """ + let _threshold: USize + + new create(threshold: USize) => + _threshold = threshold + + fun ref try_compacting(thing: Compactable) => + if thing.size() > _threshold then + thing.compact() + end \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-private-methods.pony b/code-samples/traits-and-interfaces-private-methods.pony new file mode 100644 index 00000000..0c7fd1d3 --- /dev/null +++ b/code-samples/traits-and-interfaces-private-methods.pony @@ -0,0 +1,9 @@ +actor Main + new create(env: Env) => + let x: String ref = "sailor".string() + let y: Foo = x + y._set(0, 'f') + env.out.print("Hello, " + x) + +interface Foo + fun ref _set(i: USize, value: U8): U8 \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-structural-subtyping-in-pony.pony b/code-samples/traits-and-interfaces-structural-subtyping-in-pony.pony new file mode 100644 index 00000000..a82935e6 --- /dev/null +++ b/code-samples/traits-and-interfaces-structural-subtyping-in-pony.pony @@ -0,0 +1,2 @@ +interface HasName + fun name(): String \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-structural-subtyping.pony b/code-samples/traits-and-interfaces-structural-subtyping.pony new file mode 100644 index 00000000..22b0cb4e --- /dev/null +++ b/code-samples/traits-and-interfaces-structural-subtyping.pony @@ -0,0 +1,11 @@ +interface box Stringable + """ + Things that can be turned into a String. + """ + fun string(): String iso^ + """ + Generate a string representation of this object. + """ + +primitive ExecveError + fun string(): String iso^ => "ExecveError".clone() \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-trait.pony b/code-samples/traits-and-interfaces-trait.pony new file mode 100644 index 00000000..0237adbe --- /dev/null +++ b/code-samples/traits-and-interfaces-trait.pony @@ -0,0 +1,4 @@ +trait Named + fun name(): String => "Bob" + +class Bob is Named \ No newline at end of file diff --git a/code-samples/traits-and-interfaces-type-union.pony b/code-samples/traits-and-interfaces-type-union.pony new file mode 100644 index 00000000..bc484efe --- /dev/null +++ b/code-samples/traits-and-interfaces-type-union.pony @@ -0,0 +1,4 @@ +primitive Red +primitive Blue + +type Color is (Red | Blue) \ No newline at end of file diff --git a/code-samples/type-aliases-complex-types-interface.pony b/code-samples/type-aliases-complex-types-interface.pony new file mode 100644 index 00000000..d0c24dd7 --- /dev/null +++ b/code-samples/type-aliases-complex-types-interface.pony @@ -0,0 +1,10 @@ +interface HasName + fun name(): String + +interface HasAge + fun age(): U32 + +interface HasFeelings + fun feeling(): String + +type Person is (HasName & HasAge & HasFeelings) \ No newline at end of file diff --git a/code-samples/type-aliases-complex-types-trait.pony b/code-samples/type-aliases-complex-types-trait.pony new file mode 100644 index 00000000..d0a2e5d4 --- /dev/null +++ b/code-samples/type-aliases-complex-types-trait.pony @@ -0,0 +1,10 @@ +trait HasName + fun name(): String => "Bob" + +trait HasAge + fun age(): U32 => 42 + +trait HasFeelings + fun feeling(): String => "Great!" + +type Person is (HasName & HasAge & HasFeelings) \ No newline at end of file diff --git a/code-samples/type-aliases-enumerations-apply.pony b/code-samples/type-aliases-enumerations-apply.pony new file mode 100644 index 00000000..96190412 --- /dev/null +++ b/code-samples/type-aliases-enumerations-apply.pony @@ -0,0 +1,5 @@ +primitive Red fun apply(): U32 => 0xFF0000FF +primitive Green fun apply(): U32 => 0x00FF00FF +primitive Blue fun apply(): U32 => 0x0000FFFF + +type Colour is (Red | Blue | Green) \ No newline at end of file diff --git a/code-samples/type-aliases-enumerations-iteration.pony b/code-samples/type-aliases-enumerations-iteration.pony new file mode 100644 index 00000000..ccf62fcb --- /dev/null +++ b/code-samples/type-aliases-enumerations-iteration.pony @@ -0,0 +1,7 @@ +primitive ColourList + fun apply(): Array[Colour] => + [Red; Green; Blue] + +for colour in ColourList().values() do + env.out.print(colour().string()) +end \ No newline at end of file diff --git a/code-samples/type-aliases-enumerations-namespace.pony b/code-samples/type-aliases-enumerations-namespace.pony new file mode 100644 index 00000000..8646fa77 --- /dev/null +++ b/code-samples/type-aliases-enumerations-namespace.pony @@ -0,0 +1,3 @@ +primitive Colours + fun red(): U32 => 0xFF0000FF + fun green(): U32 => 0x00FF00FF \ No newline at end of file diff --git a/code-samples/type-aliases-enumerations.pony b/code-samples/type-aliases-enumerations.pony new file mode 100644 index 00000000..7a373548 --- /dev/null +++ b/code-samples/type-aliases-enumerations.pony @@ -0,0 +1,5 @@ +primitive Red +primitive Blue +primitive Green + +type Colour is (Red | Blue | Green) \ No newline at end of file diff --git a/code-samples/type-aliases-hash-set.pony b/code-samples/type-aliases-hash-set.pony new file mode 100644 index 00000000..c5a20122 --- /dev/null +++ b/code-samples/type-aliases-hash-set.pony @@ -0,0 +1 @@ +HashSet[A, HashIs[A!]] \ No newline at end of file diff --git a/code-samples/type-aliases-map.pony b/code-samples/type-aliases-map.pony new file mode 100644 index 00000000..3ad4bb2d --- /dev/null +++ b/code-samples/type-aliases-map.pony @@ -0,0 +1 @@ +type Map[K: (Hashable box & Comparable[K] box), V] is HashMap[K, V, HashEq[K]] \ No newline at end of file diff --git a/code-samples/type-aliases-set-is.pony b/code-samples/type-aliases-set-is.pony new file mode 100644 index 00000000..7555b9d2 --- /dev/null +++ b/code-samples/type-aliases-set-is.pony @@ -0,0 +1 @@ +type SetIs[A] is HashSet[A, HashIs[A!]] \ No newline at end of file diff --git a/code-samples/type-expressions-combined.pony b/code-samples/type-expressions-combined.pony new file mode 100644 index 00000000..f700f40d --- /dev/null +++ b/code-samples/type-expressions-combined.pony @@ -0,0 +1 @@ +var _array: Array[((K, V) | _MapEmpty | _MapDeleted)] \ No newline at end of file diff --git a/code-samples/type-expressions-intersection.pony b/code-samples/type-expressions-intersection.pony new file mode 100644 index 00000000..3ad4bb2d --- /dev/null +++ b/code-samples/type-expressions-intersection.pony @@ -0,0 +1 @@ +type Map[K: (Hashable box & Comparable[K] box), V] is HashMap[K, V, HashEq[K]] \ No newline at end of file diff --git a/code-samples/type-expressions-tuple-declaration.pony b/code-samples/type-expressions-tuple-declaration.pony new file mode 100644 index 00000000..91ddbb4c --- /dev/null +++ b/code-samples/type-expressions-tuple-declaration.pony @@ -0,0 +1,3 @@ +var x: (String, U64) +x = ("hi", 3) +x = ("bye", 7) \ No newline at end of file diff --git a/code-samples/type-expressions-tuple-destructuring.pony b/code-samples/type-expressions-tuple-destructuring.pony new file mode 100644 index 00000000..90bcad40 --- /dev/null +++ b/code-samples/type-expressions-tuple-destructuring.pony @@ -0,0 +1 @@ +(var y, var z) = x \ No newline at end of file diff --git a/code-samples/type-expressions-tuple-direct-access.pony b/code-samples/type-expressions-tuple-direct-access.pony new file mode 100644 index 00000000..d8ac3526 --- /dev/null +++ b/code-samples/type-expressions-tuple-direct-access.pony @@ -0,0 +1,2 @@ +var y = x._1 +var z = x._2 \ No newline at end of file diff --git a/code-samples/type-expressions-tuple-reassignment.pony b/code-samples/type-expressions-tuple-reassignment.pony new file mode 100644 index 00000000..598170ab --- /dev/null +++ b/code-samples/type-expressions-tuple-reassignment.pony @@ -0,0 +1 @@ +x = ("wombat", x._2) \ No newline at end of file diff --git a/code-samples/type-expressions-type-alias.pony b/code-samples/type-expressions-type-alias.pony new file mode 100644 index 00000000..aedd7d7e --- /dev/null +++ b/code-samples/type-expressions-type-alias.pony @@ -0,0 +1,7 @@ +type Number is (Signed | Unsigned | Float) + +type Signed is (I8 | I16 | I32 | I64 | I128) + +type Unsigned is (U8 | U16 | U32 | U64 | U128) + +type Float is (F32 | F64) \ No newline at end of file diff --git a/code-samples/type-expressions-union.pony b/code-samples/type-expressions-union.pony new file mode 100644 index 00000000..08956872 --- /dev/null +++ b/code-samples/type-expressions-union.pony @@ -0,0 +1 @@ +var x: (String | None) \ No newline at end of file diff --git a/code-samples/use-statement-collections.pony b/code-samples/use-statement-collections.pony new file mode 100644 index 00000000..3fb9c505 --- /dev/null +++ b/code-samples/use-statement-collections.pony @@ -0,0 +1 @@ +use "collections" \ No newline at end of file diff --git a/code-samples/use-statement-scheme-indicators-optional-package-scheme-specifier.pony b/code-samples/use-statement-scheme-indicators-optional-package-scheme-specifier.pony new file mode 100644 index 00000000..982005af --- /dev/null +++ b/code-samples/use-statement-scheme-indicators-optional-package-scheme-specifier.pony @@ -0,0 +1,2 @@ +use "foo" +use "package:foo" \ No newline at end of file diff --git a/code-samples/use-statement-scheme-indicators-required-package-scheme-specifier.pony b/code-samples/use-statement-scheme-indicators-required-package-scheme-specifier.pony new file mode 100644 index 00000000..92a4bde3 --- /dev/null +++ b/code-samples/use-statement-scheme-indicators-required-package-scheme-specifier.pony @@ -0,0 +1,2 @@ +use "C:/foo/bar" // Error, scheme "C" is unknown +use "package:C:/foo/bar" // OK \ No newline at end of file diff --git a/code-samples/use-statement-time-now.pony b/code-samples/use-statement-time-now.pony new file mode 100644 index 00000000..b05f93e2 --- /dev/null +++ b/code-samples/use-statement-time-now.pony @@ -0,0 +1,5 @@ +use "time" + +class Foo + fun f() => + (var secs, var nsecs) = Time.now() \ No newline at end of file diff --git a/code-samples/use-statement-time.pony b/code-samples/use-statement-time.pony new file mode 100644 index 00000000..0abd8ee6 --- /dev/null +++ b/code-samples/use-statement-time.pony @@ -0,0 +1,2 @@ +primitive Time + fun now(): (I64, I64) \ No newline at end of file diff --git a/code-samples/use-statement-use-names-conflict.pony b/code-samples/use-statement-use-names-conflict.pony new file mode 100644 index 00000000..0b8b28bf --- /dev/null +++ b/code-samples/use-statement-use-names-conflict.pony @@ -0,0 +1,12 @@ +// In package A +class Foo + +// In package B +class Foo + +// In your code +use "packageA" +use "packageB" + +class Bar + var _x: Foo \ No newline at end of file diff --git a/code-samples/use-statement-use-names-resolution-alternative.pony b/code-samples/use-statement-use-names-resolution-alternative.pony new file mode 100644 index 00000000..701e4ff6 --- /dev/null +++ b/code-samples/use-statement-use-names-resolution-alternative.pony @@ -0,0 +1,13 @@ +// In package A +class Foo + +// In package B +class Foo + +// In your code +use "packageA" +use b = "packageB" + +class Bar + var _x: Foo // The Foo from package A + var _y: b.Foo // The Foo from package B \ No newline at end of file diff --git a/code-samples/use-statement-use-names-resolution.pony b/code-samples/use-statement-use-names-resolution.pony new file mode 100644 index 00000000..202cc472 --- /dev/null +++ b/code-samples/use-statement-use-names-resolution.pony @@ -0,0 +1,13 @@ +// In package A +class Foo + +// In package B +class Foo + +// In your code +use a = "packageA" +use b = "packageB" + +class Bar + var _x: a.Foo // The Foo from package A + var _y: b.Foo // The Foo from package B \ No newline at end of file diff --git a/code-samples/variables-fields-constructor-assignment.pony b/code-samples/variables-fields-constructor-assignment.pony new file mode 100644 index 00000000..88263d91 --- /dev/null +++ b/code-samples/variables-fields-constructor-assignment.pony @@ -0,0 +1,7 @@ +class Wombat + let name: String + var _hunger_level: U32 + + new create(hunger: U32) => + name = "Fantastibat" + _hunger_level = hunger \ No newline at end of file diff --git a/code-samples/variables-fields-definition-assignment.pony b/code-samples/variables-fields-definition-assignment.pony new file mode 100644 index 00000000..b9c7fc18 --- /dev/null +++ b/code-samples/variables-fields-definition-assignment.pony @@ -0,0 +1,3 @@ +class Wombat + let name: String = "Fantastibat" + var _hunger_level: U32 = 0 \ No newline at end of file diff --git a/code-samples/variables-fields-implicit-assignment.pony b/code-samples/variables-fields-implicit-assignment.pony new file mode 100644 index 00000000..f4bc1468 --- /dev/null +++ b/code-samples/variables-fields-implicit-assignment.pony @@ -0,0 +1,11 @@ +class Wombat + let name: String + var _hunger_level: U64 + + new ref create(name': String, level: U64) => + name = name' + set_hunger_level(level) + // Error: field _hunger_level left undefined in constructor + + fun ref set_hunger_level(hunger_level: U64) => + _hunger_level = hunger_level \ No newline at end of file diff --git a/code-samples/variables-fields-let-reassignment.pony b/code-samples/variables-fields-let-reassignment.pony new file mode 100644 index 00000000..4771bc4d --- /dev/null +++ b/code-samples/variables-fields-let-reassignment.pony @@ -0,0 +1,13 @@ +class Wombat + let name: String + var _hunger_level: U64 + + new ref create(name': String, level: U64) => + name = name' + _hunger_level = level + + fun ref set_hunger_level(hunger_level: U64) => + _hunger_level = hunger_level // Ok, _hunger_level is of var type + + fun ref set_name(name' : String) => + name = name' // Error, can't assign to a let definition more than once \ No newline at end of file diff --git a/code-samples/variables-let-reassignment.pony b/code-samples/variables-let-reassignment.pony new file mode 100644 index 00000000..46d96247 --- /dev/null +++ b/code-samples/variables-let-reassignment.pony @@ -0,0 +1,3 @@ +let x: U32 = 3 // Ok +let y: U32 // Error, can't declare a let local without assigning to it +y = 6 // Error, can't reassign to a let local \ No newline at end of file diff --git a/code-samples/variables-local-variables.pony b/code-samples/variables-local-variables.pony new file mode 100644 index 00000000..d8f25410 --- /dev/null +++ b/code-samples/variables-local-variables.pony @@ -0,0 +1,6 @@ +var x: String = "Hello" + +var y = "Hello" + +var z: String +z = "Hello" \ No newline at end of file diff --git a/code-samples/variables-scope.pony b/code-samples/variables-scope.pony new file mode 100644 index 00000000..2b27bbbb --- /dev/null +++ b/code-samples/variables-scope.pony @@ -0,0 +1,6 @@ +if a > b then + var x = "a is bigger" + env.out.print(x) // OK +end + +env.out.print(x) // Illegal \ No newline at end of file diff --git a/code-samples/variables-var-vs-let.pony b/code-samples/variables-var-vs-let.pony new file mode 100644 index 00000000..285cc055 --- /dev/null +++ b/code-samples/variables-var-vs-let.pony @@ -0,0 +1,4 @@ +var x: U32 = 3 +let y: U32 = 4 +x = 5 // OK +y = 6 // Error, y is let \ No newline at end of file diff --git a/docs/appendices/annotations.md b/docs/appendices/annotations.md index ca3c0f6c..d30175be 100644 --- a/docs/appendices/annotations.md +++ b/docs/appendices/annotations.md @@ -3,7 +3,7 @@ In Pony, we provide a special syntax for implementation-specific annotations to various elements of a program. The basic syntax is a comma-separated list of identifiers surrounded by backslashes: ```pony -\annotation1, annotation2\ +--8<-- "appendices-annotations-syntax.pony" ``` Here, `annotation1` and `annotation2` can be any valid Pony identifier, i.e. a sequence of alphanumeric characters starting with a letter or an underscore. @@ -51,9 +51,7 @@ The following annotations are recognised by the Pony compiler. Note that the Pon Recognised on a `struct` declaration. Removes padding in the associated `struct`, making it ABI-compatible with a packed C structure with compatible members (declared with the `__attribute__((packed))` extension or the `#pragma pack` preprocessor directive in many C compilers). ```pony -struct \packed\ MyPackedStruct - var x: U8 - var y: U32 +--8<-- "appendices-annotations-packed-annotation.pony" ``` #### `likely` and `unlikely` @@ -61,22 +59,7 @@ struct \packed\ MyPackedStruct Recognised on a conditional expression (`if`, `while`, `until` and `|` (as a pattern matching case)). Gives optimisation hints to the compiler on the likelihood of a given conditional expression. ```pony -if \likely\ cond then - foo -end - -while \unlikely\ cond then - bar -end - -repeat - baz -until \likely\ cond end - -match obj -| \likely\ expr => foo -| \unlikely\ let capt: T => bar -end +--8<-- "appendices-annotations-likely-and-unlikely-annotations.pony" ``` ### `nodoc` @@ -84,10 +67,7 @@ end Recognised on objects and methods (`actor`, `class`, `struct`, `primitive`, `trait`, `interface`, `new`, `be`, `fun`). Indicates to the documentation system that the item and any of its children shouldn't be included in generated output. ```pony -class \nodoc\Foo - """ - We don't want this class and its methods to appear in generated documentation - """ +--8<-- "appendices-annotations-nodoc-annotation.pony" ``` ### `nosupertype` @@ -97,13 +77,7 @@ Recognised on objects(`actor`, `class`, `primitive`, `struct`). A type annotated Here's an example of how `nosupertype` can be important: ```pony -class Empty - -class Foo - fun foo[A: Any](a: (A | Empty val)) => - match consume a - | let a': A => None - end +--8<-- "appendices-annotations-empty-without-nosupertype-annotation.pony" ``` The above code won't compile because you could supply `Empty ref`. Doing so results in a compiler error about an unsafe match because we would need to distinguish between `Empty val` and `Empty ref` at runtime. @@ -111,13 +85,7 @@ The above code won't compile because you could supply `Empty ref`. Doing so resu By adding `nosupertype` to the definition of `Empty`, we declare that `Empty` is not a subtype of `Any` and thereby allow the code to compile as there is no longer an unsafe match. ```pony -class \nosupertype\ Empty - -class Foo - fun foo[A: Any](a: (A | Empty val)) => - match consume a - | let a': A => None - end +--8<-- "appendices-annotations-empty-with-nosupertype-annotation.pony" ``` `nosupertype` is particularly valuable when constructing generic classes like collections that need a marker class to describe "lack of an item". diff --git a/docs/appendices/compiler-args.md b/docs/appendices/compiler-args.md index d1643120..0452dff1 100644 --- a/docs/appendices/compiler-args.md +++ b/docs/appendices/compiler-args.md @@ -19,8 +19,8 @@ The most useful options are `--debug`, `--path` or just `-p`, `--output` or just Let's study the documentation of the builtin standard library: ```bash - pip install mkdocs - ponyc packages/stdlib --docs && cd stdlib-docs && mkdocs serve +pip install mkdocs +ponyc packages/stdlib --docs && cd stdlib-docs && mkdocs serve ``` And point your web browser to [127.0.0.1:8000](http://127.0.0.1:8000) serving a live-reloading local version of the docs. diff --git a/docs/appendices/error-messages.md b/docs/appendices/error-messages.md index a6db5059..effc5fbf 100644 --- a/docs/appendices/error-messages.md +++ b/docs/appendices/error-messages.md @@ -13,10 +13,7 @@ Let's start with a simple one. Suppose you wrote: ```pony -actor Main - let x: I64 = 0 - new create(env: Env) => - x = 12 +--8<-- "error-messages-left-side-must-be-something-that-can-be-assigned-to.pony" ``` The error message would be: @@ -41,10 +38,7 @@ That one error resulted in two error messages. The first, pointing to the `x`, d Suppose you create a class with a mutable field and added a method to change the field: ```pony -class Wombat - var color: String = "brown" - fun dye(new_color: String) => - color = new_color +--8<-- "error-messages-left-side-is-immutable.pony" ``` The error message would be: @@ -65,10 +59,7 @@ To fix the error, you would need to give the `dye` method a mutable reference ca Suppose you made a related, but slightly different error: ```pony -class Rainbow - let colors: Array[String] = Array[String] - fun add_stripe(color: String) => - colors.push(color) +--8<-- "error-messages-receiver-type-is-not-a-subtype-of-target-type.pony" ``` In this example, rather than trying to change the value of a field, the code calls a method which attempts to modify the object referred to by the field. diff --git a/docs/appendices/examples.md b/docs/appendices/examples.md index 1f09f03e..437d0979 100644 --- a/docs/appendices/examples.md +++ b/docs/appendices/examples.md @@ -5,108 +5,37 @@ Small _how do I_ examples for Pony. These will eventually find another home. Unt ## Enumeration with values ```pony -primitive Black fun apply(): U32 => 0xFF000000 -primitive Red fun apply(): U32 => 0xFFFF0000 +--8<-- "appendices-examples-enumeration-with-values.pony" ``` ## Enumeration with values with namespace ```pony -primitive Colours - fun black(): U32 => 0xFF000000 - fun red(): U32 => 0xFFFF0000 +--8<-- "appendices-examples-enumeration-with-values-with-namespace.pony" ``` ## Enumeration which can be iterated ```pony -primitive Black -primitive Blue -primitive Red -primitive Yellow - -type Colour is (Black | Blue | Red | Yellow) - -primitive ColourList - fun tag apply(): Array[Colour] => - [Black; Blue; Red; Yellow] - -for colour in ColourList().values() do -end +--8<-- "appendices-examples-iterable-enumerations.pony" ``` ## Pass an Array of values to FFI ```pony -use @eglChooseConfig[U32](disp: Pointer[_EGLDisplayHandle], attrs: Pointer[U16] tag, - config: Pointer[_EGLConfigHandle], config_size: U32, num_config: Pointer[U32]) - -primitive _EGLConfigHandle -let a = Array[U16](8) -a.push(0x3040) -a.push(0x4) -a.push(0x3033) -a.push(0x4) -a.push(0x3022) -a.push(0x8) -a.push(0x3023) -a.push(0x8) -a.push(0x3024) -let config = Pointer[_EGLConfigHandle] -if @eglChooseConfig(e_dpy, a.cpointer(), config, U32(1), Pointer[U32]) == 0 then - env.out.print("eglChooseConfig failed") -end +--8<-- "appendices-examples-pass-array-of-values-to-ffi.pony" ``` ## 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 application name - try env.out.print(env.args(0)?) end +--8<-- "appendices-examples-access-command-line-arguments.pony" ``` ## How to use the `cli` package to parse command line arguments ```pony -use "cli" - -actor Main - new create(env: Env) => - let command_spec = - try - CommandSpec.leaf( - "pony-embed", - "sample program", - [ OptionSpec.string("output", "output filename", 'o') ], - [ ArgSpec.string("input", "source of input" where default' = "-") ] - )? .> add_help()? - else - env.exitcode(1) - return - end - let command = - match CommandParser(command_spec).parse(env.args, env.vars) - | let c: Command => c - | let ch: CommandHelp => - ch.print_help(env.out) - env.exitcode(0) - return - | let se: SyntaxError => - env.err.print(se.string()) - env.exitcode(1) - return - end - let input_source = command.arg("input").string() - let output_filename = command.option("output").string() - env.out.print("Loading data from " + input_source + ". Writing output to " + output_filename) - // ... +--8<-- "appendices-examples-use-cli-package-to-parse-command-line-arguments.pony" ``` ## How to write tests @@ -114,77 +43,25 @@ actor Main Create a test.pony file ```pony -use "pony_test" - -actor Main is TestList - new create(env: Env) => PonyTest(env, this) - new make() => None - - fun tag tests(test: PonyTest) => - test(_TestAddition) - -class iso _TestAddition is UnitTest - """ - Adding 2 numbers - """ - fun name(): String => "u32/add" - - fun apply(h: TestHelper) => - h.assert_eq[U32](2 + 2, 4) +--8<-- "appendices-examples-write-tests.pony" ``` Some assertions you can make with `TestHelper` are ```pony -fun tag log(msg: String, verbose: Bool = false) -be fail() => -be assert_failed(msg: String) => -fun tag assert_true(actual: Bool, msg: String = "") ? -fun tag expect_true(actual: Bool, msg: String = ""): Bool -fun tag assert_false(actual: Bool, msg: String = "") ? -fun tag expect_false(actual: Bool, msg: String = ""): Bool -fun tag assert_error(test: ITest, msg: String = "") ? -fun tag expect_error(test: ITest box, msg: String = ""): Bool -fun tag assert_is (expect: Any, actual: Any, msg: String = "") ? -fun tag expect_is (expect: Any, actual: Any, msg: String = ""): Bool -fun tag assert_eq[A: (Equatable[A] #read & Stringable)] - (expect: A, actual: A, msg: String = "") ? -fun tag expect_eq[A: (Equatable[A] #read & Stringable)] - (expect: A, actual: A, msg: String = ""): Bool +--8<-- "appendices-examples-test-helper.pony" ``` ## Operator overloading (easy for copy and paste) ```pony -fun add(other: A): A -fun sub(other: A): A -fun mul(other: A): A -fun div(other: A): A -fun rem(other: A): A -fun mod(other: A): A -fun eq(other: A): Bool -fun ne(other: A): Bool -fun lt(other: A): Bool -fun le(other: A): Bool -fun ge(other: A): Bool -fun gt(other: A): Bool -fun shl(other: A): A -fun shr(other: A): A -fun op_and(other:A): A -fun op_or(other: A): A -fun op_xor(othr: A): A +--8<-- "appendices-examples-operator-overloading.pony" ``` ## Create empty functions in a class ```pony -class Test - fun alpha() => - """ - """ - fun beta() => - """ - """ +--8<-- "appendices-examples-empty-class-functions.pony" ``` ## How to create Arrays with values @@ -192,30 +69,11 @@ class Test Single values can be separated by semicolon or newline. ```pony -let dice: Array[U32] = [1; 2; 3 - 4 - 5 - 6 -] +--8<-- "appendices-examples-create-arrays-with-values.pony" ``` ## How to modify a lexically captured variable in a closure ```pony -actor Main - fun foo(n:U32): {ref(U32): U32} => - var s: Array[U32] = Array[U32].init(n, 1) - {ref(i:U32)(s): U32 => - try - s(0)? = s(0)? + i - s(0)? - else - 0 - end - } - - new create(env:Env) => - var f = foo(5) - env.out.print(f(10).string()) - env.out.print(f(20).string()) +--8<-- "appendices-examples-modify-a-lexically-captured-variable-in-a-closure.pony" ``` diff --git a/docs/appendices/platform-dependent-code.md b/docs/appendices/platform-dependent-code.md index f1fece0c..83a06e4d 100644 --- a/docs/appendices/platform-dependent-code.md +++ b/docs/appendices/platform-dependent-code.md @@ -3,8 +3,7 @@ The Pony libraries, of course, want to abstract platform differences. Sometimes you may want a `use` command that only works under certain circumstances, most commonly only on a particular OS or only for debug builds. You can do this by specifying a condition for a `use` command: ```pony -use "foo" if linux -use "bar" if (windows and debug) +--8<-- "appendices-platform-dependent-code.pony" ``` Use conditions can use any of the methods defined in `builtin/Platform` as conditions. diff --git a/docs/appendices/serialisation.md b/docs/appendices/serialisation.md index 7987ce8e..1d318776 100644 --- a/docs/appendices/serialisation.md +++ b/docs/appendices/serialisation.md @@ -13,46 +13,7 @@ Pony uses an intermediate object type called `Serialised` to represent a seriali This program serialises and deserialise an object, and checks that the fields of the original object are the same as the fields of the deserialised object. ```pony -use "serialise" - -class Foo is Equatable[Foo box] - let _s: String - let _u: U32 - - new create(s: String, u: U32) => - _s = s - _u = u - - fun eq(foo: Foo box): Bool => - (_s == foo._s) and (_u == foo._u) - -actor Main - new create(env: Env) => - try - // get serialization authorities - let serialise = SerialiseAuth(env.root) - let output = OutputSerialisedAuth(env.root) - let deserialise = DeserialiseAuth(env.root) - let input = InputSerialisedAuth(env.root) - - let foo1 = Foo("abc", 123) - - // serialisation - let sfoo = Serialised(serialise, foo1)? - let bytes_foo: Array[U8] val = sfoo.output(output) - - env.out.print("serialised representation is " + - bytes_foo.size().string() + - " bytes long") - - // deserialisation - let dfoo = Serialised.input(input, bytes_foo) - let foo2 = dfoo(deserialise)? as Foo - - env.out.print("(foo1 == foo2) is " + (foo1 == foo2).string()) - else - env.err.print("there was an error") - end +--8<-- "appendices-serialization-compare-original-object-with-deserialized-object.pony" ``` ## Caveats @@ -97,48 +58,7 @@ If a class has more than one `Pointer` field then all of those fields must be ha Assume we have a Pony class with a field that is a pointer to a C string. We would like to be able to serialise and deserialise this object. In order to do that, the Pony class implements the methods `_serialise_space(...)`, `_serialise(...)`, and `_deserialise(...)`. These methods, in turn, call C functions that calculate the number of bytes needed to serialise the string and serialise and deserialise it. In this example the serialised string is represented by a four-byte big-endian number that represents the length of the string, followed by the string itself without the terminating null. So if the C string is `hello world\0` then the serialised string is `\0x00\0x00\0x00\0x0Bhello world` (where the first four bytes of the serialised string are a big-endian representation of the number `0x0000000B`, which is `11`). ```pony -use "serialise" - -use "lib:custser" - -use @get_string[Pointer[U8]]() -use @serialise_space[USize](s: Pointer[U8] tag) -use @serialise[None](bytes: Pointer[U8] tag, str: Pointer[U8] tag) -use @deserialise[Pointer[U8] tag](bytes: Pointer[U8] tag) -use @printf[I32](fmt: Pointer[U8] tag, ...) - -class CStringWrapper - var _cstr: Pointer[U8] tag - - new create(cstr: Pointer[U8] tag) => - _cstr = cstr - - fun _serialise_space(): USize => - @serialise_space(_cstr) - - fun _serialise(bytes: Pointer[U8] tag) => - @serialise(bytes, _cstr) - - fun ref _deserialise(bytes: Pointer[U8] tag) => - _cstr = @deserialise(bytes) - - fun print() => - @printf(_cstr) - -actor Main - new create(env: Env) => - let csw = CStringWrapper(@get_string()) - csw.print() - try - let serialise = SerialiseAuth(env.root) - let deserialise = DeserialiseAuth(env.root) - - let sx = Serialised(serialise, csw)? - let y = sx(deserialise)? as CStringWrapper - y.print() - else - env.err.print("there was an error") - end +--8<-- "appendices-serialization-custom-serialization.pony" ``` ```c diff --git a/docs/appendices/whitespace.md b/docs/appendices/whitespace.md index dcf339c9..e909b432 100644 --- a/docs/appendices/whitespace.md +++ b/docs/appendices/whitespace.md @@ -19,14 +19,13 @@ There are three exceptions: That stuff may seem a little esoteric right now, but we'll explain it all later. The `-` part should make sense though. ```pony -a - b +--8<-- "appendices-whitespace-subtract-b-from-a.pony" ``` That means "subtract b from a". ```pony -a --b +--8<-- "appendices-whitespace-do-a-then-do-a-unary-negation-of-b.pony" ``` That means "first do a, then, in a new expression, do a unary negation of b". @@ -50,27 +49,7 @@ For traits and interfaces that have methods without bodies, you can put the docs By convention, a docstring should be a triple-quoted string, and it should use Markdown for any formatting. ```pony -actor Main - """ - This is documentation for my Main actor - """ - - var count: USize = 0 - """ - This is documentation for my count field - """ - - new create(env: Env) => - """ - This is documentation for my create method - """ - None - -trait Readable - fun val read() - """ - This is documentation for my unimplemented read method - """ +--8<-- "appendices-whitespace-docstrings.pony" ``` ## Comments @@ -78,7 +57,5 @@ trait Readable Use __docstrings__ first! But if you need to put some comments in the implementation of your methods, perhaps to explain what's happening on various lines, you can use C++ style comments. In Pony, block comments can be nested. ```pony -// This is a line comment. -/* This is a block comment. */ -/* This block comment /* has another block comment */ inside of it. */ +--8<-- "appendices-whitespace-comments.pony" ``` diff --git a/docs/c-ffi/c-abi.md b/docs/c-ffi/c-abi.md index 9e57a11b..cd9fc54a 100644 --- a/docs/c-ffi/c-abi.md +++ b/docs/c-ffi/c-abi.md @@ -9,21 +9,7 @@ Writing your own C library for use by Pony is almost as easy as using existing l Let's look at a complete example of a C function we may wish to provide to Pony. Let's consider a pure Pony implementation of a [Jump Consistent Hash](https://arxiv.org/abs/1406.2294): ```pony -// Jump consistent hashing in Pony, with an inline pseudo random generator -// https://arxiv.org/abs/1406.2294 - -fun jch(key: U64, buckets: U32): I32 => - var k = key - var b = I64(0) - var j = I64(0) - - while j < buckets.i64() do - b = j - k = (k * 2862933555777941757) + 1 - j = ((b + 1).f64() * (I64(1 << 31).f64() / ((k >> 33) + 1).f64())).i64() - end - - b.i32() +--8<-- "c-abi-jump-consistent-hashing.pony" ``` Let's say we wish to compare the pure Pony performance to an existing C function with the following header: @@ -75,47 +61,7 @@ clang -shared -lm -o libjch.dylib jch.o The Pony code to use this new C library is just like the code we've already seen for using C libraries. ```pony -""" -This is an example of Pony integrating with native code via the built-in FFI -support -""" - -use "lib:jch" -use "collections" -use @jch_chash[I32](hash: U64, bucket_size: U32) - -actor Main - var _env: Env - - new create(env: Env) => - _env = env - - let bucket_size: U32 = 1000000 - - _env.out.print("C implementation:") - for i in Range[U64](1, 20) do - let hash = @jch_chash(i, bucket_size) - _env.out.print(i.string() + ": " + hash.string()) - end - - _env.out.print("Pony implementation:") - for i in Range[U64](1, 20) do - let hash = jch(i, bucket_size) - _env.out.print(i.string() + ": " + hash.string()) - end - - fun jch(key: U64, buckets: U32): I32 => - var k = key - var b = I64(0) - var j = I64(0) - - while j < buckets.i64() do - b = j - k = (k * 2862933555777941757) + 1 - j = ((b + 1).f64() * (I64(1 << 31).f64() / ((k >> 33) + 1).f64())).i64() - end - - b.i32() +--8<-- "c-abi-pony-use-native-jump-consistent-hashing-c-implementation.pony" ``` We can now use ponyc to compile a native executable integrating Pony and our C library. And that's all we need to do. diff --git a/docs/c-ffi/callbacks.md b/docs/c-ffi/callbacks.md index 53aaa144..e993dd29 100644 --- a/docs/c-ffi/callbacks.md +++ b/docs/c-ffi/callbacks.md @@ -9,15 +9,13 @@ Classic Pony functions have a receiver, which acts as an implicit argument to th You can define a bare function by prefixing the function name with the @ symbol. ```pony -class C - fun @callback() => - ... +--8<-- "c-ffi-callbacks-bare-functions.pony" ``` The function can then be passed as a callback to a C API with the `addressof` operator. ```pony -@setup_callback(addressof C.callback) +--8<-- "c-ffi-callbacks-bare-functions-pass-to-c-api.pony" ``` Note that it is possible to use an object reference instead of a type as the left-hand side of the method access. @@ -29,15 +27,13 @@ Since bare methods have no receiver, they cannot reference the `this` identifier Bare lambdas are special lambdas defining bare functions. A bare lambda or bare lambda type is specified using the same syntax as other lambda types, with the small variation that it is prefixed with the @ symbol. The underlying value of a bare lambda is equivalent to a C function pointer, which means that a bare lambda can be directly passed as a callback to a C function. The partial application of a bare method yields a bare lambda. ```pony -let callback = @{() => ... } -@setup_callback(callback) +--8<-- "c-ffi-callbacks-bare-lambda-callback.pony" ``` Bare lambdas can also be used to define structures containing function pointers. For example: ```pony -struct S - var fun_ptr: @{()} +--8<-- "c-ffi-callbacks-bare-lambda-struct.pony" ``` This Pony structure is equivalent to the following C structure: @@ -81,39 +77,11 @@ char **pzErrMsg /* Write error messages here */ Here's the skeleton of some Pony code that uses `sqlite3_exec` to query an SQLite database, with examples of both the bare method way and the bare lambda way: ```pony -use @sqlite3_exec[I32](db: Pointer[None] tag, sql: Pointer[U8] tag, - callback: Pointer[None], data: Pointer[None], err_msg: Pointer[Pointer[U8] tag] tag) - -class SQLiteClient - fun client_code() => - ... - @sqlite3_exec(db, sql.cstring(), addressof this.method_callback, - this, addressof zErrMsg) - ... - - fun @method_callback(client: SQLiteClient, argc: I32, - argv: Pointer[Pointer[U8]], azColName: Pointer[Pointer[U8]]): I32 - => - ... +--8<-- "c-ffi-callbacks-sqlite3-callback.pony" ``` ```pony -use @sqlite3_exec[I32](db: Pointer[None] tag, sql: Pointer[U8] tag, - callback: Pointer[None], data: Pointer[None], err_msg: Pointer[Pointer[U8] tag] tag) - -class SQLiteClient - fun client_code() => - ... - let lambda_callback = - @{(client: SQLiteClient, argc: I32, argv: Pointer[Pointer[U8]], - azColName: Pointer[Pointer[U8]]): I32 - => - ... - } - - @sqlite3_exec(db, sql.cstring(), lambda_callback, this, - addressof zErrMsg) - ... +--8<-- "c-ffi-callbacks-sqlite3-callback-2.pony" ``` Focusing on the callback-related parts, the callback function is passed using `addressof this.method_callback` (resp. by directly passing the bare lambda) as the third argument to `sqlite3_exec`. The fourth argument is `this`, which will end up being the first argument when the callback function is called. The callback function is called in `sqlite3_exec` by the call to `xCallback`. diff --git a/docs/c-ffi/calling-c.md b/docs/c-ffi/calling-c.md index 581f5219..a0e90b08 100644 --- a/docs/c-ffi/calling-c.md +++ b/docs/c-ffi/calling-c.md @@ -11,17 +11,7 @@ To help avoid bugs, Pony requires you to specify the type signatures of FFI func Here's an example of an FFI signature and call from the standard library: ```pony -use @_mkdir[I32](dir: Pointer[U8] tag) if windows -use @mkdir[I32](path: Pointer[U8] tag, mode: U32) if not windows - -class val FilePath - fun val mkdir(must_create: Bool = false): Bool => - // ... - let r = ifdef windows then - @_mkdir(element.cstring()) - else - @mkdir(element.cstring(), 0x1FF) - end +--8<-- "calling-c-file-path.pony" ``` FFI functions have the @ symbol before its name, and FFI signatures are declared using the `use` command. The types specified here are considered authoritative, and any FFI calls that use different parameter types will result in a compile error. @@ -45,17 +35,7 @@ Pony classes and structs correspond directly to pointers to the class or struct For C pointers to simple types, such as U64, the Pony `Pointer[]` polymorphic type should be used, with a `tag` reference capability. To represent `void*` arguments, you should use the `Pointer[None] tag` type, which will allow you to pass a pointer to any type, including other pointers. This is needed to write declarations for certain POSIX functions, such as `memcpy`: ```pony -// The C type is void* memcpy(void *restrict dst, const void *restrict src, size_t n); -use @memcpy[Pointer[U8]](dst: Pointer[None] tag, src: Pointer[None] tag, n: USize) - -// Now we can use memcpy with any Pointer type -let out: Pointer[Pointer[U8] tag] tag = // ... -let outlen: Pointer[U8] tag = // ... -let ptr: Pointer[U8] tag = // ... -let size: USize = // ... -// ... -@memcpy(out, addressof ptr, size.bitwidth() / 8) -@memcpy(outlen, addressof size, 1) +--8<-- "calling-c-memcpy.pony" ``` When dealing with `void*` return types from C, it is good practice to try to narrow the type down to the most specific Pony type that you expect to receive. In the example above, we chose `Pointer[U8]` as the return type, since we can use such a pointer to construct Pony Arrays and Strings. @@ -63,10 +43,7 @@ When dealing with `void*` return types from C, it is good practice to try to nar To pass pointers to values to C the `addressof` operator can be used (previously `&`), just like taking an address in C. This is done in the standard library to pass the address of a `U32` to an FFI function that takes a `int*` as an out parameter: ```pony -use @frexp[F64](value: F64, exponent: Pointer[U32]) -// ... -var exponent: U32 = 0 -var mantissa = @frexp(this, addressof exponent) +--8<-- "calling-c-addressof.pony" ``` ### Get and Pass Pointers to FFI @@ -74,21 +51,7 @@ var mantissa = @frexp(this, addressof exponent) If you want to receive a pointer to an opaque C type, using a pointer to a primitive can be useful: ```pony -use @XOpenDisplay[Pointer[_XDisplayHandle]](name: Pointer[U8] tag) -use @eglGetDisplay[Pointer[_EGLDisplayHandle]](disp: Pointer[_XDisplayHandle]) - -primitive _XDisplayHandle -primitive _EGLDisplayHandle - -let x_dpy = @XOpenDisplay(Pointer[U8]) -if x_dpy.is_null() then - env.out.print("XOpenDisplay failed") -end - -let e_dpy = @eglGetDisplay(x_dpy) -if e_dpy.is_null() then - env.out.print("eglGetDisplay failed") -end +--8<-- "calling-c-pointer-to-opaque-c-type.pony" ``` The above example would also work if we used `Pointer[None]` for all the pointer types. By using a pointer to a primitive, we are adding a level of type safety, as the compiler will ensure that we don't pass a pointer to any other type as a parameter to `eglGetDisplay`. It is important to note that these primitives should __not be used anywhere except as a type parameter__ of `Pointer[]`, to avoid misuse. @@ -98,33 +61,13 @@ The above example would also work if we used `Pointer[None]` for all the pointer Like we mentioned above, Pony classes and structs correspond directly to pointers to the class or struct in C. This means that in most cases we won't need to use the `addressof` operator when passing struct types to C. For example, let's imagine we want to use the `writev` function from Pony on Linux: ```pony -// In C: ssize_t writev(int fd, const struct iovec *iov, int iovcnt) -use @writev[USize](fd: U32, iov: IOVec tag, iovcnt: I32) - -// In C: -// struct iovec { -// void *iov_base; /* Starting address */ -// size_t iov_len; /* Number of bytes to transfer */ -// }; -struct IOVec - var base: Pointer[U8] tag = Pointer[U8] - var len: USize = 0 - -let data = "Hello from Pony!" -var iov = IOVec -iov.base = data.cpointer() -iov.len = data.size() -@writev(1, iov, 1) // Will print "Hello from Pony!" +--8<-- "calling-c-writev-struct.pony" ``` As you saw, a `IOVec` instance in Pony is equivalent to `struct iovec*`. In some cases, like the above example, it can be cumbersome to define a `struct` type in Pony if you only want to use it in a single place. You can also use a pointer to a tuple type as a shorthand for a struct: let's rework the above example: ```pony -use @writev[USize](fd: U32, iov: Pointer[(Pointer[U8] tag, USize)] tag, iovcnt: I32) - -let data = "Hello from Pony!" -var iov = (data.cpointer(), data.size()) -@writev(1, addressof iov, 1) // Will print "Hello from Pony!" +--8<-- "calling-c-writev-tuple.pony" ``` In the example above, the type `Pointer[(Pointer[U8] tag, USize)] tag` is equivalent to the `IOVec` struct type we defined earlier. That is, _a struct type is equivalent to a pointer to a tuple type with the fields of the struct as elements, in the same order as the original struct type defined them_. @@ -136,19 +79,7 @@ In the example above, the type `Pointer[(Pointer[U8] tag, USize)] tag` is equiva A common pattern in C is to pass a struct pointer to a function, and that function will fill in various values in the struct. To do this in Pony, you make a `struct` and then use a `NullablePointer`, which denotes a possibly-null type: ```pony -use @ioctl[I32](fd: I32, req: U32, ...) - -struct Winsize - var height: U16 = 0 - var width: U16 = 0 - - new create() => None - -let size = Winsize - -@ioctl(0, 21523, NullablePointer[Winsize](size)) - -env.out.print(size.height.string()) +--8<-- "calling-c-ioctl-struct.pony" ``` A `NullablePointer` type can only be used with `structs`, and is only intended for output parameters (like in the example above) or for return types from C. You don't need to use a `NullablePointer` if you are only passing a `struct` as a regular input parameter. @@ -156,11 +87,7 @@ A `NullablePointer` type can only be used with `structs`, and is only intended f If you are using a C function that returns a struct, remember, that the C function needs to return a pointer to the struct. The following in Pony should be read as **returns a pointer to struct `Rect`**: ```pony -use @from_c[Rect]() - -struct Rect - var length: U16 - var width: U16 +--8<-- "calling-c-from-c-struct.pony" ``` As we saw earlier, you can also use a `Pointer[(U16, U16)]` as well. It is the equivalent to our `Rect`. @@ -184,50 +111,25 @@ void* list_pop(struct List* list); Following the advice from previous sections, we can write the following Pony declarations: ```pony -use @list_create[Pointer[_List]]() -use @list_free[None](list: Pointer[_List]) - -use @list_push[None](list: Pointer[_List], data: Pointer[None]) -use @list_pop[Pointer[None]](list: Pointer[_List]) - -primitive _List +--8<-- "calling-c-generic-list.pony" ``` We can use these declarations to create lists of different types, and insert elements into them: ```pony -struct Point - var x: U64 = 0 - var y: U64 = 0 - -let list_of_points = @list_create() -@list_push(list_of_points, NullablePointer[Point].create(Point)) - -let list_of_strings = @list_create() -@list_push(list_of_strings, "some data".cstring()) +--8<-- "calling-c-different-types-of-lists.pony" ``` We can also get elements out of the list, although we won't be able to do anything with them: ```pony -// Compiler error: couldn't find 'x' in 'Pointer' -let point_x = @list_pop(list_of_points) -point.x - -// Compiler error: wanted Pointer[U8 val] ref^, got Pointer[None val] ref -let head = String.from_cstring(@list_pop(list_of_strings)) +--8<-- "calling-c-access-list-entry-without-return-type.pony" ``` We can fix this problem by adding an explicit return type when calling `list_pop`: ```pony -// OK -let point = @list_pop[Point](list_of_points) -let x_coord = point.x - -// OK -let pointer = @list_pop[Pointer[U8]](list_of_strings) -let data = String.from_cstring(pointer) +--8<-- "calling-c-access-list-entry-with-explicit-return-type.pony" ``` Note that the declaration for `list_pop` is still needed: if we don't add an explicit return type when calling `list_pop`, the default type will be the return type of the declaration. @@ -239,11 +141,7 @@ When specifying a different return type for an FFI function, make sure that the Some C functions are variadic, that is, they can take a variable number of parameters. To interact with these functions, you should also specify that fact in the FFI signature: ```pony -use @printf[I32](fmt: Pointer[U8] tag, ...) -// ... -let run_ns: I64 = _current_t - _last_t -let rate: I64 = (_partial_count.i64() * 1_000_000_000) / run_ns -@printf("Elapsed: %lld,%lld\n".cstring(), run_ns, rate) +--8<-- "calling-c-variadic-c-functions.pony" ``` In the example above, the compiler will type-check the first argument to `printf`, but will not be able to check any other argument, since it lacks the necessary type information. It is __very__ important that you use `...` in the FFI signature if the corresponding C function is variadic: if you don't, the compiler might generate a program that is incorrect or crash on some platforms while appearing to work correctly on others. @@ -255,10 +153,7 @@ Some FFI functions might raise Pony errors. Functions in existing C libraries ar FFI calls to functions that __might__ raise an error __must__ mark it as such by adding a ? after its declaration. The FFI call site must mark it as well. For example: ```pony -use @pony_os_send[USize](event: AsioEventID, buffer: Pointer[U8] tag, size: USize) ? -// ... -// May raise an error -@pony_os_send(_event, data.cpointer(), data.size()) ? +--8<-- "calling-c-ffi-functions-raising-errors.pony" ``` If you're writing a C library that wants to raise a Pony error, you should do so using the `pony_error` function. Here's an example from the Pony runtime: @@ -296,11 +191,7 @@ Since type signature declarations are scoped to a single Pony package, separate Consider the following example: ```pony -// In library lib_a -use @memcmp[I32](dst: Pointer[None] tag, src: Pointer[None] tag, len: USize) - -// In library lib_b -use @memcmp[I32](dst: Pointer[None] tag, src: USize, len: U64) +--8<-- "calling-c-type-signature-compatibility.pony" ``` These two declarations have different types for the `src` and `len` parameters. In the case of `src`, the types are compatible since an integer can be cast as a pointer, and vice versa. For `len`, the types will not be compatible on 32 bit platforms, where `USize` is equivalent to `U32`. It is important to take the rules around casting into account when writing type declarations in libraries that will be used by others, as it will avoid any compatibility problems with other libraries. @@ -312,16 +203,7 @@ We mentioned in the previous section that FFI declarations are scoped to a singl Given the above fact, if you define any default methods (or behaviors) in an interface or trait, you will not be able to perform an FFI call from them. For example, the code below will fail to compile: ```pony -use @printf[I32](fmt: Pointer[None] tag, ...) - -trait Foo - fun apply() => - // Error: Can't call an FFI function in a default method or behavior - @printf("Hello from trait Foo\n".cstring()) - -actor Main is Foo - new create(env: Env) => - this.apply() +--8<-- "calling-c-default-method-in-trait.pony" ``` If the trait `Foo` above was part of the public API of a package, allowing its `apply` method to perform an FFI call would render `Foo` unusable for any external users, given that the declaration for `printf` would not be in scope. @@ -329,20 +211,7 @@ If the trait `Foo` above was part of the public API of a package, allowing its ` Fortunately, avoiding this limitation is relatively painless. Whenever you need to call an FFI function from a default method implementation, consider moving said function to a separate type: ```pony -use @printf[I32](fmt: Pointer[None] tag, ...) - -trait Foo - fun apply() => - // OK - Printf("Hello from trait Foo\n") - -primitive Printf - fun apply(str: String) => - @printf(str.cstring()) - -actor Main is Foo - new create(env: Env) => - this.apply() +--8<-- "calling-c-default-method-in-primitive.pony" ``` By making the change above, we avoid exposing the call to `printf` to any consumers of our trait, thus making it usable by external users. diff --git a/docs/c-ffi/linking-c.md b/docs/c-ffi/linking-c.md index 536a413e..6b8a1ce3 100644 --- a/docs/c-ffi/linking-c.md +++ b/docs/c-ffi/linking-c.md @@ -7,7 +7,7 @@ If Pony code calls FFI functions, then those functions, or rather the libraries To link an external library to Pony code another variant of the use command is used. The `lib` specifier is used to tell the compiler you want to link to a library. For example: ```pony -use "lib:foo" +--8<-- "linking-c-use-lib-foo.pony" ``` As with other `use` commands a condition may be specified. This is particularly useful when the library has slightly different names on different platforms. @@ -15,22 +15,7 @@ As with other `use` commands a condition may be specified. This is particularly Here's a real example from the standard library: ```pony -use "path:/usr/local/opt/libressl/lib" if osx -use "lib:ssl" if not windows -use "lib:crypto" if not windows -use "lib:libssl-32" if windows -use "lib:libcrypto-32" if windows - -use @SSL_load_error_strings[None]() -use @SSL_library_init[I32]() - -primitive _SSLInit - """ - This initialises SSL when the program begins. - """ - fun _init() => - @SSL_load_error_strings() - @SSL_library_init() +--8<-- "linking-c-use-with-condition.pony" ``` On Windows, we use the libraries `libssl-32` and `libcrypto-32` and on other platforms we use `ssl` and `crypto`. These contain the FFI functions `SSL_library_init` and `SSL_load_error_strings` (amongst others). diff --git a/docs/expressions/arithmetic.md b/docs/expressions/arithmetic.md index bb86ec7b..d666abb6 100644 --- a/docs/expressions/arithmetic.md +++ b/docs/expressions/arithmetic.md @@ -15,11 +15,7 @@ Pony provides different ways of doing arithmetic to give programmers the freedom Doing arithmetic on integer types in Pony with the well known operators like `+`, `-`, `*`, `/` etc. tries to balance the needs for performance and correctness. All default arithmetic operations do not expose any undefined behaviour or error conditions. That means it handles both the cases for overflow/underflow and division by zero. Overflow/Underflow are handled with proper wrap around semantics, using one's complement on signed integers. In that respect we get behaviour like: ```pony -// unsigned wrap-around on overflow -U32.max_value() + 1 == 0 - -// signed wrap-around on overflow/underflow -I32.min_value() - 1 == I32.max_value() +--8<-- "arithmetic-default-integer-arithmetic.pony" ``` Division by zero is a special case, which affects the division `/` and remainder `%` operators. In Mathematics, division by zero is undefined. In order to avoid either defining division as partial, throwing an error on division by zero or introducing undefined behaviour for that case, the _normal_ division is defined to be `0` when the divisor is `0`. This might lead to silent errors, when used without care. Choose [Partial and checked Arithmetic](#partial-and-checked-arithmetic) to detect division by zero. @@ -69,21 +65,13 @@ Here is a list with all unsafe operations defined on Integers: Converting between integer types in Pony needs to happen explicitly. Each numeric type can be converted explicitly into every other type. ```pony -// converting an I32 to a 32 bit floating point -I32(12).f32() +--8<-- "arithmetic-explicit-numeric-conversion.pony" ``` For each conversion operation there exists an unsafe counterpart, that is much faster when converting from and to floating point numbers. All these unsafe conversion between numeric types are undefined if the target type is smaller than the source type, e.g. if we convert from `I64` to `F32`. ```pony -// converting an I32 to a 32 bit floating point, the unsafe way -I32(12).f32_unsafe() - -// an example for an undefined unsafe conversion -I64.max_value().f32_unsafe() - -// an example for an undefined unsafe conversion, that is actually safe -I64(1).u8_unsafe() +--8<-- "arithmetic-unsafe-conversion.pony" ``` Here is a full list of all available conversions for numeric types: @@ -116,23 +104,7 @@ Here is a full list of all available conversions for numeric types: If overflow or division by zero are cases that need to be avoided and performance is no critical priority, partial or checked arithmetic offer great safety during runtime. Partial arithmetic operators error on overflow/underflow and division by zero. Checked arithmetic methods return a tuple of the result of the operation and a `Boolean` indicating overflow or other exceptional behaviour. ```pony -// partial arithmetic -let result = - try - USize.max_value() +? env.args.size() - else - env.out.print("overflow detected") - end - -// checked arithmetic -let result = - match USize.max_value().addc(env.args.size()) - | (let result: USize, false) => - // use result - ... - | (_, true) => - env.out.print("overflow detected") - end +--8<-- "arithmetic-partial-and-check-arithmetic.pony" ``` Partial as well as checked arithmetic comes with the burden of handling exceptions on every case and incurs some performance overhead, be warned. diff --git a/docs/expressions/as.md b/docs/expressions/as.md index c0180bb2..08838c91 100644 --- a/docs/expressions/as.md +++ b/docs/expressions/as.md @@ -9,18 +9,7 @@ In Pony, each object is an instance of a single concrete type, which is the most `as` (like `match`) allows a program to check the runtime type of an abstract-typed value to see whether or not the object matches a given type which is more specific. If it doesn't match the more specific type, then a runtime `error` is raised. For example: ```pony - class Cat - fun pet() => - ... - - type Animal is (Cat | Fish | Snake) - - fun pet(animal: Animal) => - try - // raises error if not a Cat - let cat: Cat = animal as Cat - cat.pet() - end +--8<-- "as-operator-more-specific-type.pony" ``` In the above example, within `pet` our current view of `animal` is via the type union `Animal`. To treat `animal` as a cat, we need to do a runtime check that the concrete type of the object instance is `Cat`. If it is, then we can pet it. This example is a little contrived, but hopefully elucidates how `as` can be used to take a type that is a union and get a specific concrete type from it. @@ -30,92 +19,37 @@ Note that the type requested as the `as` argument must exist as a type of the ob In addition to using `as` with a union of disjoint types, we can also express an intersected type of the object, meaning the object has a type that the alias we have for the object is not directly related to the type we want to express. For example: ```pony - trait Alive - - trait Well - - class Person is (Alive & Well) - - class LifeSigns - fun is_all_good(alive: Alive)? => - // if the instance 'alive' is also of type 'Well' (such as a Person instance). raises error if not possible - let well: Well = alive as Well +--8<-- "as-operator-unrelated-type.pony" ``` `as` can also be used to get a more specific type of an object from an alias to it that is an interface or a trait. Let's say, for example, that you have a library for doing things with furry, rodent-like creatures. It provides a `Critter` interface which programmers can then use to create specific types of critters. ```pony -interface Critter - fun wash(): String +--8<-- "as-operator-more-specific-interface.pony:1:2" ``` The programmer uses this library to create a `Wombat` and a `Capybara` class. But the `Capybara` class provides a new method, `swim()`, that is not part of the `Critter` class. The programmer wants to store all of the critters in an array, in order to carry out actions on groups of critters. Now assume that when capybaras finish washing they want to go for a swim. The programmer can accomplish that by using `as` to attempt to use each `Critter` object in the `Array[Critter]` as a `Capybara`. If this fails because the `Critter` is not a `Capybara`, then an error is raised; the program can swallow this error and go on to the next item. ```pony -interface Critter - fun wash(): String - -class Wombat is Critter - fun wash(): String => "I'm a clean wombat!" - -class Capybara is Critter - fun wash(): String => "I feel squeaky clean!" - fun swim(): String => "I'm swimming like a fish!" - -actor Main - new create(env: Env) => - let critters = Array[Critter].>push(Wombat).>push(Capybara) - for critter in critters.values() do - env.out.print(critter.wash()) - try - env.out.print((critter as Capybara).swim()) - end - end +--8<-- "as-operator-more-specific-interface.pony" ``` You can do the same with interfaces as well. In the example below, we have an Array of `Any` which is an interface where we want to try wash any entries that conform to the `Critter` interface. ```pony -actor Main - new create(env: Env) => - let anys = Array[Any ref].>push(Wombat).>push(Capybara) - for any in anys.values() do - try - env.out.print((any as Critter).wash()) - end - end +--8<-- "as-operator-more-specific-interface-with-reference-capability.pony" ``` Note, All the `as` examples above could be written using a `match` statement where a failure to match results in `error`. For example, our last example written to use `match` would be: ```pony -actor Main - new create(env: Env) => - let anys = Array[Any ref].>push(Wombat).>push(Capybara) - for any in anys.values() do - try - match any - | let critter: Critter => - env.out.print(critter.wash()) - else - error - end - end - end +--8<-- "as-operator-match-statement-comparison.pony" ``` Thinking of the `as` keyword as "an attempt to match that will error if not matched" is a good mental model to have. If you don't care about handling the "not matched" case that causes an error when using `as`, you can rewrite an `as` to use match without an error like: ```pony -actor Main - new create(env: Env) => - let anys = Array[Any ref].>push(Wombat).>push(Capybara) - for any in anys.values() do - match any - | let critter: Critter => - env.out.print(critter.wash()) - end - end +--8<-- "as-operator-match-statement-without-try.pony" ``` You can learn more about matching on type in the [captures section of the match documentation](/expressions/match.md#captures). @@ -127,15 +61,7 @@ The `as` operator can also be used to tell the compiler what type to use for the For example, in the case of the following program, the method `foo` can take either an `Array[U32] ref` or an `Array[U64] ref` as an argument. If a literal array is passed as an argument to the method and no type is specified then the compiler cannot deduce the correct one because there are two equally valid ones. ```pony -actor Main - fun foo(xs: (Array[U32] ref | Array[U64] ref)): Bool => - // do something boring here - true - - new create(env: Env) => - foo([as U32: 1; 2; 3]) - // the compiler would complain about this: - // foo([1; 2; 3]) +--8<-- "as-operator-array-literal.pony" ``` The requested type must be a valid type for the items in the array. Since these types are checked at compile time they are guaranteed to work, so there is no need for the programmer to handle an error condition. diff --git a/docs/expressions/control-structures.md b/docs/expressions/control-structures.md index d5bc2f84..b79e4116 100644 --- a/docs/expressions/control-structures.md +++ b/docs/expressions/control-structures.md @@ -7,9 +7,7 @@ To do real work in a program you have to be able to make decisions, iterate thro The simplest control structure is the good old `if`. It allows you to perform some action only when a condition is true. In Pony it looks like this: ```pony -if a > b then - env.out.print("a is bigger") -end +--8<-- "control-structures-conditionals-if.pony" ``` __Can I use integers and pointers for the condition like I can in C?__ No. In Pony `if` conditions must have type `Bool`, i.e. they are always true or false. If you want to test whether a number `a` is not 0, then you need to explicitly say `a != 0`. This restriction removes a whole category of potential bugs from Pony programs. @@ -17,37 +15,19 @@ __Can I use integers and pointers for the condition like I can in C?__ No. In Po If you want some alternative code for when the condition fails just add an `else`: ```pony -if a > b then - env.out.print("a is bigger") -else - env.out.print("a is not bigger") -end +--8<-- "control-structures-conditionals-if-else.pony" ``` Often you want to test more than one condition in one go, giving you more than two possible outcomes. You can nest `if` statements, but this quickly gets ugly: ```pony -if a == b then - env.out.print("they are the same") -else - if a > b then - env.out.print("a is bigger") - else - env.out.print("b bigger") - end -end +--8<-- "control-structures-conditionals-nested-if-else.pony" ``` As an alternative Pony provides the `elseif` keyword that combines an `else` and an `if`. This works the same as saying `else if` in other languages and you can have as many `elseif`s as you like for each `if`. ```pony -if a == b then - env.out.print("they are the same") -elseif a > b then - env.out.print("a is bigger") -else - env.out.print("b bigger") -end +--8<-- "control-structures-conditionals-if-elseif-else.pony" ``` __Why can't I just say "else if" like I do in C? Why the extra keyword?__ The relationship between `if` and `else` in C, and other similar languages, is ambiguous. For example: @@ -72,7 +52,7 @@ In Pony control flow statements like this are all expressions that hand back a v This means you can use `if` directly in a calculation: ```pony -x = 1 + if lots then 100 else 2 end +--8<-- "control-structures-conditionals-expressions.pony" ``` This will give __x__ a value of either 3 or 101, depending on the variable __lots__. @@ -80,51 +60,25 @@ This will give __x__ a value of either 3 or 101, depending on the variable __lot If the `then` and `else` branches of an `if` produce different types then the `if` produces a __union__ of the two. ```pony -var x: (String | Bool) = - if friendly then - "Hello" - else - false - end +--8<-- "control-structures-conditionals-expression-union-type.pony" ``` __But what if my if doesn't have an else?__ Any `else` branch that doesn't exist gives an implicit `None`. ```pony -var x: (String | None) = - if friendly then - "Hello" - end +--8<-- "control-structures-conditionals-expression-implicit-none.pony" ``` The same rules that apply to the value of an `if` expression applies to loops as well. Let's take a look at what a loop value would look like: ```pony -actor Main - new create(env: Env) => - var x: (String | None) = - for name in ["Bob"; "Fred"; "Sarah"].values() do - name - end - match x - | let s: String => env.out.print("x is " + s) - | None => env.out.print("x is None") - end +--8<-- "control-structures-loop-expression.pony" ``` This will give __x__ the value "Sarah" as it is the last name in our list. If our loop has 0 iterations, then the value of its `else` block will be the value of __x__. Or if there is no `else` block, the value will be `None`. ```pony -actor Main - new create(env: Env) => - var x: (String | None) = - for name in Array[String].values() do - name - end - match x - | let s: String => env.out.print("x is " + s) - | None => env.out.print("x is None") - end +--8<-- "control-structures-loop-expression-none.pony" ``` Here __x__ would be `None`. @@ -132,15 +86,7 @@ Here __x__ would be `None`. You can also avoid needing `None` at all by providing a __default value__ for when the loop has __0 iterations__ by providing an `else` block. ```pony -actor Main - new create(env: Env) => - var x: String = - for name in Array[String].values() do - name - else - "no names!" - end - env.out.print("x is " + x) +--8<-- "control-structures-loop-expression-else.pony" ``` And finally, here the value of __x__ is "no names!" @@ -156,12 +102,7 @@ Pony `while` loops are very similar to those in other languages. A condition exp Here's an example that prints out the numbers 1 to 10: ```pony -var count: U32 = 1 - -while count <= 10 do - env.out.print(count.string()) - count = count + 1 -end +--8<-- "control-structures-loops-while.pony" ``` Just like `if` expressions, `while` is also an expression. The value returned is just the value of the expression inside the loop the last time we go round it. For this example that will be the value given by `count = count + 1` when count is incremented to 11. Since Pony assignments hand back the _old_ value our `while` loop will return 10. @@ -179,16 +120,7 @@ Sometimes you want to stop part-way through a loop and give up altogether. Pony Let's have an example. Suppose you want to go through a list of names, looking for either "Jack" or "Jill". If neither of those appear, you'll just take the last name you're given, and if you're not given any names at all, you'll use "Herbert". ```pony -var name = - while moreNames() do - var name' = getName() - if name' == "Jack" or name' == "Jill" then - break name' - end - name' - else - "Herbert" - end +--8<-- "control-structures-loops-while-break-else.pony" ``` So first we ask if there are any more names to get. If there are then we get a name and see if it's "Jack" or "Jill". If it is we're done and we break out of the loop, handing back the name we've found. If not we try again. @@ -218,9 +150,7 @@ The Pony `for` loop iterates over a collection of items using an iterator. On ea For example, to print out all the strings in an array: ```pony -for name in ["Bob"; "Fred"; "Sarah"].values() do - env.out.print(name) -end +--8<-- "control-structures-loops-for.pony" ``` Note the call to `values()` on the array — this is because the loop needs an iterator, not an array. @@ -228,8 +158,7 @@ Note the call to `values()` on the array — this is because the loop needs an i The iterator does not have to be of any particular type, but needs to provide the following methods: ```pony - fun has_next(): Bool - fun next(): T? +--8<-- "control-structures-iterator-methods.pony" ``` where T is the type of the objects in the collection. You don't need to worry about this unless you're writing your own iterators. To use existing collections, such as those provided in the standard library, you can just use `for` and it will all work. If you do write your own iterators, note that we use structural typing, so your iterator doesn't need to declare that it provides any particular type. @@ -237,11 +166,7 @@ where T is the type of the objects in the collection. You don't need to worry ab 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()? - env.out.print(name) -end +--8<-- "control-structures-loops-for-while-comparison.pony" ``` Note that the variable __name__ is declared _let_, so you cannot assign to the control variable within the loop. @@ -262,13 +187,7 @@ The differences between `while` and `repeat` in Pony are: Suppose we're trying to create something and we want to keep trying until it's good enough: ```pony -actor Main - new create(env: Env) => - var counter = U64(1) - repeat - env.out.print("hello!") - counter = counter + 1 - until counter > 7 end +--8<-- "control-structures-loops-repeat.pony" ``` Just like `while` loops, the value given by a `repeat` loop is the value of the expression within the loop on the last iteration, and `break` and `continue` can be used. diff --git a/docs/expressions/equality.md b/docs/expressions/equality.md index 92130b55..ce09a543 100644 --- a/docs/expressions/equality.md +++ b/docs/expressions/equality.md @@ -7,22 +7,7 @@ Pony features two forms of equality: by structure and by identity. Identity equality checks in Pony are done via the `is` keyword. `is` verifies that the two items are the same. ```pony -if None is None then - // TRUE! - // There is only 1 None so the identity is the same -end - -let a = Foo("hi") -let b = Foo("hi") - -if a is b then - // NOPE. THIS IS FALSE -end - -let c = a -if a is c then - // YUP! TRUE! -end +--8<-- "equality-identity-equality.pony" ``` ## Structural equality @@ -32,43 +17,13 @@ Structural equality checking in Pony is done via the infix operator `==`. It ver You can define how structural equality is checked on your object by implementing `fun eq(that: box->Foo): Bool`. Remember, since `==` is an infix operator, `eq` must be defined on the left operand, and the right operand must be of type `Foo`. ```pony -class Foo - let _a: String - - new create(a: String) => - _a = a - - fun eq(that: box->Foo): Bool => - this._a == that._a - -actor Main - new create(e: Env) => - let a = Foo("hi") - let b = Foo("bye") - let c = Foo("hi") - - if a == b then - // won't print - e.out.print("1") - end - - if a == c then - // will print - e.out.print("2") - end - - if a is c then - // won't print - e.out.print("3") - end +--8<-- "equality-structural-equality.pony" ``` If you don't define your own `eq`, you will inherit the default implementation that defines equal by value as being the same as by identity. ```pony -interface Equatable[A: Equatable[A] #read] - fun eq(that: box->A): Bool => this is that - fun ne(that: box->A): Bool => not eq(that) +--8<-- "equality-equatable-default-implementation.pony" ``` ## Primitives and equality @@ -81,11 +36,5 @@ As you might remember from [Chapter 2](/types/primitives.md), primitives are the This means, that every primitive of a given type, is always structurally equal and equal based on identity. So, for example, None is always None. ```pony -if None is None then - // this is always true -end - -if None == None then - // this is also always true -end +--8<-- "equality-primitives.pony" ``` diff --git a/docs/expressions/errors.md b/docs/expressions/errors.md index 03ed8121..1676deea 100644 --- a/docs/expressions/errors.md +++ b/docs/expressions/errors.md @@ -9,13 +9,7 @@ An error is raised with the command `error`. At any point, the code may decide t Error handlers are declared using the `try`-`else` syntax. ```pony -try - callA() - if not callB() then error end - callC() -else - callD() -end +--8<-- "errors-try-else.pony" ``` 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. @@ -29,9 +23,7 @@ __Do I have to provide an error handler?__ No. The `try` block will handle any e If you want to do something that might raise an error, but you don't care if it does you can just put it in a `try` block without an `else`. ```pony -try - // Do something that may raise an error -end +--8<-- "errors-try-without-else.pony" ``` __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. @@ -45,13 +37,7 @@ Pony does not require that all errors are handled immediately as in our previous 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. ```pony -fun factorial(x: I32): I32 ? => - if x < 0 then error end - if x == 0 then - 1 - else - x * factorial(x - 1)? - end +--8<-- "errors-partial-functions.pony" ``` 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. @@ -71,15 +57,7 @@ Behaviours are also executed asynchronously and so cannot be partial for the sam In addition to an `else` error handler, a `try` command can have a `then` block. This is executed after the rest of the `try`, whether or not an error is raised or handled. Expanding our example from earlier: ```pony -try - callA() - if not callB() then error end - callC() -else - callD() -then - callE() -end +--8<-- "errors-try-then.pony" ``` The `callE()` will always be executed. If `callB()` returns true then the sequence executed is `callA()`, `callB()`, `callC()`, `callE()`. If `callB()` returns false then the sequence executed is `callA()`, `callB()`, `callD()`, `callE()`. @@ -93,27 +71,19 @@ __Will my then block really always be executed, even if I return inside the try? A `with` expression can be used to ensure disposal of an object when it is no longer needed. A common case is a database connection which needs to be closed after use to avoid resource leaks on the server. For example: ```pony -with obj = SomeObjectThatNeedsDisposing() do - // use obj -end +--8<-- "errors-with-blocks.pony" ``` `obj.dispose()` will be called whether the code inside the `with` block completes successfully or raises an error. To take part in a `with` expression, the object that needs resource clean-up must, therefore, provide a `dispose()` method: ```pony -class SomeObjectThatNeedsDisposing - // constructor, other functions - - fun dispose() => - // release resources +--8<-- "errors-dispose.pony" ``` Multiple objects can be set up for disposal: ```pony -with obj = SomeObjectThatNeedsDisposing(), other = SomeOtherDisposableObject() do - // use obj and other -end +--8<-- "errors-dispose-multiple.pony" ``` The value of a `with` expression is the value of the last expression in the block. diff --git a/docs/expressions/literals.md b/docs/expressions/literals.md index 8694fc93..f220d34d 100644 --- a/docs/expressions/literals.md +++ b/docs/expressions/literals.md @@ -31,24 +31,19 @@ It is possible to help the compiler determine the concrete type of the literal u * `F32`, `F64` ```pony -let my_explicit_unsigned: U32 = 42_000 -let my_constructor_unsigned = U8(1) -let my_constructor_float = F64(1.234) +--8<-- "literals-numeric-typing.pony" ``` Integer literals can be given as decimal, hexadecimal or binary: ```pony -let my_decimal_int: I32 = 1024 -let my_hexadecimal_int: I32 = 0x400 -let my_binary_int: I32 = 0b10000000000 +--8<-- "literals-number-types.pony" ``` Floating Point literals are expressed as standard floating point or scientific notation: ```pony -let my_double_precision_float: F64 = 0.009999999776482582092285156250 -let my_scientific_float: F32 = 42.12e-4 +--8<-- "literals-floats.pony" ``` ## Character Literals @@ -58,9 +53,7 @@ Character literals are enclosed with single quotes (`'`). Character literals, unlike String literals, encode to a single numeric value. Usually this is a single byte, a `U8`. But they can be coerced to any integer type: ```pony -let big_a: U8 = 'A' // 65 -let hex_escaped_big_a: U8 = '\x41' // 65 -let newline: U32 = '\n' // 10 +--8<-- "literals-character-literals.pony" ``` The following escape sequences are supported: @@ -73,7 +66,7 @@ The following escape sequences are supported: It is possible to have character literals that contain multiple characters. The resulting integer value is constructed byte by byte with each character representing a single byte in the resulting integer, the last character being the least significant byte: ```pony -let multiByte: U64 = 'ABCD' // 0x41424344 +--8<-- "literals-multibyte-character-literals.pony" ``` ## String Literals @@ -88,31 +81,13 @@ String literals are enclosed with double quotes `"` or triple-quoted `"""`. They Each escape sequence encodes a full character, not byte. ```pony -use "format" - -actor Main - new create(env: Env) => - - let pony = "🐎" - let pony_hex_escaped = "p\xF6n\xFF" - let pony_unicode_escape = "\U01F40E" - - env.out.print(pony + " " + pony_hex_escaped + " " + pony_unicode_escape) - for b in pony.values() do - env.out.print(Format.int[U8](b, FormatHex)) - end - +--8<-- "literals-string-literals.pony" ``` All string literals support multi-line strings: ```pony - -let stacked_ponies = " -🐎 -🐎 -🐎 -" +--8<-- "literals-multi-line-string-literals.pony" ``` ### String Literals and Encodings @@ -122,7 +97,7 @@ String Literals contain the bytes that were read from their source code file. Th Consider the following example: ```pony -let u_umlaut = "ü" +--8<-- "literals-string-literals-encoding.pony" ``` If the file containing this code is encoded as `UTF-8` the byte-value of `u_umlaut` will be: `\xc3\xbc`. If the file is encoded with *ISO-8559-1* (Latin-1) its value will be `\xfc`. @@ -132,21 +107,7 @@ If the file containing this code is encoded as `UTF-8` the byte-value of `u_umla For embedding multi-line text in string literals, there are triple quoted strings. ```pony -let triple_quoted_string_docs = - """ - Triple quoted strings are the way to go for long multi-line text. - They are extensively used as docstrings which are turned into api documentation. - - They get some special treatment, in order to keep Pony code readable: - - * The string literal starts on the line after the opening triple quote. - * Common indentation is removed from the string literal - so it can be conveniently aligned with the enclosing indentation - e.g. each line of this literal will get its first two whitespaces removed - * Whitespace after the opening and before the closing triple quote will be - removed as well. The first line will be completely removed if it only - contains whitespace. e.g. this strings first character is `T` not `\n`. - """ +--8<-- "literals-triple-quoted-string-literals.pony" ``` ### String Literal Instances @@ -154,11 +115,7 @@ let triple_quoted_string_docs = When a single string literal is used several times in your Pony program, all of them will be converted to a single common instance. This means they will always be equal based on identity. ```pony -let pony = "🐎" -let another_pony = "🐎" -if pony is another_pony then - // True, therefore this line will run. -end +--8<-- "literals-string-literals-instances.pony" ``` ## Array Literals @@ -166,11 +123,7 @@ end Array literals are enclosed by square brackets. Array literal elements can be any kind of expressions. They are separated by semicolon or newline: ```pony -let my_literal_array = - [ - "first"; "second" - "third one on a new line" - ] +--8<-- "literals-array-literals.pony" ``` ### Type inference @@ -178,12 +131,7 @@ let my_literal_array = If the type of the array is not specified, the resulting type of the literal array expression is `Array[T] ref` where `T` (the type of the elements) is inferred as the union of all the element types: ```pony -let my_heterogenous_array = - [ - U64(42) - "42" - U64.min_value() - ] +--8<-- "literals-type-inference-union.pony" ``` In the above example the resulting array type will be `Array[(U64|String)] ref` because the array contains `String` and `U64` elements. @@ -191,11 +139,7 @@ In the above example the resulting array type will be `Array[(U64|String)] ref` If the variable or call argument the array literal is assigned to has a type, the literal is coerced to that type: ```pony -let my_stringable_array: Array[Stringable] ref = - [ - U64(0xA) - "0xA" - ] +--8<-- "literals-type-inference-coercion.pony" ``` Here `my_stringable_array` is coerced to `Array[Stringable] ref`. This works because `Stringable` is a trait that both `String` and `U64` implement. @@ -203,11 +147,7 @@ Here `my_stringable_array` is coerced to `Array[Stringable] ref`. This works bec It is also possible to return an array with a different [Reference Capability](/reference-capabilities/index.md) than `ref` just by specifying it on the type: ```pony -let my_immutable_array: Array[Stringable] val = - [ - U64(0xBEEF) - "0xBEEF" - ] +--8<-- "literals-type-inference-reference-capabilities.pony" ``` This way array literals can be used for creating arrays of any [Reference Capability](/reference-capabilities/index.md). @@ -217,12 +157,7 @@ This way array literals can be used for creating arrays of any [Reference Capabi It is also possible to give the literal a hint on what kind of type it should coerce the array elements to using an `as` Expression. The expression with the desired array element type needs to be added right after the opening square bracket, delimited by a colon: ```pony -let my_as_array = - [ as Stringable: - U64(0xFFEF) - "0xFFEF" - U64(1 + 1) - ] +--8<-- "literals-as-expression.pony" ``` This array literal is coerced to be an `Array[Stringable] ref` according to the `as` expression. diff --git a/docs/expressions/match.md b/docs/expressions/match.md index c5f2e780..fb9c188a 100644 --- a/docs/expressions/match.md +++ b/docs/expressions/match.md @@ -7,13 +7,7 @@ If we want to compare an expression to a value then we use an `if`. But if we wa Here's a simple example of a match expression that produces a string. ```pony -match x -| 2 => "int" -| 2.0 => "float" -| "2" => "string" -else - "something else" -end +--8<-- "match-expression.pony" ``` If you're used to functional languages this should be very familiar. @@ -43,15 +37,7 @@ The compiler recognizes a match as exhaustive when the union of the types for al The simplest match expression just matches on value. ```pony -fun f(x: U32): String => - match x - | 1 => "one" - | 2 => "two" - | 3 => "three" - | 5 => "not four" - else - "something else" - end +--8<-- "match-values.pony" ``` For value matching the pattern is simply the value we want to match to, just like a C switch statement. The case with the same value as the operand wins and we use its expression. @@ -59,28 +45,7 @@ For value matching the pattern is simply the value we want to match to, just lik The compiler calls the `eq()` function on the operand, passing the pattern as the argument. This means that you can use your own types as match operands and patterns, as long as you define an `eq()` function. ```pony -class Foo - var _x: U32 - - new create(x: U32) => - _x = x - - fun eq(that: Foo): Bool => - _x == that._x - -actor Main - new create(env: Env) => - None - - fun f(x: Foo): String => - match x - | Foo(1) => "one" - | Foo(2) => "two" - | Foo(3) => "three" - | Foo(5) => "not four" - else - "something else" - end +--8<-- "match-custom-eq-operand.pony" ``` ## Matching on type and value @@ -88,15 +53,7 @@ actor Main Matching on value is fine if the match operand and case patterns have all the same type. However, match can cope with multiple different types. Each case pattern is first checked to see if it is the same type as the runtime type of the operand. Only then will the values be compared. ```pony -fun f(x: (U32 | String | None)): String => - match x - | None => "none" - | 2 => "two" - | 3 => "three" - | "5" => "not four" - else - "something else" - end +--8<-- "match-type-and-value.pony" ``` In many languages using runtime type information is very expensive and so it is generally avoided whenever possible. @@ -108,20 +65,7 @@ __When are case patterns for value matching evaluated?__ Each case pattern expre At first sight it is easy to confuse a value matching pattern for a type check. Consider the following example: ```pony -class Foo is Equatable[Foo] - -actor Main - - fun f(x: (Foo | None)): String => - match x - | Foo => "foo" - | None => "bar" - else - "" - end - - new create(env: Env) => - f(Foo) +--8<-- "match-value-pattern-matching-vs-type-check.pony" ``` Both case patterns actually __do not__ check for the match operand `x` being an instance of `Foo` or `None`, but check for equality with the instance created by evaluating the case pattern (each time). `None` is a primitive and thus there is only one instance at all, in which case this value pattern kind of does the expected thing, but not quite. If `None` had a custom `eq` function that would not use [identity equality](/expressions/equality.md#identity-equality), this could lead to surprising results. @@ -135,14 +79,7 @@ Sometimes you want to be able to match the type, for any value of that type. For Captures look just like variable declarations within the pattern. Like normal variables, they can be declared as var or let. If you're not going to reassign them within the case expression it is good practice to use let. ```pony -fun f(x: (U32 | String | None)): String => - match x - | None => "none" - | 2 => "two" - | 3 => "three" - | let u: U32 => "other integer" - | let s: String => s - end +--8<-- "match-captures.pony" ``` __Can I omit the type from a capture, like I can from a local variable?__ Unfortunately no. Since we match on type and value the compiler has to know what type the pattern is, so it can't be inferred. @@ -152,45 +89,13 @@ __Can I omit the type from a capture, like I can from a local variable?__ Unfort In union types, when we pattern match on individual classes or traits, we also implicitly pattern match on the corresponding capabilities. In the example provided below, if `_x` has static type `(A iso | B ref | None)` and dynamically matches `A`, then we also know that it must be an `A iso`. ```pony -class A - fun ref sendable() => - None - -class B - fun ref update() => - None - -actor Main - var _x: (A iso | B ref | None) - - new create(env: Env) => - _x = None - - be f(a': A iso) => - match (_x = None) // type of this expression: (A iso^ | B ref | None) - | let a: A iso => f(consume a) - | let b: B ref => b.update() - end +--8<-- "match-capabilities.pony" ``` Note that using a match expression to differentiate solely based on capabilities at runtime is not possible, that is: ```pony -class A - fun ref sendable() => - None - -actor Main - var _x: (A iso | A ref | None) - - new create(env: Env) => - _x = None - - be f() => - match (_x = None) - | let a1: A iso => None - | let a2: A ref => None - end +--8<-- "match-capabilities-only.pony" ``` does not type check. @@ -200,29 +105,13 @@ does not type check. If you want to match on more than one operand at once then you can simply use a tuple. Cases will only match if __all__ the tuple elements match. ```pony -fun f(x: (String | None), y: U32): String => - match (x, y) - | (None, let u: U32) => "none" - | (let s: String, 2) => s + " two" - | (let s: String, 3) => s + " three" - | (let s: String, let u: U32) => s + " other integer" - else - "something else" - end +--8<-- "match-tuples.pony" ``` __Do I have to specify all the elements in a tuple?__ No, you don't. Any tuple elements in a pattern can be marked as "don't care" by using an underscore ('_'). The first and fourth cases in our example don't actually care about the U32 element, so we can ignore it. ```pony -fun f(x: (String | None), y: U32): String => - match (x, y) - | (None, _) => "none" - | (let s: String, 2) => s + " two" - | (let s: String, 3) => s + " three" - | (let s: String, _) => s + " other integer" - else - "something else" - end +--8<-- "match-tuples-ignore-elements.pony" ``` ## Guards @@ -234,14 +123,5 @@ Guards are introduced with the `if` keyword (_was `where` until 0.2.1_). A guard expression may use any captured variables from that case, which allows for handling ranges and complex functions. ```pony -fun f(x: (String | None), y: U32): String => - match (x, y) - | (None, _) => "none" - | (let s: String, 2) => s + " two" - | (let s: String, 3) => s + " three" - | (let s: String, let u: U32) if u > 14 => s + " other big integer" - | (let s: String, _) => s + " other small integer" - else - "something else" - end +--8<-- "match-guards.pony" ``` diff --git a/docs/expressions/methods.md b/docs/expressions/methods.md index c52a9a10..2af9c590 100644 --- a/docs/expressions/methods.md +++ b/docs/expressions/methods.md @@ -11,12 +11,7 @@ __Can I have some code outside of any methods like I do in Python?__ No. All Pon Pony functions are quite like functions (or methods) in other languages. They can have 0 or more parameters and 0 or 1 return values. If the return type is omitted then the function will have a return value of `None`. ```pony -class C - fun add(x: U32, y: U32): U32 => - x + y - - fun nop() => - add(1, 2) // Pointless, we ignore the result +--8<-- "methods-functions.pony" ``` The function parameters (if any) are specified in parentheses after the function name. Functions that don't take any parameters still need to have the parentheses. @@ -36,14 +31,7 @@ __Can I overload functions by argument type?__ No, you cannot have multiple meth Pony constructors are used to initialise newly created objects, as in many languages. However, unlike many languages, Pony constructors are named so you can have as many as you like, taking whatever parameters you like. By convention, the main constructor of each type (if there is such a thing for any given type) is called `create`. ```pony -class Foo - var _x: U32 - - new create() => - _x = 0 - - new from_int(x: U32) => - _x = x +--8<-- "methods-constructors.pony" ``` The purpose of a constructor is to set up the internal state of the object being created. To ensure this is done constructors must initialise all the fields in the object being constructed. @@ -55,82 +43,31 @@ __Can I exit a constructor early?__ Yes. Just then use the `return` command with As in many other languages, methods in Pony are called by providing the arguments within parentheses after the method name. The parentheses are required even if there are no arguments being passed to the method. ```pony -class Foo - fun hello(name: String): String => - "hello " + name - - fun f() => - let a = hello("Fred") +--8<-- "methods-functions-calling.pony" ``` Constructors are usually called "on" a type, by specifying the type that is to be created. To do this just specify the type, followed by a dot, followed by the name of the constructor you want to call. ```pony -class Foo - var _x: U32 - - new create() => - _x = 0 - - new from_int(x: U32) => - _x = x - -class Bar - fun f() => - var a: Foo = Foo.create() - var b: Foo = Foo.from_int(3) +--8<-- "methods-constructors-calling.pony" ``` Functions are always called on an object. Again just specify the object, followed by a dot, followed by the name of the function to call. If the object to call on is omitted then the current object is used (i.e. `this`). ```pony -class Foo - var _x: U32 - - new create() => - _x = 0 - - new from_int(x: U32) => - _x = x - - fun get(): U32 => - _x - -class Bar - fun f() => - var a: Foo = Foo.from_int(3) - var b: U32 = a.get() - var c: U32 = g(b) - - fun g(x: U32): U32 => - x + 1 - +--8<-- "methods-functions-calling-implicit-this.pony" ``` Constructors can also be called on an expression. Here an object is created of the same type as the specified expression - this is equivalent to directly specifying the type. ```pony -class Foo - var _x: U32 - - new create() => - _x = 0 - - new from_int(x: U32) => - _x = x - -class Bar - fun f() => - var a: Foo = Foo.create() - var b: Foo = a.from_int(3) +--8<-- "methods-constructors-calling-on-expression.pony" ``` We can even reuse the variable name in the assignment expression to call the constructor. ```pony -class Bar - fun f() => - var a: Foo = a.create() +--8<-- "methods-constructors-calling-reuse-variable-name.pony" ``` Here we specify that `var a` is type `Foo`, then proceed to use `a` to call the constructor, `create()`, for objects of type `Foo`. @@ -140,19 +77,7 @@ Here we specify that `var a` is type `Foo`, then proceed to use `a` to call the When defining a method you can provide default values for any of the arguments. The caller then has the choice to use the values you have provided or to provide their own. Default argument values are specified with a `=` after the parameter name. ```pony -class Coord - var _x: U32 - var _y: U32 - - new create(x: U32 = 0, y: U32 = 0) => - _x = x - _y = y - -class Bar - fun f() => - var a: Coord = Coord.create() // Contains (0, 0) - var b: Coord = Coord.create(3) // Contains (3, 0) - var c: Coord = Coord.create(3, 4) // Contains (3, 4) +--8<-- "methods-default-arguments.pony" ``` __Do I have to provide default values for all of my arguments?__ No, you can provide defaults for as many, or as few, as you like. @@ -164,18 +89,7 @@ So far, when calling methods we have always given all the arguments in order. Th To call a method using named arguments the `where` keyword is used, followed by the named arguments and their values. ```pony -class Coord - var _x: U32 - var _y: U32 - - new create(x: U32 = 0, y: U32 = 0) => - _x = x - _y = y - -class Bar - fun f() => - var a: Coord = Coord.create(3, 4) // Contains (3, 4) - var b: Coord = Coord.create(where y = 4, x = 3) // Contains (3, 4) +--8<-- "methods-named-arguments.pony" ``` Note how in `b` above, the arguments were given out of order by using `where` followed using the name of the arguments. @@ -187,14 +101,7 @@ Named and positional arguments can be used together in a single call. Just start Default arguments can also be used in combination with positional and named arguments - just miss out any for which you want to use the default. ```pony -class Foo - fun f(a: U32 = 1, b: U32 = 2, c: U32 = 3, d: U32 = 4, e: U32 = 5): U32 => - 0 - - fun g() => - f(6, 7 where d = 8) - // Equivalent to: - f(6, 7, 3, 8, 5) +--8<-- "methods-named-and-positional-arguments-combined.pony" ``` __Can I call using positional arguments but miss out the first one?__ No. If you use positional arguments they must be the first ones in the call. @@ -204,28 +111,13 @@ __Can I call using positional arguments but miss out the first one?__ No. If you Method chaining allows you to chain calls on an object without requiring the method to return its receiver. The syntax to call a method and chain the receiver is `object.>method()`, which is roughly equivalent to `(object.method() ; object)`. Chaining a method discards its normal return value. ```pony -primitive Printer - fun print_two_strings(out: StdStream, s1: String, s2: String) => - out.>print(s1).>print(s2) - // Equivalent to: - out.print(s1) - out.print(s2) - out +--8<-- "methods-chaining.pony" ``` Note that the last `.>` in a chain can be a `.` if the return value of the last call matters. ```pony -interface Factory - fun add_option(o: Option) - fun make_object(): Object - -primitive Foo - fun object_wrong(f: Factory, o1: Option, o2: Option): Object => - f.>add_option(o1).>add_option(o2).>make_object() // Error! The expression returns a Factory - - fun object_right(f: Factory, o1: Option, o2: Option): Object => - f.>add_option(o1).>add_option(o2).make_object() // Works. The expression returns an Object +--8<-- "methods-chaining-return-value.pony" ``` ## Anonymous methods @@ -233,15 +125,7 @@ primitive Foo Pony has anonymous methods (or Lambdas). They look like this: ```pony -use "collections" - -actor Main - new create(env: Env) => - let list_of_numbers = List[U32].from([1; 2; 3; 4]) - let is_odd = {(n: U32): Bool => (n % 2) == 1} - for odd_number in list_of_numbers.filter(is_odd).values() do - env.out.print(odd_number.string()) - end +--8<-- "methods-anonymous-methods.pony" ``` They are presented more in-depth in the [Object Literals section](/expressions/object-literals.md). diff --git a/docs/expressions/object-literals.md b/docs/expressions/object-literals.md index 1f74ac44..eafe9d00 100644 --- a/docs/expressions/object-literals.md +++ b/docs/expressions/object-literals.md @@ -9,32 +9,19 @@ But Pony is statically typed, so an object literal also creates an anonymous typ It basically looks like any other type definition, but with some small differences. Here's a simple one: ```pony -object - fun apply(): String => "hi" -end +--8<-- "object-literals-object-literal.pony" ``` Ok, that's pretty trivial. Let's extend it so that it explicitly provides an interface so that the compiler will make sure the anonymous type fulfills that interface. You can use the same notation to provide traits as well. ```pony -object is Hashable - fun apply(): String => "hi" - fun hash(): USize => this().hash() -end +--8<-- "object-literals-object-literal-with-interface.pony" ``` What we can't do is specify constructors in an object literal, because the literal _is_ the constructor. So how do we assign to fields? Well, we just assign to them. For example: ```pony -use "collections" - -class Foo - fun foo(str: String): Hashable => - object is Hashable - let s: String = str - fun apply(): String => s - fun hash(): USize => s.hash() - end +--8<-- "object-literals-fields-assignment.pony" ``` When we assign to a field in the constructor, we are _capturing_ from the lexical scope the object literal is in. Pretty fun stuff! It lets us have arbitrarily complex __closures__ that can even have multiple entry points (i.e. functions you can call on a closure). @@ -42,28 +29,13 @@ When we assign to a field in the constructor, we are _capturing_ from the lexica An object literal with fields is returned as a `ref` by default unless an explicit reference capability is declared by specifying the capability after the `object` keyword. For example, an object with sendable captured references can be declared as `iso` if needed: ```pony -use "collections" - -class Foo - fun foo(str: String): Hashable iso^ => - object iso is Hashable - let s: String = str - fun apply(): String => s - fun hash(): USize => s.hash() - end +--8<-- "object-literals-reference-capability.pony" ``` We can also implicitly capture values from the lexical scope by using them in the object literal. Sometimes values that aren't local variables, aren't fields, and aren't parameters of a function are called _free variables_. By using them in a function, we are _closing over_ them - that is, capturing them. The code above could be written without the field `s`: ```pony -use "collections" - -class Foo - fun foo(str: String): Hashable iso^ => - object iso is Hashable - fun apply(): String => str - fun hash(): USize => str.hash() - end +--8<-- "object-literals-closing-over-values.pony" ``` ## Lambdas @@ -71,47 +43,37 @@ class Foo Arbitrarily complex closures are nice, but sometimes we just want a simple closure. In Pony, you can use the lambdas for that. A lambda is written as a function (implicitly named `apply`) enclosed in curly brackets: ```pony -{(s: String): String => "lambda: " + s } +--8<-- "object-literals-lambda.pony" ``` This produces the same code as: ```pony -object - fun apply(s: String): String => "lambda: " + s -end +--8<-- "object-literals-lambda-as-explicit-object-literal.pony" ``` The reference capability of the lambda object can be declared by appending it after the closing curly bracket: ```pony -{(s: String): String => "lambda: " + s } iso +--8<-- "object-literals-lambda-with-reference-capability.pony" ``` This produces the same code as: ```pony -object iso - fun apply(s: String): String => "lambda: " + s -end +--8<-- "object-literals-lambda-with-reference-capability-as-explicit-object-literal.pony" ``` Lambdas can be used to capture from the lexical scope in the same way as object literals can assign from the lexical scope to a field. This is done by adding a second argument list after the parameters: ```pony -class Foo - new create(env: Env) => - foo({(s: String)(env) => env.out.print(s) }) - - fun foo(f: {(String)}) => - f("Hello World") +--8<-- "object-literals-lambda-capture-values.pony" ``` It's also possible to use a _capture list_ to create new names for things. A capture list is a second parenthesised list after the parameters: ```pony - new create(env: Env) => - foo({(s: String)(myenv = env) => myenv.out.print(s) }) +--8<-- "object-literals-lambda-capture-and-rename-values.pony" ``` The type of a lambda is also declared using curly brackets. Within the brackets, the function parameter types are specified within parentheses followed by an optional colon and return type. The example above uses `{(String)}` to be the type of a lambda function that takes a `String` as an argument and returns nothing. @@ -119,22 +81,7 @@ The type of a lambda is also declared using curly brackets. Within the brackets, If the lambda object is not declared with a specific reference capability, the reference capability is inferred from the structure of the lambda. If the lambda does not have any captured references, it will be `val` by default; if it does have captured references, it will be `ref` by default. The following is an example of a `val` lambda object: ```pony -use "collections" - -actor Main - new create(env: Env) => - let l = List[U32] - l.>push(10).>push(20).>push(30).push(40) - let r = reduce(l, 0, {(a:U32, b:U32): U32 => a + b }) - env.out.print("Result: " + r.string()) - - fun reduce(l: List[U32], acc: U32, f: {(U32, U32): U32} val): U32 => - try - let acc' = f(acc, l.shift()?) - reduce(l, acc', f) - else - acc - end +--8<-- "object-literals-lambda-reference-capabilities.pony" ``` The `reduce` method in this example requires the lambda type for the `f` parameter to require a reference capability of `val`. The lambda object passed in as an argument does not need to declare an explicit reference capability because `val` is the default for a lambda that does not capture anything. @@ -142,25 +89,7 @@ The `reduce` method in this example requires the lambda type for the `f` paramet As mentioned previously the lambda desugars to an object literal with an `apply` method. The reference capability for the `apply` method defaults to `box` like any other method. In a lambda that captures references, this needs to be `ref` if the function needs to modify any of the captured variables or call `ref` methods on them. The reference capability for the method (versus the reference capability for the object which was described above) is defined by putting the capability before the parenthesized argument list. ```pony -use "collections" - -actor Main - new create(env: Env) => - let l = List[String] - l.>push("hello").push("world") - var count = U32(0) - for_each(l, {ref(s:String) => - env.out.print(s) - count = count + 1 - }) - // Displays '0' as the count - env.out.print("Count: " + count.string()) - - fun for_each(l: List[String], f: {ref(String)} ref) => - try - f(l.shift()?) - for_each(l, f) - end +--8<-- "object-literals-lambda-reference-capabilities-2.pony" ``` This example declares the type of the apply function that is generated by the lambda expression as being `ref`. The lambda type declaration for the `f` parameter in the `for_each` method also declares it as `ref`. The reference capability of the lambda type must also be `ref` so that the method can be called. The lambda object does not need to declare an explicit reference capability because `ref` is the default for a lambda that has captures. @@ -172,9 +101,7 @@ The above example also notes a subtle reality of captured references. At first g Normally, an object literal is an instance of an anonymous class. To make it an instance of an anonymous actor, just include one or more behaviours in the object literal definition. ```pony -object - be apply() => env.out.print("hi") -end +--8<-- "object-literals-actor-literal.pony" ``` An actor literal is always returned as a `tag`. diff --git a/docs/expressions/ops.md b/docs/expressions/ops.md index 94d110c5..287449b8 100644 --- a/docs/expressions/ops.md +++ b/docs/expressions/ops.md @@ -5,8 +5,7 @@ Infix operators take two operands and are written between those operands. Arithmetic and comparison operators are the most common: ```pony -1 + 2 -a < b +--8<-- "operators-infix-operator.pony" ``` Pony has pretty much the same set of infix operators as other languages. @@ -16,8 +15,7 @@ Pony has pretty much the same set of infix operators as other languages. Most infix operators in Pony are actually aliases for functions. The left operand is the receiver the function is called on and the right operand is passed as an argument. For example, the following expressions are equivalent: ```pony -x + y -x.add(y) +--8<-- "operators-operator-aliasing.pony" ``` This means that `+` is not a special symbol that can only be applied to magic types. Any type can provide its own `add` function and the programmer can then use `+` with that type if they want to. @@ -27,25 +25,7 @@ When defining your own `add` function there is no restriction on the types of th Here's a full example for defining a type which allows the use of `+`. This is all you need: ```pony -// Define a suitable type -class Pair - var _x: U32 = 0 - var _y: U32 = 0 - - new create(x: U32, y: U32) => - _x = x - _y = y - - // Define a + function - fun add(other: Pair): Pair => - Pair(_x + other._x, _y + other._y) - -// Now let's use it -class Foo - fun foo() => - var x = Pair(1, 2) - var y = Pair(3, 4) - var z = x + y +--8<-- "operators-add.pony" ``` It is possible to overload infix operators to some degree using union types or f-bounded polymorphism, but this is beyond the scope of this tutorial. See the Pony standard library for further information. @@ -113,8 +93,7 @@ This is a special feature built into the compiler, it cannot be used with operat The unary operators are handled in the same manner, but with only one operand. For example, the following expressions are equivalent: ```pony --x -x.neg() +--8<-- "operators-unary-operators.pony" ``` The full list of unary operators that are aliases for functions is: @@ -136,7 +115,7 @@ In Pony, unary operators always bind stronger than any infix operators: `not a = When using infix operators in complex expressions a key question is the __precedence__, i.e. which operator is evaluated first. Given this expression: ```pony -1 + 2 * 3 // Compilation failed. +--8<-- "operators-precedence-without-parentheses.pony" ``` We will get a value of 9 if we evaluate the addition first and 7 if we evaluate the multiplication first. In mathematics, there are rules about the order in which to evaluate operators and most programming languages follow this approach. @@ -148,25 +127,25 @@ Pony takes a different approach and outlaws infix precedence. Any expression whe This means that the example above is illegal in Pony and should be rewritten as: ```pony -1 + (2 * 3) // 7 +--8<-- "operators-precedence-with-parentheses.pony" ``` Repeated use of a single operator, however, is fine: ```pony -1 + 2 + 3 // 6 +--8<-- "operators-precedence-single-operator.pony" ``` Meanwhile, mixing unary and infix operators do not need additional parentheses as unary operators always bind more closely, so if our example above used a negative three: ```pony -1 + 2 * -3 // Compilation failed. +--8<-- "operators-precedence-infix-and-unary-operators-without-parentheses.pony" ``` We would still need parentheses to remove the ambiguity for our infix operators like we did above, but not for the unary arithmetic negative (`-`): ```pony -1 + (2 * -3) // -5 +--8<-- "operators-precedence-infix-and-unary-operators-with-parentheses.pony" ``` We can see that it makes more sense for the unary operator to be applied before either infix as it only acts on a single number in the expression so it is never ambiguous. @@ -174,5 +153,5 @@ We can see that it makes more sense for the unary operator to be applied before Unary operators can also be applied to parentheses and act on the result of all operations in those parentheses prior to applying any infix operators outside the parentheses: ```pony -1 + -(2 * -3) // 7 +--8<-- "operators-precedence-unary-operator-with-parentheses.pony" ``` diff --git a/docs/expressions/partial-application.md b/docs/expressions/partial-application.md index 52345214..2ddf8ca0 100644 --- a/docs/expressions/partial-application.md +++ b/docs/expressions/partial-application.md @@ -7,17 +7,7 @@ Partial application lets us supply _some_ of the arguments to a constructor, fun A simple case is to create a "callback" function. For example: ```pony -class Foo - var _f: F64 = 0 - - fun ref addmul(add: F64, mul: F64): F64 => - _f = (_f + add) * mul - -class Bar - fun apply() => - let foo: Foo = Foo - let f = foo~addmul(3) - f(4) +--8<-- "partial-application-callback-function-with-some-arguments-bound.pony" ``` This is a bit of a silly example, but hopefully, the idea is clear. We partially apply the `addmul` function on `foo`, binding the receiver to `foo` and the `add` argument to `3`. We get back an object, `f`, that has an `apply` method that takes a `mul` argument. When it's called, it in turn calls `foo.addmul(3, mul)`. @@ -25,15 +15,13 @@ This is a bit of a silly example, but hopefully, the idea is clear. We partially We can also bind all the arguments: ```pony -let f = foo~addmul(3, 4) -f() +--8<-- "partial-application-callback-function-with-all-arguments-bound.pony" ``` Or even none of the arguments: ```pony -let f = foo~addmul() -f(3, 4) +--8<-- "partial-application-callback-function-with-no-arguments-bound.pony" ``` ## Out of order arguments @@ -41,8 +29,7 @@ f(3, 4) Partial application with named arguments allows binding arguments in any order, not just left to right. For example: ```pony -let f = foo~addmul(where mul = 4) -f(3) +--8<-- "partial-application-callback-function-with-out-of-order-arguments.pony" ``` Here, we bound the `mul` argument but left `add` unbound. @@ -58,7 +45,5 @@ That means partial application results in an anonymous class and returns a `ref` Since partial application results in an object with an apply method, we can partially apply the result! ```pony -let f = foo~addmul() -let f2 = f~apply(where mul = 4) -f2(3) +--8<-- "partial-application-partially-applying-a-partial-application.pony" ``` diff --git a/docs/expressions/sugar.md b/docs/expressions/sugar.md index 8213b490..3daf4d5e 100644 --- a/docs/expressions/sugar.md +++ b/docs/expressions/sugar.md @@ -7,29 +7,25 @@ Pony allows you to omit certain small details from your code and will put them b Many Pony classes have a function called `apply` which performs whatever action is most common for that type. Pony allows you to omit the word `apply` and just attempt to do a call directly on the object. So: ```pony -var foo = Foo.create() -foo() +--8<-- "sugar-apply-implicit.pony" ``` becomes: ```pony -var foo = Foo.create() -foo.apply() +--8<-- "sugar-apply-explicit.pony" ``` Any required arguments can be added just like normal method calls. ```pony -var foo = Foo.create() -foo(x, 37 where crash = false) +--8<-- "sugar-apply-with-arguments-implicit.pony" ``` becomes: ```pony -var foo = Foo.create() -foo.apply(x, 37 where crash = false) +--8<-- "sugar-apply-with-arguments-explicit.pony" ``` __Do I still need to provide the arguments to apply?__ Yes, only the `apply` will be added for you, the correct number and type of arguments must be supplied. Default and named arguments can be used as normal. @@ -41,13 +37,13 @@ __How do I call a function foo if apply is added?__ The `apply` sugar is only ad To create an object you need to specify the type and call a constructor. Pony allows you to miss out the constructor and will insert a call to `create()` for you. So: ```pony -var foo = Foo +--8<-- "sugar-create-implicit.pony" ``` becomes: ```pony -var foo = Foo.create() +--8<-- "sugar-create-explicit.pony" ``` Normally types are not valid things to appear in expressions, so omitting the constructor call is not ambiguous. Remember that you can easily spot that a name is a type because it will start with a capital letter. @@ -55,13 +51,13 @@ Normally types are not valid things to appear in expressions, so omitting the co If arguments are needed for `create` these can be provided as if calling the type. Default and named arguments can be used as normal. ```pony -var foo = Foo(x, 37 where crash = false) +--8<-- "sugar-create-with-arguments-implicit.pony" ``` becomes: ```pony -var foo = Foo.create(x, 37 where crash = false) +--8<-- "sugar-create-with-arguments-explicit.pony" ``` __What if I want to use a constructor that isn't named create?__ Then the sugar can't help you and you have to write it out yourself. @@ -73,15 +69,13 @@ __If the create I want to call takes no arguments can I still put in the parenth If a type has a create constructor that takes no arguments then the create and apply sugar can be used together. Just call on the type and calls to create and apply will be added. The call to create will take no arguments and the call to apply will take whatever arguments are supplied. ```pony -var foo = Foo() -var bar = Bar(x, 37 where crash = false) +--8<-- "sugar-create-apply-combined-implicit.pony" ``` becomes: ```pony -var foo = Foo.create().apply() -var bar = Bar.create().apply(x, 37 where crash = false) +--8<-- "sugar-create-apply-combined-explicit.pony" ``` __What if the create has default arguments? Do I get the combined create-apply sugar if I want to use the defaults?__ The combined create-apply sugar can only be used when the `create` constructor has no arguments. If there are default arguments then this sugar cannot be used. @@ -93,13 +87,13 @@ The `update` sugar allows any class to use an assignment to accept data. Many la In any assignment where the left-hand side is a function call, Pony will translate this to a call to update, with the value from the right-hand side as an extra argument. So: ```pony -foo(37) = x +--8<-- "sugar-update-implicit.pony" ``` becomes: ```pony -foo.update(37 where value = x) +--8<-- "sugar-update-explicit.pony" ``` The value from the right-hand side of the assignment is always passed to a parameter named `value`. Any object can allow this syntax simply by providing an appropriate function `update` with an argument `value`. @@ -107,9 +101,7 @@ The value from the right-hand side of the assignment is always passed to a param __Does my update function have to have a single parameter that takes an integer?__ No, you can define update to take whatever parameters you like, as long as there is one called `value`. The following are all fine: ```pony -foo1(2, 3) = x -foo2() = x -foo3(37, "Hello", 3.5 where a = 2, b = 3) = x +--8<-- "sugar-update-additional-parameters.pony" ``` __Does it matter where `value` appears in my parameter list?__ Whilst it doesn't strictly matter it is good practice to put `value` as the last parameter. That way all of the others can be specified by position. diff --git a/docs/expressions/variables.md b/docs/expressions/variables.md index 910df754..740083b3 100644 --- a/docs/expressions/variables.md +++ b/docs/expressions/variables.md @@ -9,7 +9,7 @@ Local variables in Pony work very much as they do in other languages, allowing y To define a local variable the `var` keyword is used (`let` can also be used, but we'll get to that later). Right after the `var` comes the variable's name, and then you can (optionally) put a `:` followed by the variable's type. For example: ```pony -var x: String = "Hello" +--8<-- "variables-local-variables.pony:1:1" ``` Here, we're assigning the __string literal__ `"Hello"` to `x`. @@ -21,12 +21,7 @@ Every variable has a type, but you don't have to specify it in the declaration i The following definitions of `x`, `y` and `z` are all effectively identical. ```pony -var x: String = "Hello" - -var y = "Hello" - -var z: String -z = "Hello" +--8<-- "variables-local-variables.pony" ``` __Can I miss out both the type and initial value for a variable?__ No. The compiler will complain that it can't figure out a type for that variable. @@ -36,12 +31,7 @@ All local variable names start with a lowercase letter. If you want to you can e The chunk of code that a variable lives in is known as its __scope__. Exactly what its scope is depends on where it is defined. For example, the scope of a variable defined within the `then` expression of an `if` statement is that `then` expression. We haven't looked at `if` statements yet, but they're very similar to every other language. ```pony -if a > b then - var x = "a is bigger" - env.out.print(x) // OK -end - -env.out.print(x) // Illegal +--8<-- "variables-scope.pony" ``` Variables only exist from when they are defined until the end of the current scope. For our variable `x` this is the `end` at the end of the then expression: after that, it cannot be used. @@ -51,18 +41,13 @@ Variables only exist from when they are defined until the end of the current sco Local variables are declared with either a `var` or a `let`. Using `var` means the variable can be assigned and reassigned as many times as you like. Using `let` means the variable can only be assigned once. ```pony -var x: U32 = 3 -let y: U32 = 4 -x = 5 // OK -y = 6 // Error, y is let +--8<-- "variables-var-vs-let.pony" ``` Using `let` instead of `var` also means the variable has to be assigned immediately. ```pony -let x: U32 = 3 // Ok -let y: U32 // Error, can't declare a let local without assigning to it -y = 6 // Error, can't reassign to a let local +--8<-- "variables-let-reassignment.pony" ``` Note that a variable having been declared with `let` only restricts reassignment, and does not influence the mutability of the object it references. This is the job of reference capabilities, explained later in this tutorial. @@ -85,21 +70,13 @@ Just like local variables, fields can be `var` or `let`. Nevertheless, rules for In the example below, the initial value of the two fields of the class `Wombat` is assigned at the definition level: ```pony -class Wombat - let name: String = "Fantastibat" - var _hunger_level: U32 = 0 +--8<-- "variables-fields-definition-assignment.pony" ``` Alternatively, these fields could be assigned in the constructor method: ```pony -class Wombat - let name: String - var _hunger_level: U32 - - new create(hunger: U32) => - name = "Fantastibat" - _hunger_level = hunger +--8<-- "variables-fields-constructor-assignment.pony" ``` If the assignment is not done at the definition level or in the constructor, an error is raised by the compiler. This is true for both `var` and `let` fields. @@ -107,17 +84,7 @@ If the assignment is not done at the definition level or in the constructor, an Please note that the assignment of a value to a field has to be explicit. The below example raises an error when compiled, even when the field is of `var` type: ```pony -class Wombat - let name: String - var _hunger_level: U64 - - new ref create(name': String, level: U64) => - name = name' - set_hunger_level(level) - // Error: field _hunger_level left undefined in constructor - - fun ref set_hunger_level(hunger_level: U64) => - _hunger_level = hunger_level +--8<-- "variables-fields-implicit-assignment.pony" ``` We will see later in the Methods section that a class can have several constructors. For now, just remember that if the assignment of a field is not done at the definition level, it has to be done in each constructor of the class the field belongs to. @@ -125,19 +92,7 @@ We will see later in the Methods section that a class can have several construct As for variables, using `var` means a field can be assigned and reassigned as many times as you like in the class. Using `let` means the field can only be assigned once. ```pony -class Wombat - let name: String - var _hunger_level: U64 - - new ref create(name': String, level: U64) => - name = name' - _hunger_level = level - - fun ref set_hunger_level(hunger_level: U64) => - _hunger_level = hunger_level // Ok, _hunger_level is of var type - - fun ref set_name(name' : String) => - name = name' // Error, can't assign to a let definition more than once +--8<-- "variables-fields-let-reassignment.pony" ``` __Can field declarations appear after methods?__ No. If `var` or `let` keywords appear after a `fun` or `be` declaration, they will be treated as variables within the method body rather than fields within the type declaration. As a result, fields must appear prior to methods in the type declaration diff --git a/docs/generics/generic-constraints.md b/docs/generics/generic-constraints.md index 67bda8a7..47a28747 100644 --- a/docs/generics/generic-constraints.md +++ b/docs/generics/generic-constraints.md @@ -5,7 +5,7 @@ The type parameter constraint for a generic class or method can constrain to a particular capability as seen previously: ```pony -class Foo[A: Any val] +--8<-- "generics-foo-with-any-val.pony::1" ``` Without the constraint, the generic must work for all possible capabilities. Sometimes you don't want to be limited to a specific capability and you can't support all capabilities. The solution for this is generic constraint qualifiers. These represent classes of capabilities that are accepted in the generic. The valid qualifiers are: @@ -21,21 +21,5 @@ Without the constraint, the generic must work for all possible capabilities. Som In the previous section, we went through extra work to support `iso`. If there's no requirement for `iso` support we can use `#read` and support `ref`, `val`, and `box`: ```pony -class Foo[A: Any #read] - var _c: A - - new create(c: A) => - _c = c - - fun get(): this->A => _c - - fun ref set(c: A) => _c = c - -actor Main - new create(env:Env) => - let a = Foo[String ref](recover ref "hello".clone() end) - env.out.print(a.get().string()) - - let b = Foo[String val]("World") - env.out.print(b.get().string()) +--8<-- "generic-constraints-foo-any-read.pony" ``` diff --git a/docs/generics/generics-and-reference-capabilities.md b/docs/generics/generics-and-reference-capabilities.md index cac5fb5a..fa7e634b 100644 --- a/docs/generics/generics-and-reference-capabilities.md +++ b/docs/generics/generics-and-reference-capabilities.md @@ -3,63 +3,31 @@ In the examples presented previously we've explicitly set the reference capability to `val`: ```pony -class Foo[A: Any val] +--8<-- "generics-foo-with-any-val.pony::1" ``` If the capability is left out of the type parameter then the generic class or function can accept any reference capability. This would look like: ```pony -class Foo[A: Any] +--8<-- "generics-and-reference-capabilities-explicit-constraint-and-default-capability.pony" ``` It can be made shorter because `Any` is the default constraint, leaving us with: ```pony -class Foo[A] +--8<-- "generics-and-reference-capabilities-default-capability-and-constraint.pony" ``` This is what the example shown before looks like but with any reference capability accepted: ```pony -// Note - this won't compile -class Foo[A] - var _c: A - - new create(c: A) => - _c = c - - fun get(): A => _c - - fun ref set(c: A) => _c = c - -actor Main - new create(env: Env) => - let a = Foo[U32](42) - env.out.print(a.get().string()) - a.set(21) - env.out.print(a.get().string()) +--8<-- "generics-and-reference-capabilities-accept-any-reference-capability.pony" ``` Unfortunately, this doesn't compile. For a generic class to compile it must be compilable for all possible types and reference capabilities that satisfy the constraints in the type parameter. In this case, that's any type with any reference capability. The class works for the specific reference capability of `val` as we saw earlier, but how well does it work for `ref`? Let's expand it and see: ```pony -// Note - this also won't compile -class Foo - var _c: String ref - - new create(c: String ref) => - _c = c - - fun get(): String ref => _c - - fun ref set(c: String ref) => _c = c - -actor Main - new create(env: Env) => - let a = Foo(recover ref String end) - env.out.print(a.get().string()) - a.set(recover ref String end) - env.out.print(a.get().string()) +--8<-- "generics-and-reference-capabilities-foo-ref.pony" ``` This does not compile. The compiler complains that `get()` doesn't actually return a `String ref`, but `this->String ref`. We obviously need to simply change the type signature to fix this, but what is going on here? @@ -68,22 +36,7 @@ This does not compile. The compiler complains that `get()` doesn't actually retu So let's apply what we just learned: ```pony -class Foo - var _c: String ref - - new create(c: String ref) => - _c = c - - fun get(): this->String ref => _c - - fun ref set(c: String ref) => _c = c - -actor Main - new create(env: Env) => - let a = Foo(recover ref String end) - env.out.print(a.get().string()) - a.set(recover ref String end) - env.out.print(a.get().string()) +--8<-- "generics-and-reference-capabilities-foo-ref-and-this-ref.pony" ``` That compiles and runs, so `ref` is valid now. The real test though is `iso`. Let's convert the class to `iso` and walk through what is needed to get it to compile. We'll then revisit our generic class to get it working: @@ -91,76 +44,37 @@ That compiles and runs, so `ref` is valid now. The real test though is `iso`. Le ## An `iso` specific class ```pony -// Note - this won't compile -class Foo - var _c: String iso - - new create(c: String iso) => - _c = c - - fun get(): this->String iso => _c - - fun ref set(c: String iso) => _c = c - -actor Main - new create(env: Env) => - let a = Foo(recover iso String end) - env.out.print(a.get().string()) - a.set(recover iso String end) - env.out.print(a.get().string()) +--8<-- "generics-and-reference-capabilities-foo-iso.pony" ``` This fails to compile. The first error is: ```error -main.pony:5:8: right side must be a subtype of left side - _c = c - ^ - Info: - main.pony:4:17: String iso! is not a subtype of String iso: iso! is not a subtype of iso - new create(c: String iso) => - ^ +--8<-- "generics-and-reference-capabilities-foo-iso-error-message.txt" ``` The error is telling us that we are aliasing the `String iso` - The `!` in `iso!` means it is an alias of an existing `iso`. Looking at the code shows the problem: ```pony -new create(c: String iso) => - _c = c +--8<-- "generics-and-reference-capabilities-foo-iso.pony:5:6" ``` We have `c` as an `iso` and are trying to assign it to `_c`. This creates two aliases to the same object, something that `iso` does not allow. To fix it for the `iso` case we have to `consume` the parameter. The correct constructor should be: ```pony -new create(c: String iso) => - _c = consume c +--8<-- "generics-and-reference-capabilities-foo-iso-consume-iso-constructor-parameter.pony:4:5" ``` A similar issue exists with the `set` method. Here we also need to consume the variable `c` that is passed in: ```pony -fun set(c: String iso) => _c = consume c +--8<-- "generics-and-reference-capabilities-foo-iso-consume-iso-function-parameter.pony" ``` Now we have a version of `Foo` that is working correctly for `iso`. Note how applying the arrow type to the `get` method also works for `iso`. But here the result is a different one, by applying [viewpoint adaptation](/reference-capabilities/combining-capabilities.md) we get from `ref->iso` (with `ref` being the capability of the receiver, the `Foo` object referenced by `a`) to `iso`. Through the magic of [automatic receiver recovery](/reference-capabilities/recovering-capabilities.md) we can call the `string` method on it: ```pony -class Foo - var _c: String iso - - new create(c: String iso) => - _c = consume c - - fun get(): this->String iso => _c - - fun ref set(c: String iso) => _c = consume c - -actor Main - new create(env: Env) => - let a = Foo(recover iso String end) - env.out.print(a.get().string()) - a.set(recover iso String end) - env.out.print(a.get().string()) +--8<-- "generics-and-reference-capabilities-foo-iso-consume-iso-constructor-parameter.pony" ``` ## A capability generic class @@ -168,26 +82,7 @@ actor Main Now that we have `iso` working we know how to write a generic class that works for `iso` and it will work for other capabilities too: ```pony -class Foo[A] - var _c: A - - new create(c: A) => - _c = consume c - - fun get(): this->A => _c - - fun ref set(c: A) => _c = consume c - -actor Main - new create(env: Env) => - let a = Foo[String iso]("Hello".clone()) - env.out.print(a.get().string()) - - let b = Foo[String ref](recover ref "World".clone() end) - env.out.print(b.get().string()) - - let c = Foo[U8](42) - env.out.print(c.get().string()) +--8<-- "generics-and-reference-capabilities-capability-generic-class.pony" ``` It's quite a bit of work to get a generic class or method to work across all capability types, in particular for `iso`. There are ways of restricting the generic to subsets of capabilities and that's the topic of the next section. diff --git a/docs/generics/index.md b/docs/generics/index.md index 6192e451..93799b00 100644 --- a/docs/generics/index.md +++ b/docs/generics/index.md @@ -10,49 +10,13 @@ Parameters are introduced to a class using square brackets. Take the following example of a non-generic class: ```pony -class Foo - var _c: U32 - - new create(c: U32) => - _c = c - - fun get(): U32 => _c - - fun ref set(c: U32) => _c = c - -actor Main - new create(env:Env) => - let a = Foo(42) - env.out.print(a.get().string()) - a.set(21) - env.out.print(a.get().string()) +--8<-- "generics-foo-non-generic.pony" ``` This class only works for the type `U32`, a 32 bit unsigned integer. We can make this work over other types by making the type a parameter to the class. For this example it looks like: ```pony -class Foo[A: Any val] - var _c: A - - new create(c: A) => - _c = c - - fun get(): A => _c - - fun ref set(c: A) => _c = c - -actor Main - new create(env:Env) => - let a = Foo[U32](42) - env.out.print(a.get().string()) - a.set(21) - - env.out.print(a.get().string()) - let b = Foo[F32](1.5) - env.out.print(b.get().string()) - - let c = Foo[String]("Hello") - env.out.print(c.get().string()) +--8<-- "generics-foo-with-any-val.pony" ``` The first thing to note here is that the `Foo` class now takes a type parameter in square brackets, `[A: Any val]`. That syntax for the type parameter is: @@ -64,23 +28,13 @@ In this case, the name is `A`, the constraint is `Any` and the reference capabil The user of the class must provide a type when referencing the class name. This is done when creating it: ```pony -let a = Foo[U32](42) -let b = Foo[F32](1.5) -let c = Foo[String]("Hello") +--8<-- "generics-generic-class-initialization.pony" ``` That tells the compiler what specific class to create, replacing `A` with the type provided. For example, a `Foo[String]` usage becomes equivalent to: ```pony -class FooString - var _c: String val - - new create(c: String val) => - _c = c - - fun get(): String val => _c - - fun ref set(c: String val) => _c = c +--8<-- "generics-foo-string.pony" ``` ### Type parameter defaults @@ -89,23 +43,13 @@ Sometimes the same parameter type is used over and over again, and it is tedious The class `Bar` expects its type parameter to be a `USize val` by default: ```pony -class Bar[A: Any box = USize val] - var _c: A - - new create(c: A) => - _c = c - - fun get(): A => _c - - fun ref set(c: A) => _c = c +--8<-- "generics-type-parameter-defaults-definition.pony" ``` Now, when the default type parameter is the desired one, it can simply be omitted. But it is still possible to be explicit or use a different type. ```pony -let a = Bar(42) -let b = Bar[USize](42) -let c = Bar[F32](1.5) +--8<-- "generics-type-parameter-defaults-initialization.pony" ``` Note that we could simply write `class Bar[A: Any box = USize]` because `val` is the default reference capability of the `USize` type. @@ -115,17 +59,7 @@ Note that we could simply write `class Bar[A: Any box = USize]` because `val` is Methods can be generic too. They are defined in the same way as normal methods but have type parameters inside square brackets after the method name: ```pony -primitive Foo - fun bar[A: Stringable val](a: A): String => - a.string() - -actor Main - new create(env:Env) => - let a = Foo.bar[U32](10) - env.out.print(a.string()) - - let b = Foo.bar[String]("Hello") - env.out.print(b.string()) +--8<-- "generics-generic-methods.pony" ``` This example shows a constraint other than `Any`. The `Stringable` type is any type with a `string()` method to convert to a `String`. diff --git a/docs/getting-started/hello-world.md b/docs/getting-started/hello-world.md index cfd178de..09cdcc1b 100644 --- a/docs/getting-started/hello-world.md +++ b/docs/getting-started/hello-world.md @@ -18,9 +18,7 @@ __Does the name of the file matter?__ Not to the compiler, no. Pony doesn't care In your file, put the following code: ```pony -actor Main - new create(env: Env) => - env.out.print("Hello, world!") +--8<-- "hello-world-main.pony" ``` ## Compiling the program diff --git a/docs/getting-started/how-it-works.md b/docs/getting-started/how-it-works.md index b00ea407..5f0c069c 100644 --- a/docs/getting-started/how-it-works.md +++ b/docs/getting-started/how-it-works.md @@ -3,9 +3,7 @@ Let's look at our `helloworld` code again: ```pony -actor Main - new create(env: Env) => - env.out.print("Hello, world!") +--8<-- "hello-world-main.pony" ``` Let's go through that line by line. @@ -13,7 +11,7 @@ Let's go through that line by line. ## Line 1 ```pony -actor Main +--8<-- "hello-world-main.pony::1" ``` This is a __type declaration__. The keyword `actor` means we are going to define an actor, which is a bit like a class in Python, Java, C#, C++, etc. Pony has classes too, which we'll see later. @@ -25,7 +23,7 @@ A Pony program has to have a `Main` actor. It's kind of like the `main` function ## Line 2 ```pony - new create(env: Env) => +--8<-- "hello-world-main.pony:2:2" ``` This is a __constructor__. The keyword `new` means it's a function that creates a new instance of the type. In this case, it creates a new __Main__. @@ -43,7 +41,7 @@ __Wait, what's the body?__ It's the code that comes after the `=>`. ## Line 3 ```pony - env.out.print("Hello, world!") +--8<-- "hello-world-main.pony:3:3" ``` This is your program! What the heck is it doing? diff --git a/docs/gotchas/divide-by-zero.md b/docs/gotchas/divide-by-zero.md index b492fb00..2dd35729 100644 --- a/docs/gotchas/divide-by-zero.md +++ b/docs/gotchas/divide-by-zero.md @@ -9,7 +9,7 @@ In math, divide by zero is undefined. There is no answer to that question as the In Pony, *integer division by zero results in zero*. That's right, ```pony -let x = I64(1) / I64(0) +--8<-- "divide-by-zero.pony" ``` results in `0` being assigned to `x`. Baffling right? Well, yes and no. From a mathematical standpoint, it is very much baffling. From a practical standpoint, it is very much not. @@ -17,12 +17,7 @@ results in `0` being assigned to `x`. Baffling right? Well, yes and no. From a m While Pony has [Partial division](/expressions/arithmetic.md#partial-and-checked-arithmetic): ```pony -let x = - try - I64(1) /? I64(0) - else - // handle division by zero - end +--8<-- "divide-by-zero-partial.pony" ``` Defining division as partial leads to code littered with `try`s attempting to deal with the possibility of division by zero. Even if you had asserted that your denominator was not zero, you'd still need to protect against divide by zero because, at this time, the compiler can't detect that value dependent typing. @@ -30,8 +25,7 @@ Defining division as partial leads to code littered with `try`s attempting to de Pony also offers [Unsafe Division](/expressions/arithmetic.md#unsafe-integer-arithmetic), which declares division by zero as undefined, as in C: ```pony -// the value of x is undefined -let x = I64(1) /~ I64(0) +--8<-- "divide-by-zero-unsafe.pony" ``` But declaring this case as undefined does not help us out here. As a programmer you'd still need to guard that case in order to not poison your program with undefined values or risking terminating your program with a `SIGFPE`. So, in order to maintain a practical API and avoid undefined behaviour, _normal_ division on integers in Pony is defined to be `0`. To avoid `0`s silently creeping through your divisions, use [Partial or Checked Division](/expressions/arithmetic.md#partial-and-checked-arithmetic). @@ -43,5 +37,5 @@ In conformance with IEEE 754, *floating point division by zero results in `inf` If you can assert that your denominator cannot be `0`, it is possible to use [Unsafe Division](/expressions/arithmetic.md#floating-point) to gain some performance: ```pony -let x = F64(1.5) /~ F64(0.5) +--8<-- "divide-by-zero-floats.pony" ``` diff --git a/docs/gotchas/garbage-collection.md b/docs/gotchas/garbage-collection.md index 88d114d1..87e5ace3 100644 --- a/docs/gotchas/garbage-collection.md +++ b/docs/gotchas/garbage-collection.md @@ -15,13 +15,7 @@ Garbage collection is never attempted on any actor while it is executing a behav Here's a typical "I'm learning Pony" program: ```pony -use "collections" - -actor Main - new create(env: Env) => - for i in Range(1, 2_000_000) do - ... something that uses up heap ... - end +--8<-- "garbage-collection.pony" ``` This program will never garbage collect before exiting. `create` is run as a behavior on actors, which means that no garbage collection will occur while it's running. Long loops in behaviors are a good way to exhaust memory. Don't do it. If you want to execute something in such a fashion, use a [Timer](https://stdlib.ponylang.io/time-Timer/). diff --git a/docs/gotchas/recursion.md b/docs/gotchas/recursion.md index 6b8337c3..7448e855 100644 --- a/docs/gotchas/recursion.md +++ b/docs/gotchas/recursion.md @@ -5,19 +5,7 @@ Recursive functions in Pony can cause many problems. Every function call in a pr If you have a heavy recursive algorithm, you must take some precautions in your code to avoid stack overflows. Most recursive functions can be easily transformed into tail-recursive function which are less problematic. A tail-recursive function is a function in which the recursive call is the last instruction of the function. Here is an example with a factorial function: ```pony -fun recursive_factorial(x: U32): U32 => - if x == 0 then - 1 - else - x * recursive_factorial(x - 1) - end - -fun tail_recursive_factorial(x: U32, y: U32): U32 => - if x == 0 then - y - else - tail_recursive_factorial(x - 1, x * y) - end +--8<-- "recursion.pony" ``` The compiler can optimise a tail-recursive function to a loop, completely avoiding call stack growth. Note that this is an _optimisation_ which is only performed in release builds (i.e. builds without the `-d` flag passed to ponyc.) If you need to avoid stack growth in debug builds as well then you have to write your function as a loop manually. diff --git a/docs/gotchas/scheduling.md b/docs/gotchas/scheduling.md index 46e31a21..fa70bf3f 100644 --- a/docs/gotchas/scheduling.md +++ b/docs/gotchas/scheduling.md @@ -11,10 +11,7 @@ An easy way to monopolize a scheduler thread is to use the FFI facilities of Pon Another way to monopolize a scheduler thread is to write a behavior that never exits or takes a really long time to exit. ```pony -be bad_citizen() => - while true do - _env.out.print("Never gonna give you up. Really gonna make you cry") - end +--8<-- "scheduling.pony" ``` That is some seriously bad citizen code that will hog a scheduler thread forever. Call that behavior a few times and your program will grind to a halt. If you find yourself writing code with loops that will run for a long time, stop and rethink your design. Take a look at the [Timer](https://stdlib.ponylang.io/time-Timer/) class from the standard library. Combine that together with a counter in your class and you can execute the same behavior repeatedly while yielding your scheduler thread to other actors. diff --git a/docs/gotchas/side-effect-ordering-in-function-call-expressions.md b/docs/gotchas/side-effect-ordering-in-function-call-expressions.md index 83ae7dec..db993304 100644 --- a/docs/gotchas/side-effect-ordering-in-function-call-expressions.md +++ b/docs/gotchas/side-effect-ordering-in-function-call-expressions.md @@ -3,16 +3,7 @@ Consider the following code: ```pony -class Foo - fun fn(x: U64) => None - -actor Main - new create(env: Env) => - var x: U64 = 0 - try foo()?.fn(x = 42) end - env.out.print(x.string()) - - fun foo(): Foo ? => error +--8<-- "function-call-side-effects.pony" ``` What do you think it will print? Probably `0` right? Or maybe you realized this code is in the gotchas section so it must be `42`. If you went with `42`, you'd be right. Why? diff --git a/docs/object-capabilities/derived-authority.md b/docs/object-capabilities/derived-authority.md index 3cef7bf0..9a76e44a 100644 --- a/docs/object-capabilities/derived-authority.md +++ b/docs/object-capabilities/derived-authority.md @@ -13,28 +13,7 @@ In Pony, the `Main` actor is created with an `Env` object, which holds the unfor Here is a program that connects to example.com via TCP on port 80 and quits: ```pony -use "net" - -class MyTCPConnectionNotify is TCPConnectionNotify - let _out: OutStream - - new iso create(out: OutStream) => - _out = out - - fun ref connected(conn: TCPConnection ref) => - _out.print("connected") - conn.close() - - fun ref connect_failed(conn: TCPConnection ref) => - _out.print("connect_failed") - -actor Connect - new create(out: OutStream, auth: TCPConnectAuth) => - TCPConnection(auth, MyTCPConnectionNotify(out), "example.com", "80") - -actor Main - new create(env: Env) => - Connect(env.out, TCPConnectAuth(env.root)) +--8<-- "derived-authority-delegating-and-restricting-authority.pony" ``` The `Main` actor authorizes the `Connect` actor by passing a `TCPConnectAuth` token created from the ambient authority token in `env.root`. The ambient authority token is unforgeable since the `AmbientAuth` constructor is private and the only existing instance is provided by the runtime itself. @@ -42,7 +21,7 @@ The `Main` actor authorizes the `Connect` actor by passing a `TCPConnectAuth` to The `Connect` actor uses this derived authority when it creates a TCP connection: ```pony -TCPConnection(auth, MyTCPConnectionNotify(out), "example.com", "80") +--8<-- "derived-authority-delegating-and-restricting-authority.pony:18:18" ``` The `TCPConnection` requires an authority as first parameter, and since the compiler checks that the correct type was passed, this guarantees that a `TCPConnection` can only be created by an actor holding the required authorization. @@ -58,13 +37,7 @@ The first parameter of the `TCPConnection` constructor has the type `TCPConnectA Now imagine we don't trust the `Connect` actor, so we don't want to provide it with more authority than needed. For example, there is no point in granting it filesystem access, since it is supposed to do network things (specifically, TCP), not access the filesystem. Instead of passing the entire `AmbientAuth` (the root of all authority), we "downgrade" that to a `TCPConnectAuth` (the most restrictive authority in `net`), pass it to the `Connect` actor, and have that pass it to the `TCPConnection` constructor: ```pony -actor Connect - new create(out: OutStream, auth: TCPConnectAuth) => - TCPConnection(auth, MyTCPConnectionNotify(out), "example.com", "80") - -actor Main - new create(env: Env) => - try Connect(env.out, TCPConnectAuth(env.root)) end +--8<-- "derived-authority-restrict-then-delegate-your-authority.pony" ``` Now we are sure it cannot access the filesystem or listen on a TCP or UDP port. Pay close mind to the authority that code you are calling is asking for. Never give `AmbientAuth` to __any__ code you do not trust completely both now and in the future. You should always create the most specific authority and give the library that authority. If the library is asking for more authority than it needs, __do not use the library__. @@ -80,35 +53,13 @@ As the package author, it is then our responsibility to realize that the minimal Let's have a look at the authorizations available in the standard library's `net` package. ```pony -primitive NetAuth - new create(from: AmbientAuth) => - None - -primitive DNSAuth - new create(from: (AmbientAuth | NetAuth)) => - None - -primitive UDPAuth - new create(from: (AmbientAuth | NetAuth)) => - None - -primitive TCPAuth - new create(from: (AmbientAuth | NetAuth)) => - None - -primitive TCPListenAuth - new create(from: (AmbientAuth | NetAuth | TCPAuth)) => - None - -primitive TCPConnectAuth - new create(from: (AmbientAuth | NetAuth | TCPAuth)) => - None +--8<-- "derived-authority-authority-hierarchies.pony" ``` Look at the constructor for `TCPConnectAuth`: ```pony -new create(from: (AmbientAuth | NetAuth | TCPAuth)) +--8<-- "derived-authority-authority-hierarchies.pony:22:22" ``` you might notice that this looks like a hierarchy of authorities: @@ -120,9 +71,7 @@ where in this paragraph, ">>" means "grants at least as much authority as". In f This hierarchy is established by means of the constructor of the weaker authority accepting one of the stronger authorities, for example: ```pony -primitive TCPAuth - new create(from: (AmbientAuth | NetAuth)) => - None +--8<-- "derived-authority-authority-hierarchies.pony:13:15" ``` Where `TCPAuth` grants less authority than `NetAuth`. `NetAuth` can be used to create any of the derived authorities `DNSAuth`, `UDPAuth`, `TCPAuth`, `TCPListenAuth`, `TCPConnectAuth` whereas `TCPAuth` can only be used to derive `TCPListenAuth` and `TCPConnectAuth`. diff --git a/docs/packages/use-statement.md b/docs/packages/use-statement.md index 3dcbf407..9c337c08 100644 --- a/docs/packages/use-statement.md +++ b/docs/packages/use-statement.md @@ -5,7 +5,7 @@ To use a package in your code you need to have a __use__ command. This tells the Use commands are a similar concept to Python and Java "import", C/C++ "#include" and C# "using" commands, but not exactly the same. They come at the beginning of Pony files and look like this: ```pony -use "collections" +--8<-- "use-statement-collections.pony" ``` This will find all of the publicly visible types defined in the _collections_ package and add them to the type namespace of the file containing the `use` command. These types are then available to use within that file, just as if they were defined locally. @@ -13,18 +13,13 @@ This will find all of the publicly visible types defined in the _collections_ pa For example, the standard library contains the package _time_. This contains the following definition (among others): ```pony -primitive Time - fun now(): (I64, I64) +--8<-- "use-statement-time.pony" ``` To access the _now_ function just add a use command: ```pony -use "time" - -class Foo - fun f() => - (var secs, var nsecs) = Time.now() +--8<-- "use-statement-time-now.pony" ``` ## Use names @@ -32,18 +27,7 @@ class Foo As we saw above the use command adds all the public types from a package into the namespace of the using file. This means that using a package may define type names that you want to use for your own types. Furthermore, if you use two packages within a file they may both define the same type name, causing a clash in your namespace. For example: ```pony -// In package A -class Foo - -// In package B -class Foo - -// In your code -use "packageA" -use "packageB" - -class Bar - var _x: Foo +--8<-- "use-statement-use-names-conflict.pony" ``` The declarations of _x is an error because we don't know which `Foo` is being referred to. Actually using 'Foo' is not even required, simply using both `packageA` and `packageB` is enough to cause an error here. @@ -51,37 +35,13 @@ The declarations of _x is an error because we don't know which `Foo` is being re To avoid this problem the use command allows you to specify an alias. If you do this then only that alias is put into your namespace. The types from the used package can then be accessed using this alias as a qualifier. Our example now becomes: ```pony -// In package A -class Foo - -// In package B -class Foo - -// In your code -use a = "packageA" -use b = "packageB" - -class Bar - var _x: a.Foo // The Foo from package A - var _y: b.Foo // The Foo from package B +--8<-- "use-statement-use-names-resolution.pony" ``` If you prefer you can give an alias to only one of the packages. `Foo` will then still be added to your namespace referring to the unaliased package: ```pony -// In package A -class Foo - -// In package B -class Foo - -// In your code -use "packageA" -use b = "packageB" - -class Bar - var _x: Foo // The Foo from package A - var _y: b.Foo // The Foo from package B +--8<-- "use-statement-use-names-resolution-alternative.pony" ``` __Can I just specify the full package path and forget about the use command, like I do in Java and C#?__ No, you can't do that in Pony. You can't refer to one package based on a `use` command for another package and you can't use types from a package without a use command for that package. Every package that you want to use must have its own `use` command. @@ -95,15 +55,13 @@ The string we give to a `use` command is known as the _specifier_. This consists The following two use commands are exactly equivalent: ```pony -use "foo" -use "package:foo" +--8<-- "use-statement-scheme-indicators-optional-package-scheme-specifier.pony" ``` If you are using a locator string that includes a colon, for example, an absolute path in Windows, then you __have__ to include the "package" scheme specifier: ```pony -use "C:/foo/bar" // Error, scheme "C" is unknown -use "package:C:/foo/bar" // OK +--8<-- "use-statement-scheme-indicators-required-package-scheme-specifier.pony" ``` To allow use commands to be portable across operating systems, and to avoid confusion with escape characters, '/' should always be used as the path separator in use commands, even on Windows. diff --git a/docs/reference-capabilities/aliasing.md b/docs/reference-capabilities/aliasing.md index ecd2a3b7..98f0dd85 100644 --- a/docs/reference-capabilities/aliasing.md +++ b/docs/reference-capabilities/aliasing.md @@ -11,8 +11,7 @@ In Pony, that works for some reference capabilities, but not all. The reason for this is that the `iso` reference capability denies other `iso` variables that point to the same object. That is, you can only have one `iso` variable pointing to any given object. The same goes for `trn`. ```pony -fun test(a: Wombat iso) => - var b: Wombat iso = a // Not allowed! +--8<-- "aliasing-multiple-references-to-an-iso-object.pony" ``` Here we have some function that gets passed an isolated Wombat. If we try to alias `a` by assigning it to `b`, we'll be breaking reference capability guarantees, so the compiler will stop us. Instead, we can only store aliases that are compatible with the original capability. @@ -20,15 +19,13 @@ Here we have some function that gets passed an isolated Wombat. If we try to ali __What can I alias an `iso` as?__ Since an `iso` says no other variable can be used by _any_ actor to read from or write to that object, we can only create aliases to an `iso` that can neither read nor write. Fortunately, we have a reference capability that does exactly that: `tag`. So we can do this and the compiler will be happy: ```pony -fun test(a: Wombat iso) => - var b: Wombat tag = a // Allowed! +--8<-- "aliasing-iso-to-tag.pony" ``` __What about aliasing `trn`?__ Since a `trn` says no other variable can be used by _any_ actor to write to that object, we need something that doesn't allow writing but also doesn't prevent our `trn` variable from writing. Fortunately, we have a reference capability that does that too: `box`. So we can do this and the compiler will be happy: ```pony -fun test(a: Wombat trn) => - var b: Wombat box = a // Allowed! +--8<-- "aliasing-trn-to-box.pony" ``` __What about aliasing other stuff?__ For both `iso` and `trn`, the guarantees require that aliases must give up on some ability (reading and writing for `iso`, writing for `trn`). For the other capabilities (`ref`, `val`, `box` and `tag`), aliases allow for the same operations, so such a reference can just be aliased as itself. @@ -50,8 +47,7 @@ Occasionally we'll want to talk about the type of an alias generically. An alias We indicate an alias type by putting a `!` at the end. Here's an example: ```pony -fun test(a: A) => - var b: A! = a +--8<-- "aliasing-alias-types.pony" ``` Here, we're using `A` as a __type variable__, which we'll cover later. So `A!` means "an alias of whatever type `A` is". We can also use it to talk about capabilities: we could have written the statements about `iso` and `trn` as just `iso!` = `tag` and `trn!` = `box`. @@ -63,8 +59,7 @@ In Pony, every expression has a type. So what's the type of `consume a`? It's no To show a type is ephemeral, we put a `^` at the end. For example: ```pony -fun test(a: Wombat iso): Wombat iso^ => - consume a +--8<-- "aliasing-ephemeral-types.pony" ``` Here, our function takes an isolated Wombat as a parameter and returns an ephemeral isolated Wombat. diff --git a/docs/reference-capabilities/arrow-types.md b/docs/reference-capabilities/arrow-types.md index 7ba8c8fa..36fc120f 100644 --- a/docs/reference-capabilities/arrow-types.md +++ b/docs/reference-capabilities/arrow-types.md @@ -9,10 +9,7 @@ When that happens, we can write a __viewpoint adapted type__, which we call an _ A function with a `box` receiver can be called with a `ref` receiver or a `val` receiver as well since those are both subtypes of `box`. Sometimes, we want to be able to talk about a type to take this into account. For example: ```pony -class Wombat - var _friend: Wombat - - fun friend(): this->Wombat => _friend +--8<-- "arrow-types-this.pony" ``` Here, we have a `Wombat`, and every `Wombat` has a friend that's also a `Wombat` (lucky `Wombat`). In fact, it's a `Wombat ref`, since `ref` is the default reference capability for a `Wombat` (since we didn't specify one). We also have a function that returns that friend. It's got a `box` receiver (because `box` is the default receiver reference capability for a function if we don't specify it). @@ -32,7 +29,7 @@ We haven't covered generics yet, so this may seem a little weird. We'll cover th Another time we don't know the precise reference capability of something is if we are using a type parameter. Here's an example from the standard library: ```pony -class ListValues[A, N: ListNode[A] box] is Iterator[N->A] +--8<-- "arrow-types-type-parameter.pony" ``` Here, we have a `ListValues` type that has two type parameters, `A` and `N`. In addition, `N` has a constraint: it has to be a subtype of `ListNode[A] box`. That's all fine and well, but we also say the `ListValues[A, N]` provides `Iterator[N->A]`. That's the interesting bit: we provide an interface that let's us iterate over values of the type `N->A`. @@ -46,9 +43,7 @@ There's one more way we use arrow types, and it's also related to generics. Some In other words, the unknown type will be a subtype of `box`, but that's all we know. Here's an example from the standard library: ```pony -interface Comparable[A: Comparable[A] box] - fun eq(that: box->A): Bool => this is that - fun ne(that: box->A): Bool => not eq(that) +--8<-- "arrow-types-box.pony" ``` Here, we say that something is `Comparable[A]` if and only if it has functions `eq` and `ne` and those functions have a single parameter of type `box->A` and return a `Bool`. In other words, whatever `A` is bound to, we only need to be able to read it. diff --git a/docs/reference-capabilities/consume-and-destructive-read.md b/docs/reference-capabilities/consume-and-destructive-read.md index 5fcaef7a..f5dd126a 100644 --- a/docs/reference-capabilities/consume-and-destructive-read.md +++ b/docs/reference-capabilities/consume-and-destructive-read.md @@ -9,16 +9,13 @@ Sometimes, you want to _move_ an object from one variable to another. In other w You can do this by using `consume`. When you `consume` a variable you take the value out of it, effectively leaving the variable empty. No code can read from that variable again until a new value is written to it. Consuming a local variable or a parameter allows you to move it to a new location, most importantly for `iso` and `trn`. ```pony -fun test(a: Wombat iso) => - var b: Wombat iso = consume a // Allowed! +--8<-- "consume-and-destructive-read-consuming-a-variable.pony:1:2" ``` The compiler is happy with that because by consuming `a`, you've said the value can't be used again and the compiler will complain if you try to. ```pony -fun test(a: Wombat iso) => - var b: Wombat iso = consume a // Allowed! - var c: Wombat tag = a // Not allowed! +--8<-- "consume-and-destructive-read-consuming-a-variable.pony" ``` Here's an example of that. When you try to assign `a` to `c`, the compiler will complain. @@ -26,8 +23,7 @@ Here's an example of that. When you try to assign `a` to `c`, the compiler will By default, a `consume` expression returns a type with the capability of the variable that you are assigning to. You can see this in the example above, where we say that `b` is `Wombat iso`, and as such the result of the `consume` expression is `Wombat iso`. We could also have said that `b` is a `Wombat val`, but we can instead give an explicit reference capability to the `consume` expression: ```pony -fun test(a: AnIncrediblyLongTypeName iso) => - var b = consume val a +--8<-- "consume-and-destructive-read-consuming-a-variable-and-change-its-reference-capability.pony" ``` The expression in line 2 of the example above is equivalent to saying `var b: AnIncrediblyLongTypeName val = consume a`. @@ -39,14 +35,7 @@ __Can I `consume` a field?__ Definitely not! Consuming something means it is emp There's another way to _move_ a value from one name to another. Earlier, we talked about how assignment in Pony returns the _old_ value of the left-hand side, rather than the new value. This is called _destructive read_, and we can use it to do what we want to do, even with fields. ```pony -class Aardvark - var buddy: Wombat iso - - new create() => - buddy = recover Wombat end - - fun ref test(a: Wombat iso) => - var b: Wombat iso = buddy = consume a // Allowed! +--8<-- "consume-and-destructive-read-moving-a-value.pony" ``` Here, we consume `a`, assign it to the field `buddy`, and assign the _old_ value of `buddy` to `b`. diff --git a/docs/reference-capabilities/recovering-capabilities.md b/docs/reference-capabilities/recovering-capabilities.md index 8192267b..842d1e11 100644 --- a/docs/reference-capabilities/recovering-capabilities.md +++ b/docs/reference-capabilities/recovering-capabilities.md @@ -15,7 +15,7 @@ This most straightforward use of `recover` is to get an `iso` that you can pass The `recover` expression wraps a list of expressions and is terminated by an `end`, like this: ```pony -recover Array[String].create() end +--8<-- "recovering-capabilities-ref-to-iso.pony" ``` This expression returns an `Array[String] iso`, instead of the usual `Array[String] ref` you would get. The reason it is `iso` and not any of the other mutable reference capabilities is because there is a default reference capability when you don't specify one. The default for any mutable reference capability is `iso` and the default for any immutable reference capability is `val`. @@ -23,27 +23,7 @@ This expression returns an `Array[String] iso`, instead of the usual `Array[Stri Here's a more complicated example from the standard library: ```pony -recover - var s = String((prec + 1).max(width.max(31))) - var value = x - - try - if value == 0 then - s.push(table(0)?) - else - while value != 0 do - let index = ((value = value / base) - (value * base)) - s.push(table(index.usize())?) - end - end - end - - _extend_digits(s, prec') - s.append(typestring) - s.append(prestring) - _pad(s, width, align, fill) - s -end +--8<-- "recovering-capabilities-format-int.pony" ``` 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`. @@ -51,7 +31,7 @@ That's from `format/_FormatInt`. It creates a `String ref`, does a bunch of stuf You can also give an explicit reference capability: ```pony -let key = recover val line.substring(0, i).>strip() end +--8<-- "recovering-capabilities-with-explicit-reference-capability.pony" ``` That's from `net/http/_PayloadBuilder`. We get a substring of `line`, which is a `String iso^`, then we call strip on it, which returns itself. But since strip is a `ref` function, it returns itself as a `String ref^` - so we use a `recover val` to end up with a `String val`. @@ -75,8 +55,7 @@ Notice that this technique looks mostly at the call-site, rather than at the def This may sound a little complicated, but in practice, it means you can write code that treats an `iso` mostly like a `ref`, and the compiler will complain when it's wrong. For example: ```pony -let s = recover String end -s.append("hi") +--8<-- "recovering-capabilities-string-append.pony" ``` Here, we create a `String iso` and then append some text to it. The append method takes a `ref` receiver and a `box` parameter. We can automatically recover the `iso` receiver since we pass a `val` parameter, so everything is fine. diff --git a/docs/reference-capabilities/reference-capabilities.md b/docs/reference-capabilities/reference-capabilities.md index 9e80a601..011a4958 100644 --- a/docs/reference-capabilities/reference-capabilities.md +++ b/docs/reference-capabilities/reference-capabilities.md @@ -80,25 +80,19 @@ Note that if you have a variable referring to an actor then you can send message A reference capability comes at the end of a type. So, for example: ```pony -String iso // An isolated string -String trn // A transition string -String ref // A string reference -String val // A string value -String box // A string box -String tag // A string tag +--8<-- "reference-capabilities-string-capabilities.pony" ``` __What does it mean when a type doesn't specify a reference capability?__ It means you are using the _default_ reference capability for that type, which is defined along with the type. Here’s an example from the standard library: ```pony -class val String +--8<-- "reference-capabilities-string-default.pony" ``` When we use a String we usually mean an immutable string value, so we make `val` the default reference capability for `String` (but not necessarily for `String` constructors, see below). For example, when we don't specify the capability in the following code, the compiler understands that we are using the default reference capability `val` specified in the type definition: ```pony -let a: String val = "Hello, world!" -let b: String = "I'm a wombat!" // Also a String val +--8<-- "reference-capabilities-default-vs-explicit.pony" ``` __So do I have to specify a reference capability when I define a type?__ Only if you want to. There are sensible defaults that most types will use. These are `ref` for classes, `val` for primitives (i.e. immutable references), and `tag` for actors. @@ -108,25 +102,14 @@ __So do I have to specify a reference capability when I define a type?__ Only if When you write a constructor, by default, that constructor will either create a new object with `ref` or `tag` as the capability. In the case of actors, the constructor will always create a `tag`. For classes, it defaults to `ref` but you can create with other capabilities. Let's take a look at an example: ```pony -class Foo - let x: U32 - - new val create(x': U32) => - x = x' +--8<-- "reference-capability-specificy-a-capability-other-than-the-default.pony" ``` Now when you call `Foo.create(1)`, you'll get a `Foo val` instead of `Foo ref`. But what if you want to create both `val` and `ref` `Foo`s? You could do something like this: ```pony -class Foo - let x: U32 - - new val create_val(x': U32) => - x = x' - - new ref create_ref(x': U32) => - x = x' +--8<-- "reference-capabilities-constructors-for-different-capabilities.pony" ``` But, that's probably not what you'd really want to do. Better to use the capabilities recovery facilities of Pony that we'll cover later in the [Recovering Capabilities](/reference-capabilities/recovering-capabilities.md) section. diff --git a/docs/testing/ponycheck.md b/docs/testing/ponycheck.md index cfb3ed3c..fc01ec8f 100644 --- a/docs/testing/ponycheck.md +++ b/docs/testing/ponycheck.md @@ -19,17 +19,7 @@ PonyCheck is heavily inspired by QuickCheck and other great property based testi Writing property based tests in PonyCheck is done by implementing the trait [`Property1`](https://stdlib.ponylang.io/pony_check-Property1). A [`Property1`](https://stdlib.ponylang.io/pony_check-Property1) needs to define a type parameter for the type of the input sample, a [`Generator`](https://stdlib.ponylang.io/pony_check-Generator) and a property function. Here is a minimal example: ```pony -use "pony_test" - -class _MyFirstProperty is Property1[String] - fun name(): String => - "my_first_property" - - fun gen(): Generator[String] => - Generators.ascii() - - fun property(arg1: String, ph: PropertyHelper) => - ph.assert_eq[String](arg1, arg1) +--8<-- "ponycheck-usage.pony" ``` A `Property1` needs a name for identification in test output. We created a `Generator` by using one of the many convenience factory methods and combinators defined in the [`Generators`](https://stdlib.ponylang.io/pony_check-Generators) primitive and we used [`PropertyHelper`](https://stdlib.ponylang.io/pony_check-PropertyHelper) to assert on a condition that should hold for all samples @@ -37,26 +27,7 @@ A `Property1` needs a name for identification in test output. We created a `Gene Below are two classic list reverse properties from the QuickCheck paper adapted to Pony arrays: ```pony -use "pony_check" -use "collections" - -class _ListReverseProperty is Property1[Array[USize]] - fun name(): String => "list/reverse" - - fun gen(): Generator[Array[USize]] => - Generators.seq_of[USize, Array[USize]](Generators.usize()) - - fun property(arg1: Array[USize], ph: PropertyHelper) => - ph.assert_array_eq[USize](arg1, arg1.reverse().reverse()) - -class _ListReverseOneProperty is Property1[Array[USize]] - fun name(): String => "list/reverse/one" - - fun gen(): Generator[Array[USize]] => - Generators.seq_of[USize, Array[USize]](Generators.usize() where min = 1, max = 1) - - fun property(arg1: Array[USize], ph: PropertyHelper) => - ph.assert_array_eq[USize](arg1, arg1.reverse()) +--8<-- "ponycheck-usage-quickcheck.pony" ``` ## Integration with PonyTest @@ -64,35 +35,14 @@ class _ListReverseOneProperty is Property1[Array[USize]] PonyCheck properties need to be executed. The test runner for PonyCheck is [PonyTest](https://stdlib.ponylang.io/pony_test--index). To integrate [`Property1`](https://stdlib.ponylang.io/pony_check-Property1) into [PonyTest](https://stdlib.ponylang.io/pony_test--index), `Property1` needs to be wrapped inside a [`Property1UnitTest`](https://stdlib.ponylang.io/pony_check-Property1UnitTest) and passed to the PonyTest `apply` method as a regular PonyTest [`UnitTest`](https://stdlib.ponylang.io/pony_test-UnitTest): ```pony -use "pony_test" -use "pony_check" - -actor Main is TestList - new create(env: Env) => - PonyTest(env, this) - - fun tag tests(test: PonyTest) => - test(Property1UnitTest[String](_MyFirstProperty)) +--8<-- "ponycheck-ponytest.pony" ``` It is also possible to integrate any number of properties directly into one [`UnitTest`](https://stdlib.ponylang.io/pony_test-UnitTest) using the [`PonyCheck.for_all`](https://stdlib.ponylang.io/pony_check-PonyCheck) convenience function: ```pony -class _ListReverseProperties is UnitTest - fun name(): String => "list/properties" - - fun apply(h: TestHelper) ? => - let gen1 = Generators.seq_of[USize, Array[USize]](Generators.usize()) - PonyCheck.for_all[Array[USize]](gen1, h)({ - (arg1: Array[USize], ph: PropertyHelper) => - ph.assert_array_eq[USize](arg1, arg1.reverse().reverse()) - }) - let gen2 = Generators.seq_of[USize, Array[USize]](1, Generators.usize()) - PonyCheck.for_all[Array[USize]](gen2, h)({ - (arg1: Array[USize], ph: PropertyHelper) => - ph.assert_array_eq[USize](arg1, arg1.reverse()) - }) +--8<-- "ponycheck-ponytest-for-all.pony" ``` ## Additional resources diff --git a/docs/testing/ponytest.md b/docs/testing/ponytest.md index 04542503..bf996a3d 100644 --- a/docs/testing/ponytest.md +++ b/docs/testing/ponytest.md @@ -15,30 +15,7 @@ To use PonyTest simply write a class for each test and a `TestList` type that te The following is a complete program with 2 trivial tests. ```pony -use "pony_test" - -actor Main is TestList - new create(env: Env) => - PonyTest(env, this) - - new make() => - None - - fun tag tests(test: PonyTest) => - test(_TestAdd) - test(_TestSub) - -class iso _TestAdd is UnitTest - 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 apply(h: TestHelper) => - h.assert_eq[U32](2, 4 - 2) +--8<-- "ponytest-example.pony" ``` The make() constructor is not needed for this example. However, it allows for easy aggregation of tests (see below) so it is recommended that all test Mains provide it. @@ -58,20 +35,7 @@ Often it is desirable to run a collection of unit tests from multiple different This can be achieved by writing an aggregate test list class, which calls the list function for each package. The following is an example that aggregates the tests from packages `foo` and `bar`. ```pony -use "pony_test" -use foo = "foo" -use bar = "bar" - -actor Main is TestList - new create(env: Env) => - PonyTest(env, this) - - new make() => - None - - fun tag tests(test: PonyTest) => - foo.Main.make().tests(test) - bar.Main.make().tests(test) +--8<-- "ponytest-aggregation.pony" ``` Aggregate test classes may themselves be aggregated. Every test list class may contain any combination of its own tests and aggregated lists. diff --git a/docs/types/actors.md b/docs/types/actors.md index f53defc2..eac21ca5 100644 --- a/docs/types/actors.md +++ b/docs/types/actors.md @@ -15,15 +15,7 @@ Like a function, a behaviour can have parameters. Unlike a function, it doesn't __So what does a behaviour return?__ Behaviours always return `None`, like a function without explicit result type, because they can't return something they calculate (since they haven't run yet). ```pony -actor Aardvark - let name: String - var _hunger_level: U64 = 0 - - new create(name': String) => - name = name' - - be eat(amount: U64) => - _hunger_level = _hunger_level - amount.min(_hunger_level) +--8<-- "actors-behaviors.pony" ``` Here we have an `Aardvark` that can eat asynchronously. Clever Aardvark. @@ -47,13 +39,7 @@ When you're writing Pony code, it's nice to think of actors not as a unit of par In the example below, the `Main` actor calls a behaviour `call_me_later` which, as we know, is _asynchronous_, so we won't wait for it to run before continuing. Then, we run the method `env.out.print`, which is _also asynchronous_, and will print the provided text to the terminal. Now that we've finished executing code inside the `Main` actor, the behaviour we've called earlier will eventually run, and it will print the last text. ```pony -actor Main - new create(env: Env) => - call_me_later(env) - env.out.print("This is printed first") - - be call_me_later(env: Env) => - env.out.print("This is printed last") +--8<-- "actors-sequential.pony" ``` Since all this code runs inside the same actor, and the calls to the other behaviour `env.out.print` are sequential as well, we are guaranteed that `"This is printed first"` is always printed __before__ `"This is printed last"`. diff --git a/docs/types/classes.md b/docs/types/classes.md index 40f84a18..be9f22dd 100644 --- a/docs/types/classes.md +++ b/docs/types/classes.md @@ -3,7 +3,7 @@ Just like other object-oriented languages, Pony has __classes__. A class is declared with the keyword `class`, and it has to have a name that starts with a capital letter, like this: ```pony -class Wombat +--8<-- "classes-wombat.pony:1:1" ``` __Do all types start with a capital letter?__ Yes! And nothing else starts with a capital letter. So when you see a name in Pony code, you will instantly know whether it's a type or not. @@ -21,9 +21,7 @@ A class is composed of: These are just like fields in C structures or fields in classes in C++, C#, Java, Python, Ruby, or basically any language, really. There are three kinds of fields: `var`, `let`, and `embed` fields. A `var` field can be assigned to over and over again, but a `let` field is assigned to in the constructor and never again. Embed fields will be covered in more detail in the documentation on [variables](/expressions/variables.md). ```pony -class Wombat - let name: String - var _hunger_level: U64 +--8<-- "classes-wombat.pony:1:3" ``` Here, a `Wombat` has a `name`, which is a `String`, and a `_hunger_level`, which is a `U64` (an unsigned 64-bit integer). @@ -37,24 +35,13 @@ Pony constructors have names. Other than that, they are just like constructors i Constructors are introduced with the __new__ keyword. ```pony -class Wombat - let name: String - var _hunger_level: U64 - - new create(name': String) => - name = name' - _hunger_level = 0 - - new hungry(name': String, hunger': U64) => - name = name' - _hunger_level = hunger' +--8<-- "classes-wombat-constructors.pony" ``` Here, we have two constructors, one that creates a `Wombat` that isn't hungry, and another that creates a `Wombat` that might be hungry or might not. Unlike some other languages that differentiate between constructors with method overloading, Pony won't presume to know which alternate constructor to invoke based on the arity and type of your arguments. To choose a constructor, invoke it like a method with the `.` syntax: ```pony -let defaultWombat = Wombat("Fantastibat") // Invokes the create method by default -let hungryWombat = Wombat.hungry("Nomsbat", 12) // Invokes the hungry method +--8<-- "classes-wombat-constructor-invocation.pony" ``` __What's with the single quote thing, i.e. name'?__ You can use single quotes in parameter and local variable names. In mathematics, it's called a _prime_, and it's used to say "another one of these, but not the same one". Basically, it's just convenient. @@ -64,18 +51,7 @@ Every constructor has to set every field in an object. If it doesn't, the compil Sometimes it's convenient to set a field the same way for all constructors. ```pony -class Wombat - let name: String - var _hunger_level: U64 - var _thirst_level: U64 = 1 - - new create(name': String) => - name = name' - _hunger_level = 0 - - new hungry(name': String, hunger': U64) => - name = name' - _hunger_level = hunger' +--8<-- "classes-wombat.pony:1:12" ``` Here, every `Wombat` begins a little bit thirsty, regardless of which constructor is called. @@ -83,14 +59,7 @@ Here, every `Wombat` begins a little bit thirsty, regardless of which constructo ### Zero Argument Constructors ```pony -class Hawk - var _hunger_level: U64 = 0 - -class Owl - var _hunger_level: U64 - - new create() => - _hunger_level = 42 +--8<-- "classes-zero-argument-constructors.pony" ``` Here we have two classes, because the `Hawk` class defines no constructors, a default constructor with zero arguments called `create` is generated. The `Owl` defines its own constructor that sets the `_hunger_level`. @@ -98,9 +67,7 @@ Here we have two classes, because the `Hawk` class defines no constructors, a de When constructing instances of classes that have zero-argument constructors, they can be constructed with just the class name: ```pony -class Forest - let _owl: Owl = Owl - let _hawk: Hawk = Hawk +--8<-- "classes-zero-argument-constructors-invocation.pony" ``` This is explained later, in more detail in the [sugar](/expressions/sugar.md) section. @@ -110,22 +77,7 @@ This is explained later, in more detail in the [sugar](/expressions/sugar.md) se Functions in Pony are like methods in Java, C#, C++, Ruby, Python, or pretty much any other object oriented language. They are introduced with the keyword `fun`. They can have parameters like constructors do, and they can also have a result type (if no result type is given, it defaults to `None`). ```pony -class Wombat - let name: String - var _hunger_level: U64 - var _thirst_level: U64 = 1 - - new create(name': String) => - name = name' - _hunger_level = 0 - - new hungry(name': String, hunger': U64) => - name = name' - _hunger_level = hunger' - - fun hunger(): U64 => _hunger_level - - fun ref set_hunger(to: U64 = 0): U64 => _hunger_level = to +--8<-- "classes-wombat.pony" ``` The first function, `hunger`, is pretty straight forward. It has a result type of `U64`, and it returns `_hunger_level`, which is a `U64`. The only thing a bit different here is that no `return` keyword is used. This is because the result of a function is the result of the last expression in the function, in this case, the value of `_hunger_level`. @@ -155,15 +107,13 @@ __Wait, seriously? The _old_ value?__ Yes. In Pony, assignment is an expression __...why?__ It's called a "destructive read", and it lets you do awesome things with a capabilities-secure type system. We'll talk about that later. For now, we'll just mention that you can also use it to implement a _swap_ operation. In most languages, to swap the values of `a` and `b` you need to do something like: ```pony -var temp = a -a = b -b = temp +--8<-- "classes-swap-values.pony" ``` In Pony, you can just do: ```pony -a = b = a +--8<-- "classes-swap-values-sugar.pony" ``` ### Finalisers diff --git a/docs/types/primitives.md b/docs/types/primitives.md index 286ef53a..aa5090ca 100644 --- a/docs/types/primitives.md +++ b/docs/types/primitives.md @@ -16,30 +16,7 @@ There are three main uses of primitives (four, if you count built-in "machine wo 3. As a "collection of functions". Since primitives can have functions, you can group functions together in a primitive type. You can see this in the standard library, where path handling functions are grouped in the __primitive__ `Path`, for example. ```pony -// 2 "marker values" -primitive OpenedDoor -primitive ClosedDoor - -// An "enumeration" type -type DoorState is (OpenedDoor | ClosedDoor) - -// A collection of functions -primitive BasicMath - fun add(a: U64, b: U64): U64 => - a + b - - fun multiply(a: U64, b: U64): U64 => - a * b - -actor Main - new create(env: Env) => - let doorState : DoorState = ClosedDoor - let isDoorOpen : Bool = match doorState - | OpenedDoor => true - | ClosedDoor => false - end - env.out.print("Is door open? " + isDoorOpen.string()) - env.out.print("2 + 3 = " + BasicMath.add(2,3).string()) +--8<-- "primitives-doors.pony" ``` Primitives are quite powerful, particularly as enumerations. Unlike enumerations in other languages, each "value" in the enumeration is a complete type, which makes attaching data and functionality to enumeration values easy. diff --git a/docs/types/structs.md b/docs/types/structs.md index 916b4763..5ef0481f 100644 --- a/docs/types/structs.md +++ b/docs/types/structs.md @@ -23,13 +23,7 @@ Pony struct fields are defined in the same way as they are for Pony classes, usi For example: ```pony -struct Inner - var x: I32 = 0 - -struct Outer - embed inner_embed: Inner = Inner - var inner_var: Inner = Inner - +--8<-- "structs-fields.pony" ``` ### Constructors @@ -37,23 +31,7 @@ struct Outer Struct constructors, like class constructors, have names. Everything you previously learned about Pony class constructors applies to struct constructors. ```pony -struct Pointer[A] - """ - A Pointer[A] is a raw memory pointer. It has no descriptor and thus can't be - included in a union or intersection, or be a subtype of any interface. Most - functions on a Pointer[A] are private to maintain memory safety. - """ - new create() => - """ - A null pointer. - """ - compile_intrinsic - - new _alloc(len: USize) => - """ - Space for len instances of A. - """ - compile_intrinsic +--8<-- "structs-constructors.pony" ``` Here we have two constructors. One that creates a new null Pointer, and another creates a Pointer with space for many instances of the type the Pointer is pointing at. Don't worry if you don't follow everything you are seeing in the above example. The important part is, it should basically look like the class constructor example [we saw earlier](/types/classes.md#what-goes-in-a-class). diff --git a/docs/types/traits-and-interfaces.md b/docs/types/traits-and-interfaces.md index a71df3c8..a78baddd 100644 --- a/docs/types/traits-and-interfaces.md +++ b/docs/types/traits-and-interfaces.md @@ -15,7 +15,7 @@ The core idea is that you have a type that declares it has a relationship to som In Pony, nominal subtyping is done via the keyword `is`. `is` declares at the point of declaration that an object has a relationship to a category type. For example, to use nominal subtyping to declare that the class `Name` provides `Stringable`, you'd do: ```pony -class Name is Stringable +--8<-- "traits-and-interfaces-nominal-subtyping.pony" ``` ## Structural subtyping @@ -29,17 +29,7 @@ Structural typing is very similar to [duck typing](https://en.wikipedia.org/wiki You do not declare structural relationships ahead of time, instead it is done by checking if a given concrete type can fulfill the required interface. For example, in the code below, we have the interface `Stringable` from the standard library. Anything can be used as a `Stringable` so long as it provides the method `fun string(): String iso^`. In our example below, `ExecveError` provides the `Stringable` interface and can be used anywhere a `Stringable` is needed. Because `Stringable` is a structural type, `ExecveError` doesn't have to declare a relationship to `Stringable`, it simply has that relationship because it has "the same shape". ```pony -interface box Stringable - """ - Things that can be turned into a String. - """ - fun string(): String iso^ - """ - Generate a string representation of this object. - """ - -primitive ExecveError - fun string(): String iso^ => "ExecveError".clone() +--8<-- "traits-and-interfaces-structural-subtyping.pony" ``` ## Nominal and structural subtyping in Pony @@ -53,10 +43,7 @@ Both `trait` and `interface` can establish a relationship via nominal subtyping. The primary means of doing nominal subtyping in Pony is using __traits__. A __trait__ looks a bit like a __class__, but it uses the keyword `trait` and it can't have any fields. ```pony -trait Named - fun name(): String => "Bob" - -class Bob is Named +--8<-- "traits-and-interfaces-trait.pony" ``` Here, we have a trait `Named` that has a single function `name` that returns a String. It also provides a default implementation of `name` that returns the string literal "Bob". @@ -66,32 +53,19 @@ We also have a class `Bob` that says it `is Named`. This means `Bob` is in the ` Since `Bob` doesn't have its own `name` function, it uses the one from the trait. If the trait's function didn't have a default implementation, the compiler would complain that `Bob` had no implementation of `name`. ```pony -trait Named - fun name(): String => "Bob" - -trait Bald - fun hair(): Bool => false - -class Bob is (Named & Bald) +--8<-- "traits-and-interfaces-multiple-traits.pony" ``` It is possible for a class to have relationships with multiple categories. In the above example, the class `Bob` _provides both Named and Bald_. ```pony -trait Named - fun name(): String => "Bob" - -trait Bald is Named - fun hair(): Bool => false - -class Bob is Bald +--8<-- "traits-and-interfaces-nested-traits.pony" ``` It is also possible to combine categories together. In the example above, all `Bald` classes are automatically `Named`. Consequently, the `Bob` class has access to both hair() and name() default implementation of their respective trait. One can think of the `Bald` category to be more specific than the `Named` one. ```pony -class Larry - fun name(): String => "Larry" +--8<-- "traits-and-interfaces-nominal-subtyping-in-pony.pony" ``` Here, we have a class `Larry` that has a `name` function with the same signature. But `Larry` does __not__ provide `Named`! @@ -101,13 +75,7 @@ __Wait, why not?__ Because `Larry` doesn't say it `is Named`. Remember, traits a You can also do nominal subtyping using the keyword `interface`. __Interfaces__ in Pony are primarily used for structural subtyping. Like traits, interfaces can also have default method implementations, but in order to use default method implementations, an interface must be used in a nominal fashion. For example: ```pony -interface HasName - fun name(): String => "Bob" - -class Bob is HasName - -class Larry - fun name(): String => "Larry" +--8<-- "traits-and-interfaces-nominal-and-structural-subtyping.pony" ``` Both `Bob` and `Larry` are in the category `HasName`. `Bob` because it has declared that it is a `HasName` and `Larry` because it is structurally a `HasName`. @@ -117,8 +85,7 @@ Both `Bob` and `Larry` are in the category `HasName`. `Bob` because it has decla Pony has structural subtyping using __interfaces__. Interfaces look like traits, but they use the keyword `interface`. ```pony -interface HasName - fun name(): String +--8<-- "traits-and-interfaces-structural-subtyping-in-pony.pony" ``` Here, `HasName` looks a lot like `Named`, except it's an interface instead of a trait. This means both `Bob` and `Larry` provide `HasName`! The programmers that wrote `Bob` and `Larry` don't even have to be aware that `HasName` exists. @@ -132,15 +99,7 @@ It is common for new Pony users to ask, __Should I use traits or interfaces in m A key difference between traits and interfaces is that interfaces can't have private methods. So, if you need private methods, you'll need to use a trait and have users opt in via nominal typing. Interfaces can't have private methods because otherwise, users could use them to break encapsulation and access private methods on concrete types from other packages. For example: ```pony -actor Main - new create(env: Env) => - let x: String ref = "sailor".string() - let y: Foo = x - y._set(0, 'f') - env.out.print("Hello, " + x) - -interface Foo - fun ref _set(i: USize, value: U8): U8 +--8<-- "traits-and-interfaces-private-methods.pony" ``` In the code above, the interface `Foo` allows access to the private `_set` method of `String` and allows for changing `sailor` to `failor` or it would anyway, if interfaces were allowed to have private methods. @@ -150,19 +109,13 @@ In the code above, the interface `Foo` allows access to the private `_set` metho Traits allow you to create "open world enumerations" that others can participate in. For example: ```pony -trait Color - -primitive Red is Color -primitive Blue is Color +--8<-- "traits-and-interfaces-open-world-enumerations.pony" ``` Here we are using a trait to provide a category of things, `Color`, that any other types can opt into by declaring itself to be a `Color`. This creates an "open world" of enumerations that you can't do using the more traditional Pony approach using type unions. ```pony -primitive Red -primitive Blue - -type Color is (Red | Blue) +--8<-- "traits-and-interfaces-type-union.pony" ``` In our trait based example, we can add new colors at any time. With the type union based approach, we can only add them by modifying definition of `Color` in the package that provides it. @@ -170,17 +123,13 @@ In our trait based example, we can add new colors at any time. With the type uni Interfaces can't be used for open world enumerations. If we defined `Color` as an interface: ```pony -interface Color +--8<-- "traits-and-interfaces-open-world-interface.pony" ``` Then literally everything in Pony would be a `Color` because everything matches the `Color` interface. You can however, do something similar using "marker methods" with an interface: ```pony -interface Color - fun is_color(): None - -primitive Red - fun is_color(): None => None +--8<-- "traits-and-interfaces-marker-methods.pony" ``` Here we are using structural typing to create a collection of things that are in the category `Color` by providing a method that "marks" being a member of the category: `is_color`. @@ -192,23 +141,7 @@ We've covered a couple ways that traits can be better than interfaces, let's clo Here's a contrived example: ```pony -interface Compactable - fun ref compact() - fun size(): USize - -class Compactor - """ - Compacts data structures when their size crosses a threshold - """ - let _threshold: USize - - new create(threshold: USize) => - _threshold = threshold - - fun ref try_compacting(thing: Compactable) => - if thing.size() > _threshold then - thing.compact() - end +--8<-- "traits-and-interfaces-open-world-typing.pony" ``` The flexibility of `interface` has allowed us to define a type `Compactable` that we can use to allow our `Compactor` to accept a variety of data types including `Array`, `Map`, and `String` from the standard library. diff --git a/docs/types/type-aliases.md b/docs/types/type-aliases.md index 8505f926..7b7fc9ad 100644 --- a/docs/types/type-aliases.md +++ b/docs/types/type-aliases.md @@ -9,11 +9,7 @@ We'll give a couple examples of using type aliases, just to get the feel of them One way to use type aliases is to express an enumeration. For example, imagine we want to say something must either be Red, Blue or Green. We could write something like this: ```pony -primitive Red -primitive Blue -primitive Green - -type Colour is (Red | Blue | Green) +--8<-- "type-aliases-enumerations.pony" ``` There are two new concepts in there. The first is the type alias, introduced with the keyword `type`. It just means that the name that comes after `type` will be translated by the compiler to the type that comes after `is`. @@ -26,31 +22,19 @@ You can also declare constants like in C or Go like this, making use of `apply`, which can be omitted during call (will be discussed further in [Sugar](/expressions/sugar.md)), ```pony -primitive Red fun apply(): U32 => 0xFF0000FF -primitive Green fun apply(): U32 => 0x00FF00FF -primitive Blue fun apply(): U32 => 0x0000FFFF - -type Colour is (Red | Blue | Green) +--8<-- "type-aliases-enumerations-apply.pony" ``` or namespace them like this ```pony -primitive Colours - fun red(): U32 => 0xFF0000FF - fun green(): U32 => 0x00FF00FF +--8<-- "type-aliases-enumerations-namespace.pony" ``` You might also want to iterate over the enumeration items like this to print their value for debugging purposes ```pony -primitive ColourList - fun apply(): Array[Colour] => - [Red; Green; Blue] - -for colour in ColourList().values() do - env.out.print(colour().string()) -end +--8<-- "type-aliases-enumerations-iteration.pony" ``` ## Complex types @@ -58,31 +42,13 @@ end If a type is complicated, it can be nice to give it a mnemonic name. For example, if we want to say that a type must implement more than one interface, we could say: ```pony -interface HasName - fun name(): String - -interface HasAge - fun age(): U32 - -interface HasFeelings - fun feeling(): String - -type Person is (HasName & HasAge & HasFeelings) +--8<-- "type-aliases-complex-types-interface.pony" ``` This use of complex types applies to traits, not just interfaces: ```pony -trait HasName - fun name(): String => "Bob" - -trait HasAge - fun age(): U32 => 42 - -trait HasFeelings - fun feeling(): String => "Great!" - -type Person is (HasName & HasAge & HasFeelings) +--8<-- "type-aliases-complex-types-trait.pony" ``` There's another new concept here: the type has a `&` in it. This is similar to the `|` of a __union__ type: it means this is an __intersection__ type. That is, it's something that must be _all_ of `HasName`, `HasAge` _and_ `HasFeelings`. @@ -92,7 +58,7 @@ But the use of `type` here is exactly the same as the enumeration example above, Another example, this time from the standard library, is `SetIs`. Here's the actual definition: ```pony -type SetIs[A] is HashSet[A, HashIs[A!]] +--8<-- "type-aliases-set-is.pony" ``` Again there's something new here. After the name `SetIs` comes the name `A` in square brackets. That's because `SetIs` is a __generic type__. That is, you can give a `SetIs` another type as a parameter, to make specific kinds of set. If you've used Java or C#, this will be pretty familiar. If you've used C++, the equivalent concept is templates, but they work quite differently. @@ -100,7 +66,7 @@ Again there's something new here. After the name `SetIs` comes the name `A` in s And again the use of `type` just provides a more convenient way to refer to the type we're aliasing: ```pony -HashSet[A, HashIs[A!]] +--8<-- "type-aliases-hash-set.pony" ``` That's another __generic type__. It means a `SetIs` is really a kind of `HashSet`. Another concept has snuck in, which is `!` types. This is a type that is the __alias__ of another type. That's tricky stuff that you only need when writing complex generic types, so we'll leave it for later. @@ -108,7 +74,7 @@ That's another __generic type__. It means a `SetIs` is really a kind of `HashSet One more example, again from the standard library, is the `Map` type that gets used a lot. It's actually a type alias. Here's the real definition of `Map`: ```pony -type Map[K: (Hashable box & Comparable[K] box), V] is HashMap[K, V, HashEq[K]] +--8<-- "type-aliases-map.pony" ``` Unlike our previous example, the first type parameter, `K`, has a type associated with it. This is a __constraint__, which means when you parameterise a `Map`, the type you pass for `K` must be a subtype of the constraint. diff --git a/docs/types/type-expressions.md b/docs/types/type-expressions.md index c4310e6c..39d56dd9 100644 --- a/docs/types/type-expressions.md +++ b/docs/types/type-expressions.md @@ -9,28 +9,25 @@ There are three kinds of type expression: __tuples__, __unions__, and __intersec A __tuple__ type is a sequence of types. For example, if we wanted something that was a `String` followed by a `U64`, we would write this: ```pony -var x: (String, U64) -x = ("hi", 3) -x = ("bye", 7) +--8<-- "type-expressions-tuple-declaration.pony" ``` All type expressions are written in parentheses, and the elements of a tuple are separated by a comma. We can also destructure a tuple using assignment: ```pony -(var y, var z) = x +--8<-- "type-expressions-tuple-destructuring.pony" ``` Or we can access the elements of a tuple directly: ```pony -var y = x._1 -var z = x._2 +--8<-- "type-expressions-tuple-direct-access.pony" ``` Note that there's no way to assign to an element of a tuple. Instead, you can just reassign the entire tuple, like this: ```pony -x = ("wombat", x._2) +--8<-- "type-expressions-tuple-reassignment.pony" ``` __Why use a tuple instead of a class?__ Tuples are a way to express a collection of values that doesn't have any associated code or expected behaviour. Basically, if you just need a quick collection of things, maybe to return more than one value from a function, for example, you can use a tuple. @@ -42,7 +39,7 @@ A __union__ type is written like a __tuple__, but it uses a `|` (pronounced "or" Unions can be used for tons of stuff that require multiple concepts in other languages. For example, optional values, enumerations, marker values, and more. ```pony -var x: (String | None) +--8<-- "type-expressions-union.pony" ``` Here we have an example of using a union to express an optional type, where `x` might be a `String`, but it also might be `None`. @@ -54,7 +51,7 @@ An __intersection__ uses a `&` (pronounced "and" when reading the type) between This can be very useful for combining traits or interfaces, for example. Here's something from the standard library: ```pony -type Map[K: (Hashable box & Comparable[K] box), V] is HashMap[K, V, HashEq[K]] +--8<-- "type-expressions-intersection.pony" ``` That's a fairly complex type alias, but let's look at the constraint of `K`. It's `(Hashable box & Comparable[K] box)`, which means `K` is `Hashable` _and_ it is `Comparable[K]`, at the same time. @@ -64,7 +61,7 @@ That's a fairly complex type alias, but let's look at the constraint of `K`. It' Type expressions can be combined into more complex types. Here's another example from the standard library: ```pony -var _array: Array[((K, V) | _MapEmpty | _MapDeleted)] +--8<-- "type-expressions-combined.pony" ``` Here we have an array where each element is either a tuple of `(K, V)` or a `_MapEmpty` or a `_MapDeleted`. @@ -72,13 +69,7 @@ Here we have an array where each element is either a tuple of `(K, V)` or a `_Ma Because every type expression has parentheses around it, they are actually easy to read once you get the hang of it. However, if you use a complex type expression often, it can be nice to provide a type alias for it. ```pony -type Number is (Signed | Unsigned | Float) - -type Signed is (I8 | I16 | I32 | I64 | I128) - -type Unsigned is (U8 | U16 | U32 | U64 | U128) - -type Float is (F32 | F64) +--8<-- "type-expressions-type-alias.pony" ``` Those are all type aliases used by the standard library. diff --git a/mkdocs.yml b/mkdocs.yml index a923d371..caed243a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,6 +22,9 @@ markdown_extensions: - name: mermaid class: mermaid-experimental format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.snippets: + base_path: ['code-samples'] + check_paths: true - smarty - toc: permalink: true