From 765384ba44d2a1938dd655c324db157b445bbf4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Sun, 21 Apr 2024 05:35:42 +0200 Subject: [PATCH 01/58] feat(config): Activate mkdocs snippets extension --- mkdocs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index 8d058037..2df019a3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,6 +22,8 @@ markdown_extensions: - name: mermaid class: mermaid-experimental format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.snippets + base_path: ['code-samples'] - smarty - toc: permalink: true From c1a8a22e2e9d52356cc42963368f528913339e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Sun, 21 Apr 2024 05:36:19 +0200 Subject: [PATCH 02/58] refactor(code-samples): Turn code samples into snippets --- code-samples/hello-world-compile.sh | 7 +++++++ code-samples/hello-world-create-directory.sh | 2 ++ code-samples/hello-world-main.pony | 3 +++ code-samples/hello-world-run.sh | 2 ++ ...at-you-need-compile-pony-other-directory.sh | 1 + code-samples/what-you-need-compile-pony.sh | 1 + .../what-you-need-run-python-shebang.sh | 1 + code-samples/what-you-need-run-python.sh | 1 + docs/getting-started/hello-world.md | 18 ++++-------------- docs/getting-started/how-it-works.md | 10 ++++------ docs/getting-started/what-you-need.md | 8 ++++---- 11 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 code-samples/hello-world-compile.sh create mode 100644 code-samples/hello-world-create-directory.sh create mode 100644 code-samples/hello-world-main.pony create mode 100644 code-samples/hello-world-run.sh create mode 100644 code-samples/what-you-need-compile-pony-other-directory.sh create mode 100644 code-samples/what-you-need-compile-pony.sh create mode 100644 code-samples/what-you-need-run-python-shebang.sh create mode 100644 code-samples/what-you-need-run-python.sh diff --git a/code-samples/hello-world-compile.sh b/code-samples/hello-world-compile.sh new file mode 100644 index 00000000..d02070a6 --- /dev/null +++ b/code-samples/hello-world-compile.sh @@ -0,0 +1,7 @@ +$ ponyc +Building . +Building builtin +Generating +Optimising +Writing ./helloworld.o +Linking ./helloworld \ No newline at end of file diff --git a/code-samples/hello-world-create-directory.sh b/code-samples/hello-world-create-directory.sh new file mode 100644 index 00000000..70d70180 --- /dev/null +++ b/code-samples/hello-world-create-directory.sh @@ -0,0 +1,2 @@ +mkdir helloworld +cd helloworld \ 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/hello-world-run.sh b/code-samples/hello-world-run.sh new file mode 100644 index 00000000..e72ed104 --- /dev/null +++ b/code-samples/hello-world-run.sh @@ -0,0 +1,2 @@ +$ ./helloworld +Hello, world! \ No newline at end of file diff --git a/code-samples/what-you-need-compile-pony-other-directory.sh b/code-samples/what-you-need-compile-pony-other-directory.sh new file mode 100644 index 00000000..fbe3a6f0 --- /dev/null +++ b/code-samples/what-you-need-compile-pony-other-directory.sh @@ -0,0 +1 @@ +ponyc path/to/my/code \ No newline at end of file diff --git a/code-samples/what-you-need-compile-pony.sh b/code-samples/what-you-need-compile-pony.sh new file mode 100644 index 00000000..f3fe9dd3 --- /dev/null +++ b/code-samples/what-you-need-compile-pony.sh @@ -0,0 +1 @@ +ponyc \ No newline at end of file diff --git a/code-samples/what-you-need-run-python-shebang.sh b/code-samples/what-you-need-run-python-shebang.sh new file mode 100644 index 00000000..cb693ca3 --- /dev/null +++ b/code-samples/what-you-need-run-python-shebang.sh @@ -0,0 +1 @@ +./helloworld.py \ No newline at end of file diff --git a/code-samples/what-you-need-run-python.sh b/code-samples/what-you-need-run-python.sh new file mode 100644 index 00000000..48920769 --- /dev/null +++ b/code-samples/what-you-need-run-python.sh @@ -0,0 +1 @@ +python helloworld.py \ No newline at end of file diff --git a/docs/getting-started/hello-world.md b/docs/getting-started/hello-world.md index cfd178de..f7690419 100644 --- a/docs/getting-started/hello-world.md +++ b/docs/getting-started/hello-world.md @@ -3,8 +3,7 @@ Now that you've successfully installed the Pony compiler, let's start programming! Our first program will be a very traditional one. We're going to print "Hello, world!". First, create a directory called `helloworld`: ```bash -mkdir helloworld -cd helloworld +--8<-- "hello-world-create-directory.sh" ``` __Does the name of the directory matter?__ Yes, it does. It's the name of your program! By default when your program is compiled, the resulting executable binary will have the same name as the directory your program lives in. You can also set the name using the --bin-name or -b options on the command line. @@ -18,9 +17,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 @@ -28,13 +25,7 @@ actor Main Now compile it: ```bash -$ ponyc -Building . -Building builtin -Generating -Optimising -Writing ./helloworld.o -Linking ./helloworld +--8<-- "hello-world-compile.sh" ``` (If you're using Docker, you'd write something like `$ docker run -v Some_Absolute_Path/helloworld:/src/main ponylang/ponyc`, depending of course on what the absolute path to your `helloworld` directory is.) @@ -48,8 +39,7 @@ __Wait, it linked too?__ Yes. You won't need a build system (like `make`) for Po Now we can run the program: ```bash -$ ./helloworld -Hello, world! +--8<-- "hello-world-run.sh" ``` Congratulations, you've written your first Pony program! Next, we'll explain what some of that code does. diff --git a/docs/getting-started/how-it-works.md b/docs/getting-started/how-it-works.md index b00ea407..e981592d 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: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/getting-started/what-you-need.md b/docs/getting-started/what-you-need.md index bf5c4151..ac97ba6c 100644 --- a/docs/getting-started/what-you-need.md +++ b/docs/getting-started/what-you-need.md @@ -19,13 +19,13 @@ What this means is that once you build your program, you can run it over and ove But it also means you need to build your program before you can run it. In an interpreted language or a JIT compiled language, you tend to do things like this to run your program: ```bash -python helloworld.py +--8<-- "what-you-need-run-python.sh" ``` Or maybe you put a __shebang__ in your program (like `#!/usr/bin/env python`), then `chmod` to set the executable bit, and then do: ```bash -./helloworld.py +--8<-- "what-you-need-run-python-shebang.sh" ``` When you use Pony, you don't do any of that! @@ -35,13 +35,13 @@ When you use Pony, you don't do any of that! If you are in the same directory as your program, you can just do: ```bash -ponyc +--8<-- "what-you-need-run-compile-pony.sh" ``` That tells the Pony compiler that your current working directory contains your source code, and to please compile it. If your source code is in some other directory, you can tell ponyc where it is: ```bash -ponyc path/to/my/code +--8<-- "what-you-need-compile-pony-other-directory.sh" ``` There are other options as well, but we'll cover those later. From 58e6599d8ff7559b6c938be8b200b1a18f18c0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Sun, 21 Apr 2024 06:08:14 +0200 Subject: [PATCH 03/58] fix(config): Add missing colon to mark array entry as object rather than a string --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 2df019a3..22010a6f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,7 +22,7 @@ markdown_extensions: - name: mermaid class: mermaid-experimental format: !!python/name:pymdownx.superfences.fence_code_format - - pymdownx.snippets + - pymdownx.snippets: base_path: ['code-samples'] - smarty - toc: From 62a001de249ea75bce164986b82c2a2736940ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Sun, 21 Apr 2024 06:21:40 +0200 Subject: [PATCH 04/58] feat(config): Enable check_paths option for snippets --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 22010a6f..1b1f648e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,6 +24,7 @@ markdown_extensions: format: !!python/name:pymdownx.superfences.fence_code_format - pymdownx.snippets: base_path: ['code-samples'] + check_paths: true - smarty - toc: permalink: true From c6b995975bce496326a19b54f7d057890006a380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 06:23:24 +0200 Subject: [PATCH 05/58] fix(code-samples): :pencil2: Fix snippet file embed path --- docs/getting-started/what-you-need.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/what-you-need.md b/docs/getting-started/what-you-need.md index ac97ba6c..c0cd24cc 100644 --- a/docs/getting-started/what-you-need.md +++ b/docs/getting-started/what-you-need.md @@ -35,7 +35,7 @@ When you use Pony, you don't do any of that! If you are in the same directory as your program, you can just do: ```bash ---8<-- "what-you-need-run-compile-pony.sh" +--8<-- "what-you-need-compile-pony.sh" ``` That tells the Pony compiler that your current working directory contains your source code, and to please compile it. If your source code is in some other directory, you can tell ponyc where it is: From 70dad88d3f82cc9a7ead989be29ea3ca3d33973c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 06:25:45 +0200 Subject: [PATCH 06/58] refactor(code-samples): :memo: Turn code samples into snippets for "Object Capabilities" directory --- ...rived-authority-authority-hierarchies.pony | 23 +++++++ ...-delegating-and-restricting-authority.pony | 22 +++++++ ...restrict-then-delegate-your-authority.pony | 7 +++ code-samples/trust-boundary-safe-packages.sh | 1 + docs/object-capabilities/derived-authority.md | 63 ++----------------- docs/object-capabilities/trust-boundary.md | 2 +- 6 files changed, 60 insertions(+), 58 deletions(-) create mode 100644 code-samples/derived-authority-authority-hierarchies.pony create mode 100644 code-samples/derived-authority-delegating-and-restricting-authority.pony create mode 100644 code-samples/derived-authority-restrict-then-delegate-your-authority.pony create mode 100644 code-samples/trust-boundary-safe-packages.sh 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/trust-boundary-safe-packages.sh b/code-samples/trust-boundary-safe-packages.sh new file mode 100644 index 00000000..0818fb8b --- /dev/null +++ b/code-samples/trust-boundary-safe-packages.sh @@ -0,0 +1 @@ +ponyc --safe=files:net:process my_project \ No newline at end of file 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/object-capabilities/trust-boundary.md b/docs/object-capabilities/trust-boundary.md index a26165cb..1b7d26db 100644 --- a/docs/object-capabilities/trust-boundary.md +++ b/docs/object-capabilities/trust-boundary.md @@ -17,7 +17,7 @@ But we can do better than that. In Pony, you can optionally declare a set of _safe_ packages on the `ponyc` command line, like this: ```sh -ponyc --safe=files:net:process my_project +--8<-- "trust-boundary-safe-packages.sh" ``` Here, we are declaring that only the `files`, `net` and `process` packages are allowed to use C-FFI calls. We've established our trust boundary: any other packages that try to use C-FFI calls will result in a compile-time error. From 2464d028dcd57eabc3a18d5bb6f01d80e7553e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 06:38:49 +0200 Subject: [PATCH 07/58] refactor(code-samples): :memo: Turn code samples into snippets for "Packages" directory --- code-samples/use-statement-collections.pony | 1 + ...ors-optional-package-scheme-specifier.pony | 2 + ...ors-required-package-scheme-specifier.pony | 2 + code-samples/use-statement-time-now.pony | 5 ++ code-samples/use-statement-time.pony | 2 + .../use-statement-use-names-conflict.pony | 12 ++++ ...ment-use-names-resolution-alternative.pony | 13 +++++ .../use-statement-use-names-resolution.pony | 13 +++++ docs/packages/use-statement.md | 58 +++---------------- 9 files changed, 58 insertions(+), 50 deletions(-) create mode 100644 code-samples/use-statement-collections.pony create mode 100644 code-samples/use-statement-scheme-indicators-optional-package-scheme-specifier.pony create mode 100644 code-samples/use-statement-scheme-indicators-required-package-scheme-specifier.pony create mode 100644 code-samples/use-statement-time-now.pony create mode 100644 code-samples/use-statement-time.pony create mode 100644 code-samples/use-statement-use-names-conflict.pony create mode 100644 code-samples/use-statement-use-names-resolution-alternative.pony create mode 100644 code-samples/use-statement-use-names-resolution.pony 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/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. From 50e60d22a759e25d420c2521c03793447b7e665a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 06:46:08 +0200 Subject: [PATCH 08/58] refactor(code-samples): :memo: Turn code samples into snippets for "Testing" directory --- code-samples/ponycheck-ponytest.pony | 9 +++ .../ponycheck-usage-ponytest-for-all.pony | 14 +++++ code-samples/ponycheck-usage-quickcheck.pony | 20 +++++++ code-samples/ponycheck-usage.pony | 11 ++++ code-samples/ponytest-aggregation.pony | 14 +++++ code-samples/ponytest-example.pony | 24 ++++++++ docs/testing/ponycheck.md | 58 ++----------------- docs/testing/ponytest.md | 40 +------------ 8 files changed, 98 insertions(+), 92 deletions(-) create mode 100644 code-samples/ponycheck-ponytest.pony create mode 100644 code-samples/ponycheck-usage-ponytest-for-all.pony create mode 100644 code-samples/ponycheck-usage-quickcheck.pony create mode 100644 code-samples/ponycheck-usage.pony create mode 100644 code-samples/ponytest-aggregation.pony create mode 100644 code-samples/ponytest-example.pony 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-ponytest-for-all.pony b/code-samples/ponycheck-usage-ponytest-for-all.pony new file mode 100644 index 00000000..320106c6 --- /dev/null +++ b/code-samples/ponycheck-usage-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-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/docs/testing/ponycheck.md b/docs/testing/ponycheck.md index cfb3ed3c..f14a195c 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-usage-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-usage-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. From 676f5ee39bbd5ecf2aeb6503be883e4e3c444241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 06:53:10 +0200 Subject: [PATCH 09/58] fix(code-samples): :pencil2: Fix snippet file embed path --- ...-ponytest-for-all.pony => ponycheck-ponytest-for-all.pony} | 0 docs/testing/ponycheck.md | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename code-samples/{ponycheck-usage-ponytest-for-all.pony => ponycheck-ponytest-for-all.pony} (100%) diff --git a/code-samples/ponycheck-usage-ponytest-for-all.pony b/code-samples/ponycheck-ponytest-for-all.pony similarity index 100% rename from code-samples/ponycheck-usage-ponytest-for-all.pony rename to code-samples/ponycheck-ponytest-for-all.pony diff --git a/docs/testing/ponycheck.md b/docs/testing/ponycheck.md index f14a195c..fc01ec8f 100644 --- a/docs/testing/ponycheck.md +++ b/docs/testing/ponycheck.md @@ -35,14 +35,14 @@ Below are two classic list reverse properties from the QuickCheck paper adapted 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 ---8<-- "ponycheck-usage-ponytest.pony" +--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 ---8<-- "ponycheck-usage-ponytest-for-all.pony" +--8<-- "ponycheck-ponytest-for-all.pony" ``` ## Additional resources From c0a6b17b83f4314ff1ebbcab00d3e3770482e562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 07:01:18 +0200 Subject: [PATCH 10/58] refactor(code-samples): :memo: Turn code samples into snippets for "Gotchas" directory --- code-samples/divide-by-zero-floats.pony | 1 + code-samples/divide-by-zero-partial.pony | 6 ++++++ code-samples/divide-by-zero-unsafe.pony | 2 ++ code-samples/divide-by-zero.pony | 1 + code-samples/function-call-side-effects.pony | 10 ++++++++++ code-samples/garbage-collection.pony | 7 +++++++ code-samples/recursion.pony | 13 +++++++++++++ code-samples/scheduling.pony | 4 ++++ docs/gotchas/divide-by-zero.md | 14 ++++---------- docs/gotchas/garbage-collection.md | 8 +------- docs/gotchas/recursion.md | 14 +------------- docs/gotchas/scheduling.md | 5 +---- ...effect-ordering-in-function-call-expressions.md | 11 +---------- 13 files changed, 52 insertions(+), 44 deletions(-) create mode 100644 code-samples/divide-by-zero-floats.pony create mode 100644 code-samples/divide-by-zero-partial.pony create mode 100644 code-samples/divide-by-zero-unsafe.pony create mode 100644 code-samples/divide-by-zero.pony create mode 100644 code-samples/function-call-side-effects.pony create mode 100644 code-samples/garbage-collection.pony create mode 100644 code-samples/recursion.pony create mode 100644 code-samples/scheduling.pony 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/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/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/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/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? From 003722b6319c54da2f6e71b1296e5e2ad07295ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 07:27:40 +0200 Subject: [PATCH 11/58] fix(code-samples): :pencil2: Shorten snippet line syntax --- docs/getting-started/how-it-works.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/how-it-works.md b/docs/getting-started/how-it-works.md index e981592d..5f0c069c 100644 --- a/docs/getting-started/how-it-works.md +++ b/docs/getting-started/how-it-works.md @@ -11,7 +11,7 @@ Let's go through that line by line. ## Line 1 ```pony ---8<-- "hello-world-main.pony:1:1" +--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. From fd48fe335440959603bc5beb087c030b796147cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 20:30:06 +0200 Subject: [PATCH 12/58] refactor(code-samples): :memo: Turn code samples into snippets for "Types" directory --- code-samples/actors-behaviors.pony | 9 ++ code-samples/actors-sequential.pony | 7 ++ code-samples/classes-swap-values-sugar.pony | 1 + code-samples/classes-swap-values.pony | 3 + ...classes-wombat-constructor-invocation.pony | 2 + code-samples/classes-wombat-constructors.pony | 11 +++ code-samples/classes-wombat.pony | 16 ++++ ...zero-argument-constructors-invocation.pony | 3 + .../classes-zero-argument-constructors.pony | 8 ++ code-samples/primitives-doors.pony | 24 +++++ code-samples/structs-constructors.pony | 17 ++++ code-samples/structs-fields.pony | 6 ++ .../traits-and-interfaces-marker-methods.pony | 5 + ...traits-and-interfaces-multiple-traits.pony | 7 ++ .../traits-and-interfaces-nested-traits.pony | 7 ++ ...aces-nominal-and-structural-subtyping.pony | 7 ++ ...-interfaces-nominal-subtyping-in-pony.pony | 2 + ...aits-and-interfaces-nominal-subtyping.pony | 1 + ...nd-interfaces-open-world-enumerations.pony | 4 + ...s-and-interfaces-open-world-interface.pony | 1 + ...aits-and-interfaces-open-world-typing.pony | 17 ++++ ...traits-and-interfaces-private-methods.pony | 9 ++ ...terfaces-structural-subtyping-in-pony.pony | 2 + ...s-and-interfaces-structural-subtyping.pony | 11 +++ code-samples/traits-and-interfaces-trait.pony | 4 + .../traits-and-interfaces-type-union.pony | 4 + .../type-aliases-complex-types-interface.pony | 10 ++ .../type-aliases-complex-types-trait.pony | 10 ++ .../type-aliases-enumerations-apply.pony | 5 + .../type-aliases-enumerations-iteration.pony | 7 ++ .../type-aliases-enumerations-namespace.pony | 3 + code-samples/type-aliases-enumerations.pony | 5 + code-samples/type-aliases-map.pony | 1 + code-samples/type-aliases-set-is.pony | 1 + code-samples/type-alises-hash-set.pony | 1 + code-samples/type-expressions-combined.pony | 1 + .../type-expressions-intersection.pony | 1 + .../type-expressions-tuple-declaration.pony | 3 + .../type-expressions-tuple-destructuring.pony | 1 + .../type-expressions-tuple-direct-access.pony | 2 + .../type-expressions-tuple-reassignment.pony | 1 + code-samples/type-expressions-type-alias.pony | 7 ++ code-samples/type-expressions-union.pony | 1 + docs/types/actors.md | 18 +--- docs/types/classes.md | 70 ++------------ docs/types/primitives.md | 25 +---- docs/types/structs.md | 26 +---- docs/types/traits-and-interfaces.md | 95 +++---------------- docs/types/type-aliases.md | 52 ++-------- docs/types/type-expressions.md | 25 ++--- 50 files changed, 294 insertions(+), 265 deletions(-) create mode 100644 code-samples/actors-behaviors.pony create mode 100644 code-samples/actors-sequential.pony create mode 100644 code-samples/classes-swap-values-sugar.pony create mode 100644 code-samples/classes-swap-values.pony create mode 100644 code-samples/classes-wombat-constructor-invocation.pony create mode 100644 code-samples/classes-wombat-constructors.pony create mode 100644 code-samples/classes-wombat.pony create mode 100644 code-samples/classes-zero-argument-constructors-invocation.pony create mode 100644 code-samples/classes-zero-argument-constructors.pony create mode 100644 code-samples/primitives-doors.pony create mode 100644 code-samples/structs-constructors.pony create mode 100644 code-samples/structs-fields.pony create mode 100644 code-samples/traits-and-interfaces-marker-methods.pony create mode 100644 code-samples/traits-and-interfaces-multiple-traits.pony create mode 100644 code-samples/traits-and-interfaces-nested-traits.pony create mode 100644 code-samples/traits-and-interfaces-nominal-and-structural-subtyping.pony create mode 100644 code-samples/traits-and-interfaces-nominal-subtyping-in-pony.pony create mode 100644 code-samples/traits-and-interfaces-nominal-subtyping.pony create mode 100644 code-samples/traits-and-interfaces-open-world-enumerations.pony create mode 100644 code-samples/traits-and-interfaces-open-world-interface.pony create mode 100644 code-samples/traits-and-interfaces-open-world-typing.pony create mode 100644 code-samples/traits-and-interfaces-private-methods.pony create mode 100644 code-samples/traits-and-interfaces-structural-subtyping-in-pony.pony create mode 100644 code-samples/traits-and-interfaces-structural-subtyping.pony create mode 100644 code-samples/traits-and-interfaces-trait.pony create mode 100644 code-samples/traits-and-interfaces-type-union.pony create mode 100644 code-samples/type-aliases-complex-types-interface.pony create mode 100644 code-samples/type-aliases-complex-types-trait.pony create mode 100644 code-samples/type-aliases-enumerations-apply.pony create mode 100644 code-samples/type-aliases-enumerations-iteration.pony create mode 100644 code-samples/type-aliases-enumerations-namespace.pony create mode 100644 code-samples/type-aliases-enumerations.pony create mode 100644 code-samples/type-aliases-map.pony create mode 100644 code-samples/type-aliases-set-is.pony create mode 100644 code-samples/type-alises-hash-set.pony create mode 100644 code-samples/type-expressions-combined.pony create mode 100644 code-samples/type-expressions-intersection.pony create mode 100644 code-samples/type-expressions-tuple-declaration.pony create mode 100644 code-samples/type-expressions-tuple-destructuring.pony create mode 100644 code-samples/type-expressions-tuple-direct-access.pony create mode 100644 code-samples/type-expressions-tuple-reassignment.pony create mode 100644 code-samples/type-expressions-type-alias.pony create mode 100644 code-samples/type-expressions-union.pony 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/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/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/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/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-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-alises-hash-set.pony b/code-samples/type-alises-hash-set.pony new file mode 100644 index 00000000..c5a20122 --- /dev/null +++ b/code-samples/type-alises-hash-set.pony @@ -0,0 +1 @@ +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/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..08d39893 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-tying.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. From f9836902ab0d601dedb24313eb0ee766086a42ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 20:33:53 +0200 Subject: [PATCH 13/58] fix(code-samples): :pencil2: Fix snippet file embed path --- docs/types/traits-and-interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types/traits-and-interfaces.md b/docs/types/traits-and-interfaces.md index 08d39893..a78baddd 100644 --- a/docs/types/traits-and-interfaces.md +++ b/docs/types/traits-and-interfaces.md @@ -141,7 +141,7 @@ We've covered a couple ways that traits can be better than interfaces, let's clo Here's a contrived example: ```pony ---8<-- "traits-and-interfaces-open-world-tying.pony" +--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. From 27e06237363b314fa5dd899cf01ecf9d8fa4cbf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 20:42:29 +0200 Subject: [PATCH 14/58] fix(code-samples): :pencil2: Fix snippet file name --- code-samples/type-aliases-hash-set.pony | 1 + 1 file changed, 1 insertion(+) create mode 100644 code-samples/type-aliases-hash-set.pony 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 From 154c7dcda5cca980dd84039913c0a44d9f2cf36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Sun, 21 Apr 2024 20:44:42 +0200 Subject: [PATCH 15/58] cont'd --- code-samples/type-alises-hash-set.pony | 1 - 1 file changed, 1 deletion(-) delete mode 100644 code-samples/type-alises-hash-set.pony diff --git a/code-samples/type-alises-hash-set.pony b/code-samples/type-alises-hash-set.pony deleted file mode 100644 index c5a20122..00000000 --- a/code-samples/type-alises-hash-set.pony +++ /dev/null @@ -1 +0,0 @@ -HashSet[A, HashIs[A!]] \ No newline at end of file From cc8c939d1ea521d866e49e83ccd68986958437bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 01:29:36 +0200 Subject: [PATCH 16/58] refactor(code-samples): :memo: Turn code samples into snippets for "Expressions" directory --- ...arithmetic-default-integer-arithmetic.pony | 5 + ...rithmetic-explicit-numeric-conversion.pony | 2 + ...ithmetic-partial-and-check-arithmetic.pony | 17 +++ .../arithmetic-unsafe-conversion.pony | 8 + code-samples/as-operator-array-literal.pony | 9 ++ ...s-operator-match-statement-comparison.pony | 13 ++ ...-operator-match-statement-without-try.pony | 9 ++ ...c-interface-with-reference-capability.pony | 8 + .../as-operator-more-specific-interface.pony | 19 +++ .../as-operator-more-specific-type.pony | 12 ++ code-samples/as-operator-unrelated-type.pony | 10 ++ ...conditionals-expression-implicit-none.pony | 4 + ...es-conditionals-expression-union-type.pony | 6 + ...l-structures-conditionals-expressions.pony | 1 + ...tionals-if-else-c-ambiguous-relationship.c | 6 + ...ntrol-structures-conditionals-if-else.pony | 5 + ...tructures-conditionals-if-elseif-else.pony | 7 + .../control-structures-conditionals-if.pony | 3 + ...tructures-conditionals-nested-if-else.pony | 9 ++ .../control-structures-iterator-methods.pony | 2 + ...ntrol-structures-loop-expression-else.pony | 9 ++ ...ntrol-structures-loop-expression-none.pony | 10 ++ .../control-structures-loop-expression.pony | 10 ++ ...structures-loops-for-while-comparison.pony | 5 + .../control-structures-loops-for.pony | 3 + .../control-structures-loops-repeat.pony | 7 + ...rol-structures-loops-while-break-else.pony | 10 ++ .../control-structures-loops-while.pony | 6 + ...lity-equatable-default-implementation.pony | 3 + code-samples/equality-identity-equality.pony | 16 ++ code-samples/equality-primitives.pony | 7 + .../equality-structural-equality.pony | 29 ++++ code-samples/errors-dispose-multiple.pony | 3 + code-samples/errors-dispose.pony | 5 + code-samples/errors-partial-functions.pony | 7 + code-samples/errors-try-else.pony | 7 + code-samples/errors-try-then.pony | 9 ++ code-samples/errors-try-without-else.pony | 3 + code-samples/errors-with-blocks.pony | 3 + code-samples/literals-array-literals.pony | 5 + code-samples/literals-as-expression.pony | 6 + code-samples/literals-character-literals.pony | 3 + code-samples/literals-floats.pony | 2 + .../literals-multi-line-string-literals.pony | 5 + ...literals-multibyte-character-literals.pony | 1 + code-samples/literals-number-types.pony | 3 + code-samples/literals-numeric-typing.pony | 3 + .../literals-string-literals-encoding.pony | 1 + .../literals-string-literals-instances.pony | 5 + code-samples/literals-string-literals.pony | 13 ++ ...iterals-triple-quoted-string-literals.pony | 15 ++ .../literals-type-inference-coercion.pony | 5 + ...type-inference-reference-capabilities.pony | 5 + .../literals-type-inference-union.pony | 6 + code-samples/match-capabilities-only.pony | 15 ++ code-samples/match-capabilities.pony | 19 +++ code-samples/match-captures.pony | 8 + code-samples/match-custom-eq-operand.pony | 22 +++ code-samples/match-expression.pony | 7 + code-samples/match-guards.pony | 10 ++ .../match-tuples-ignore-elements.pony | 9 ++ code-samples/match-tuples.pony | 9 ++ code-samples/match-type-and-value.pony | 9 ++ ...-value-pattern-matching-vs-type-check.pony | 14 ++ code-samples/match-values.pony | 9 ++ code-samples/methods-anonymous-methods.pony | 9 ++ .../methods-chaining-return-value.pony | 10 ++ code-samples/methods-chaining.pony | 7 + ...ds-constructors-calling-on-expression.pony | 13 ++ ...structors-calling-reuse-variable-name.pony | 3 + .../methods-constructors-calling.pony | 13 ++ code-samples/methods-constructors.pony | 8 + code-samples/methods-default-arguments.pony | 13 ++ code-samples/methods-function-calling.pony | 6 + ...thods-functions-calling-implicit-this.pony | 20 +++ code-samples/methods-functions.pony | 6 + ...med-and-positional-arguments-combined.pony | 8 + code-samples/methods-named-arguments.pony | 12 ++ .../object-literals-actor-literal.pony | 3 + .../object-literals-closing-over-values.pony | 8 + .../object-literals-fields-assignment.pony | 9 ++ ...als-lambda-as-explicit-object-literal.pony | 3 + ...rals-lambda-capture-and-rename-values.pony | 2 + ...object-literals-lambda-capture-values.pony | 6 + ...erals-lambda-reference-capabilities-2.pony | 19 +++ ...iterals-lambda-reference-capabilities.pony | 16 ++ ...capability-as-explicit-object-literal.pony | 3 + ...rals-lambda-with-reference-capability.pony | 1 + code-samples/object-literals-lambda.pony | 1 + ...iterals-object-literal-with-interface.pony | 4 + .../object-literals-object-literal.pony | 3 + .../object-literals-reference-capability.pony | 9 ++ code-samples/operators-add.pony | 19 +++ code-samples/operators-infix-operator.pony | 2 + code-samples/operators-operator-aliasing.pony | 2 + ...-and-unary-operators-with-parentheses.pony | 1 + ...d-unary-operators-without-parentheses.pony | 1 + .../operators-precedence-single-operator.pony | 1 + ...dence-unary-operator-with-parentheses.pony | 1 + ...operators-precedence-with-parentheses.pony | 1 + ...rators-precedence-without-parentheses.pony | 1 + code-samples/operators-unary-operators.pony | 2 + ...ack-function-with-all-arguments-bound.pony | 2 + ...back-function-with-no-arguments-bound.pony | 2 + ...-function-with-out-of-order-arguments.pony | 2 + ...ck-function-with-some-arguments-bound.pony | 11 ++ ...tially-applying-a-partial-application.pony | 3 + code-samples/sugar-apply-explicit.pony | 2 + code-samples/sugar-apply-implicit.pony | 2 + .../sugar-apply-with-arguments-explicit.pony | 2 + .../sugar-apply-with-arguments-implicit.pony | 2 + .../sugar-create-apply-combined-explicit.pony | 2 + .../sugar-create-apply-combined-implicit.pony | 2 + code-samples/sugar-create-explicit.pony | 1 + code-samples/sugar-create-implicit.pony | 1 + .../sugar-create-with-arguments-explicit.pony | 1 + .../sugar-create-with-arguments-implicit.pony | 1 + .../sugar-update-additional-parameters.pony | 3 + code-samples/sugar-update-explicit.pony | 1 + code-samples/sugar-update-implicit.pony | 1 + ...riables-fields-constructor-assignment.pony | 7 + ...ariables-fields-definition-assignment.pony | 3 + .../variables-fields-implicit-assignment.pony | 11 ++ .../variables-fields-let-reassignment.pony | 13 ++ code-samples/variables-let-reassignment.pony | 3 + code-samples/variables-local-variables.pony | 6 + code-samples/variables-scope.pony | 6 + code-samples/variables-var-vs-let.pony | 4 + docs/expressions/arithmetic.md | 36 +---- docs/expressions/as.md | 90 +---------- docs/expressions/control-structures.md | 120 +++------------ docs/expressions/equality.md | 59 +------- docs/expressions/errors.md | 44 +----- docs/expressions/literals.md | 95 ++---------- docs/expressions/match.md | 142 ++---------------- docs/expressions/methods.md | 142 ++---------------- docs/expressions/object-literals.md | 101 ++----------- docs/expressions/ops.md | 41 ++--- docs/expressions/partial-application.md | 25 +-- docs/expressions/sugar.md | 34 ++--- docs/expressions/variables.md | 63 ++------ 141 files changed, 986 insertions(+), 862 deletions(-) create mode 100644 code-samples/arithmetic-default-integer-arithmetic.pony create mode 100644 code-samples/arithmetic-explicit-numeric-conversion.pony create mode 100644 code-samples/arithmetic-partial-and-check-arithmetic.pony create mode 100644 code-samples/arithmetic-unsafe-conversion.pony create mode 100644 code-samples/as-operator-array-literal.pony create mode 100644 code-samples/as-operator-match-statement-comparison.pony create mode 100644 code-samples/as-operator-match-statement-without-try.pony create mode 100644 code-samples/as-operator-more-specific-interface-with-reference-capability.pony create mode 100644 code-samples/as-operator-more-specific-interface.pony create mode 100644 code-samples/as-operator-more-specific-type.pony create mode 100644 code-samples/as-operator-unrelated-type.pony create mode 100644 code-samples/control-structures-conditionals-expression-implicit-none.pony create mode 100644 code-samples/control-structures-conditionals-expression-union-type.pony create mode 100644 code-samples/control-structures-conditionals-expressions.pony create mode 100644 code-samples/control-structures-conditionals-if-else-c-ambiguous-relationship.c create mode 100644 code-samples/control-structures-conditionals-if-else.pony create mode 100644 code-samples/control-structures-conditionals-if-elseif-else.pony create mode 100644 code-samples/control-structures-conditionals-if.pony create mode 100644 code-samples/control-structures-conditionals-nested-if-else.pony create mode 100644 code-samples/control-structures-iterator-methods.pony create mode 100644 code-samples/control-structures-loop-expression-else.pony create mode 100644 code-samples/control-structures-loop-expression-none.pony create mode 100644 code-samples/control-structures-loop-expression.pony create mode 100644 code-samples/control-structures-loops-for-while-comparison.pony create mode 100644 code-samples/control-structures-loops-for.pony create mode 100644 code-samples/control-structures-loops-repeat.pony create mode 100644 code-samples/control-structures-loops-while-break-else.pony create mode 100644 code-samples/control-structures-loops-while.pony create mode 100644 code-samples/equality-equatable-default-implementation.pony create mode 100644 code-samples/equality-identity-equality.pony create mode 100644 code-samples/equality-primitives.pony create mode 100644 code-samples/equality-structural-equality.pony create mode 100644 code-samples/errors-dispose-multiple.pony create mode 100644 code-samples/errors-dispose.pony create mode 100644 code-samples/errors-partial-functions.pony create mode 100644 code-samples/errors-try-else.pony create mode 100644 code-samples/errors-try-then.pony create mode 100644 code-samples/errors-try-without-else.pony create mode 100644 code-samples/errors-with-blocks.pony create mode 100644 code-samples/literals-array-literals.pony create mode 100644 code-samples/literals-as-expression.pony create mode 100644 code-samples/literals-character-literals.pony create mode 100644 code-samples/literals-floats.pony create mode 100644 code-samples/literals-multi-line-string-literals.pony create mode 100644 code-samples/literals-multibyte-character-literals.pony create mode 100644 code-samples/literals-number-types.pony create mode 100644 code-samples/literals-numeric-typing.pony create mode 100644 code-samples/literals-string-literals-encoding.pony create mode 100644 code-samples/literals-string-literals-instances.pony create mode 100644 code-samples/literals-string-literals.pony create mode 100644 code-samples/literals-triple-quoted-string-literals.pony create mode 100644 code-samples/literals-type-inference-coercion.pony create mode 100644 code-samples/literals-type-inference-reference-capabilities.pony create mode 100644 code-samples/literals-type-inference-union.pony create mode 100644 code-samples/match-capabilities-only.pony create mode 100644 code-samples/match-capabilities.pony create mode 100644 code-samples/match-captures.pony create mode 100644 code-samples/match-custom-eq-operand.pony create mode 100644 code-samples/match-expression.pony create mode 100644 code-samples/match-guards.pony create mode 100644 code-samples/match-tuples-ignore-elements.pony create mode 100644 code-samples/match-tuples.pony create mode 100644 code-samples/match-type-and-value.pony create mode 100644 code-samples/match-value-pattern-matching-vs-type-check.pony create mode 100644 code-samples/match-values.pony create mode 100644 code-samples/methods-anonymous-methods.pony create mode 100644 code-samples/methods-chaining-return-value.pony create mode 100644 code-samples/methods-chaining.pony create mode 100644 code-samples/methods-constructors-calling-on-expression.pony create mode 100644 code-samples/methods-constructors-calling-reuse-variable-name.pony create mode 100644 code-samples/methods-constructors-calling.pony create mode 100644 code-samples/methods-constructors.pony create mode 100644 code-samples/methods-default-arguments.pony create mode 100644 code-samples/methods-function-calling.pony create mode 100644 code-samples/methods-functions-calling-implicit-this.pony create mode 100644 code-samples/methods-functions.pony create mode 100644 code-samples/methods-named-and-positional-arguments-combined.pony create mode 100644 code-samples/methods-named-arguments.pony create mode 100644 code-samples/object-literals-actor-literal.pony create mode 100644 code-samples/object-literals-closing-over-values.pony create mode 100644 code-samples/object-literals-fields-assignment.pony create mode 100644 code-samples/object-literals-lambda-as-explicit-object-literal.pony create mode 100644 code-samples/object-literals-lambda-capture-and-rename-values.pony create mode 100644 code-samples/object-literals-lambda-capture-values.pony create mode 100644 code-samples/object-literals-lambda-reference-capabilities-2.pony create mode 100644 code-samples/object-literals-lambda-reference-capabilities.pony create mode 100644 code-samples/object-literals-lambda-with-reference-capability-as-explicit-object-literal.pony create mode 100644 code-samples/object-literals-lambda-with-reference-capability.pony create mode 100644 code-samples/object-literals-lambda.pony create mode 100644 code-samples/object-literals-object-literal-with-interface.pony create mode 100644 code-samples/object-literals-object-literal.pony create mode 100644 code-samples/object-literals-reference-capability.pony create mode 100644 code-samples/operators-add.pony create mode 100644 code-samples/operators-infix-operator.pony create mode 100644 code-samples/operators-operator-aliasing.pony create mode 100644 code-samples/operators-precedence-infix-and-unary-operators-with-parentheses.pony create mode 100644 code-samples/operators-precedence-infix-and-unary-operators-without-parentheses.pony create mode 100644 code-samples/operators-precedence-single-operator.pony create mode 100644 code-samples/operators-precedence-unary-operator-with-parentheses.pony create mode 100644 code-samples/operators-precedence-with-parentheses.pony create mode 100644 code-samples/operators-precedence-without-parentheses.pony create mode 100644 code-samples/operators-unary-operators.pony create mode 100644 code-samples/partial-application-callback-function-with-all-arguments-bound.pony create mode 100644 code-samples/partial-application-callback-function-with-no-arguments-bound.pony create mode 100644 code-samples/partial-application-callback-function-with-out-of-order-arguments.pony create mode 100644 code-samples/partial-application-callback-function-with-some-arguments-bound.pony create mode 100644 code-samples/partial-application-partially-applying-a-partial-application.pony create mode 100644 code-samples/sugar-apply-explicit.pony create mode 100644 code-samples/sugar-apply-implicit.pony create mode 100644 code-samples/sugar-apply-with-arguments-explicit.pony create mode 100644 code-samples/sugar-apply-with-arguments-implicit.pony create mode 100644 code-samples/sugar-create-apply-combined-explicit.pony create mode 100644 code-samples/sugar-create-apply-combined-implicit.pony create mode 100644 code-samples/sugar-create-explicit.pony create mode 100644 code-samples/sugar-create-implicit.pony create mode 100644 code-samples/sugar-create-with-arguments-explicit.pony create mode 100644 code-samples/sugar-create-with-arguments-implicit.pony create mode 100644 code-samples/sugar-update-additional-parameters.pony create mode 100644 code-samples/sugar-update-explicit.pony create mode 100644 code-samples/sugar-update-implicit.pony create mode 100644 code-samples/variables-fields-constructor-assignment.pony create mode 100644 code-samples/variables-fields-definition-assignment.pony create mode 100644 code-samples/variables-fields-implicit-assignment.pony create mode 100644 code-samples/variables-fields-let-reassignment.pony create mode 100644 code-samples/variables-let-reassignment.pony create mode 100644 code-samples/variables-local-variables.pony create mode 100644 code-samples/variables-scope.pony create mode 100644 code-samples/variables-var-vs-let.pony 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/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/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-c-ambiguous-relationship.c b/code-samples/control-structures-conditionals-if-else-c-ambiguous-relationship.c new file mode 100644 index 00000000..e00e552b --- /dev/null +++ b/code-samples/control-structures-conditionals-if-else-c-ambiguous-relationship.c @@ -0,0 +1,6 @@ +// C code +if(a) + if(b) + printf("a and b\n"); +else + printf("not a\n"); \ 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/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/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/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-function-calling.pony b/code-samples/methods-function-calling.pony new file mode 100644 index 00000000..8b63f28d --- /dev/null +++ b/code-samples/methods-function-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-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.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/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/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/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..2a4044c8 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,48 +15,25 @@ __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: ```c -// C code -if(a) - if(b) - printf("a and b\n"); -else - printf("not a\n"); +--8<-- "control-structures-conditionals-if-else-c-ambiguous-relationship.c" ``` Here it is not obvious whether the `else` is an alternative to the first or the second `if`. In fact here the `else` relates to the `if(b)` so our example contains a bug. Pony avoids this type of bug by handling `if` and `else` differently and the need for `elseif` comes out of that. @@ -72,7 +47,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 +55,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 +81,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 +97,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 +115,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 +145,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 +153,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 +161,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 +182,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 From 999c6c45ddf3b7d3132110a31280be73913f1cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 01:34:04 +0200 Subject: [PATCH 17/58] fix(code-samples): :pencil2: Fix snippet file name --- ...thods-function-calling.pony => methods-functions-calling.pony} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename code-samples/{methods-function-calling.pony => methods-functions-calling.pony} (100%) diff --git a/code-samples/methods-function-calling.pony b/code-samples/methods-functions-calling.pony similarity index 100% rename from code-samples/methods-function-calling.pony rename to code-samples/methods-functions-calling.pony From 706c0d0594fb0d9f8f20bb9d81b8e3eda4c12131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 02:57:26 +0200 Subject: [PATCH 18/58] refactor(code-samples): :memo: Turn code samples into snippets for "Reference Capabilities" directory --- code-samples/aliasing-alias-types.pony | 2 ++ code-samples/aliasing-ephemeral-types.pony | 2 ++ code-samples/aliasing-iso-to-tag.pony | 2 ++ ...-multiple-references-to-an-iso-object.pony | 2 ++ code-samples/aliasing-trn-to-box.pony | 2 ++ code-samples/arrow-types-box.pony | 3 ++ code-samples/arrow-types-this.pony | 4 +++ code-samples/arrow-types-type-parameter.pony | 1 + ...e-and-change-its-reference-capability.pony | 2 ++ ...destructive-read-consuming-a-variable.pony | 3 ++ ...e-and-destructive-read-moving-a-value.pony | 8 +++++ .../recovering-capabilities-format-int.pony | 21 ++++++++++++++ .../recovering-capabilities-ref-to-iso.pony | 1 + ...recovering-capabilities-string-append.pony | 2 ++ ...es-with-explicit-reference-capability.pony | 1 + ...nstructors-for-different-capabilities.pony | 8 +++++ ...ence-capabilities-default-vs-explicit.pony | 2 ++ .../reference-capabilities-file-open.c | 1 + ...ence-capabilities-string-capabilities.pony | 6 ++++ ...reference-capabilities-string-default.pony | 1 + ...y-a-capability-other-than-the-default.pony | 5 ++++ docs/reference-capabilities/aliasing.md | 15 ++++------ docs/reference-capabilities/arrow-types.md | 11 ++----- .../consume-and-destructive-read.md | 19 +++--------- .../recovering-capabilities.md | 29 +++---------------- .../reference-capabilities.md | 29 ++++--------------- 26 files changed, 101 insertions(+), 81 deletions(-) create mode 100644 code-samples/aliasing-alias-types.pony create mode 100644 code-samples/aliasing-ephemeral-types.pony create mode 100644 code-samples/aliasing-iso-to-tag.pony create mode 100644 code-samples/aliasing-multiple-references-to-an-iso-object.pony create mode 100644 code-samples/aliasing-trn-to-box.pony create mode 100644 code-samples/arrow-types-box.pony create mode 100644 code-samples/arrow-types-this.pony create mode 100644 code-samples/arrow-types-type-parameter.pony create mode 100644 code-samples/consume-and-destructive-read-consuming-a-variable-and-change-its-reference-capability.pony create mode 100644 code-samples/consume-and-destructive-read-consuming-a-variable.pony create mode 100644 code-samples/consume-and-destructive-read-moving-a-value.pony create mode 100644 code-samples/recovering-capabilities-format-int.pony create mode 100644 code-samples/recovering-capabilities-ref-to-iso.pony create mode 100644 code-samples/recovering-capabilities-string-append.pony create mode 100644 code-samples/recovering-capabilities-with-explicit-reference-capability.pony create mode 100644 code-samples/reference-capabilities-constructors-for-different-capabilities.pony create mode 100644 code-samples/reference-capabilities-default-vs-explicit.pony create mode 100644 code-samples/reference-capabilities-file-open.c create mode 100644 code-samples/reference-capabilities-string-capabilities.pony create mode 100644 code-samples/reference-capabilities-string-default.pony create mode 100644 code-samples/reference-capability-specificy-a-capability-other-than-the-default.pony 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/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/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/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/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-file-open.c b/code-samples/reference-capabilities-file-open.c new file mode 100644 index 00000000..11f44b39 --- /dev/null +++ b/code-samples/reference-capabilities-file-open.c @@ -0,0 +1 @@ +int fd = open("/etc/passwd", O_RDWR); \ 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/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..20e11e16 100644 --- a/docs/reference-capabilities/reference-capabilities.md +++ b/docs/reference-capabilities/reference-capabilities.md @@ -9,7 +9,7 @@ In Pony, we do it with _reference capabilities_. If you open a file in UNIX and get a file descriptor back, that file descriptor is a token that designates an object - but it isn't a capability. To be a capability, we need to open that file with some permission - some access right. For example: ```C -int fd = open("/etc/passwd", O_RDWR); +--8<-- "reference-capabilities-file-open.c" ``` Now we have a token and a set of rights. @@ -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. From cc1f53a279a78a6e3d275ecbf89a5ec9e3e7db4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 03:37:33 +0200 Subject: [PATCH 19/58] refactor(code-samples): :memo: Turn code samples into snippets for "Generics" directory --- .../generic-constraints-foo-any-read.pony | 17 +++ ...ities-accept-any-reference-capability.pony | 17 +++ ...capabilities-capability-generic-class.pony | 20 +++ ...ies-default-capability-and-constraint.pony | 1 + ...cit-constraint-and-default-capability.pony | 1 + ...iso-consume-iso-constructor-parameter.pony | 16 +++ ...oo-iso-consume-iso-function-parameter.pony | 1 + ...nce-capabilities-foo-iso-error-message.txt | 7 + ...cs-and-reference-capabilities-foo-iso.pony | 17 +++ ...nce-capabilities-foo-ref-and-this-ref.pony | 16 +++ ...cs-and-reference-capabilities-foo-ref.pony | 17 +++ code-samples/generics-foo-non-generic.pony | 16 +++ code-samples/generics-foo-string.pony | 9 ++ code-samples/generics-foo-with-any-val.pony | 22 +++ ...generics-generic-class-initialization.pony | 3 + code-samples/generics-generic-methods.pony | 11 ++ ...cs-type-parameter-defaults-definition.pony | 9 ++ ...ype-parameter-defaults-initialization.pony | 3 + docs/generics/generic-constraints.md | 20 +-- .../generics-and-reference-capabilities.md | 131 ++---------------- docs/generics/index.md | 80 +---------- 21 files changed, 225 insertions(+), 209 deletions(-) create mode 100644 code-samples/generic-constraints-foo-any-read.pony create mode 100644 code-samples/generics-and-reference-capabilities-accept-any-reference-capability.pony create mode 100644 code-samples/generics-and-reference-capabilities-capability-generic-class.pony create mode 100644 code-samples/generics-and-reference-capabilities-default-capability-and-constraint.pony create mode 100644 code-samples/generics-and-reference-capabilities-explicit-constraint-and-default-capability.pony create mode 100644 code-samples/generics-and-reference-capabilities-foo-iso-consume-iso-constructor-parameter.pony create mode 100644 code-samples/generics-and-reference-capabilities-foo-iso-consume-iso-function-parameter.pony create mode 100644 code-samples/generics-and-reference-capabilities-foo-iso-error-message.txt create mode 100644 code-samples/generics-and-reference-capabilities-foo-iso.pony create mode 100644 code-samples/generics-and-reference-capabilities-foo-ref-and-this-ref.pony create mode 100644 code-samples/generics-and-reference-capabilities-foo-ref.pony create mode 100644 code-samples/generics-foo-non-generic.pony create mode 100644 code-samples/generics-foo-string.pony create mode 100644 code-samples/generics-foo-with-any-val.pony create mode 100644 code-samples/generics-generic-class-initialization.pony create mode 100644 code-samples/generics-generic-methods.pony create mode 100644 code-samples/generics-type-parameter-defaults-definition.pony create mode 100644 code-samples/generics-type-parameter-defaults-initialization.pony 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/docs/generics/generic-constraints.md b/docs/generics/generic-constraints.md index 67bda8a7..fbe10cf4 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: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..45a0bd94 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: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-with-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`. From bb74be71ad4b60fd0b459aa03f0402d63f1c1e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 03:41:44 +0200 Subject: [PATCH 20/58] fix(code-samples): :pencil2: Fix snippet file embed path --- docs/generics/generics-and-reference-capabilities.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generics/generics-and-reference-capabilities.md b/docs/generics/generics-and-reference-capabilities.md index 45a0bd94..5bb8a657 100644 --- a/docs/generics/generics-and-reference-capabilities.md +++ b/docs/generics/generics-and-reference-capabilities.md @@ -15,7 +15,7 @@ If the capability is left out of the type parameter then the generic class or fu It can be made shorter because `Any` is the default constraint, leaving us with: ```pony ---8<-- "generics-and-reference-capabilities-with-default-capability-and-constraint.pony" +--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: From 2e6bc8a52d8103a56c174d51820d1c5b62aadd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 03:54:38 +0200 Subject: [PATCH 21/58] fix(code-samples): :pencil2: Shorten snippet line syntax --- docs/generics/generic-constraints.md | 2 +- docs/generics/generics-and-reference-capabilities.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/generics/generic-constraints.md b/docs/generics/generic-constraints.md index fbe10cf4..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 ---8<-- "generics-foo-with-any-val.pony:1:1" +--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: diff --git a/docs/generics/generics-and-reference-capabilities.md b/docs/generics/generics-and-reference-capabilities.md index 5bb8a657..fa7e634b 100644 --- a/docs/generics/generics-and-reference-capabilities.md +++ b/docs/generics/generics-and-reference-capabilities.md @@ -3,7 +3,7 @@ In the examples presented previously we've explicitly set the reference capability to `val`: ```pony ---8<-- "generics-foo-with-any-val.pony:1:1" +--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: From 57bbbeb44d0b7a1f531173bbd8611a3eaea0da5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 05:50:49 +0200 Subject: [PATCH 22/58] refactor(code-samples): :memo: Turn code samples into snippets for "C-FFI" directory --- ...mpile-jump-consistent-hashing-for-macos.sh | 3 + .../c-abi-jump-consistent-hashing-header.c | 9 + ...i-jump-consistent-hashing-implementation.c | 17 ++ .../c-abi-jump-consistent-hashing.pony | 15 ++ ...p-consistent-hashing-c-implementation.pony | 41 ++++ ...allbacks-bare-functions-pass-to-c-api.pony | 1 + .../c-ffi-callbacks-bare-functions.pony | 3 + .../c-ffi-callbacks-bare-lambda-callback.pony | 2 + .../c-ffi-callbacks-bare-lambda-struct.pony | 2 + .../c-ffi-callbacks-sqlite3-callback-2.pony | 16 ++ .../c-ffi-callbacks-sqlite3-callback.c | 16 ++ .../c-ffi-callbacks-sqlite3-callback.pony | 14 ++ ...-callbacks-struct-with-function-pointers.c | 4 + ...-list-entry-with-explicit-return-type.pony | 7 + ...access-list-entry-without-return-type.pony | 6 + code-samples/calling-c-addressof.pony | 4 + ...calling-c-default-method-in-primitive.pony | 14 ++ .../calling-c-default-method-in-trait.pony | 10 + .../calling-c-different-types-of-lists.pony | 9 + .../calling-c-ffi-functions-raising-errors.c | 18 ++ ...alling-c-ffi-functions-raising-errors.pony | 4 + code-samples/calling-c-file-path.pony | 11 + code-samples/calling-c-from-c-struct.pony | 5 + code-samples/calling-c-generic-list.c | 7 + code-samples/calling-c-generic-list.pony | 7 + code-samples/calling-c-ioctl-struct.pony | 13 ++ code-samples/calling-c-memcpy.pony | 11 + .../calling-c-pointer-to-opaque-c-type.pony | 15 ++ ...alling-c-type-signature-compatibility.pony | 5 + .../calling-c-variadic-c-functions.pony | 5 + code-samples/calling-c-writev-struct.pony | 17 ++ code-samples/calling-c-writev-tuple.pony | 5 + code-samples/linking-c-use-lib-foo.pony | 1 + .../linking-c-use-with-condition.pony | 16 ++ docs/c-ffi/c-abi.md | 90 +------- docs/c-ffi/callbacks.md | 66 +----- docs/c-ffi/calling-c.md | 192 ++---------------- docs/c-ffi/linking-c.md | 19 +- 38 files changed, 367 insertions(+), 333 deletions(-) create mode 100644 code-samples/c-abi-compile-jump-consistent-hashing-for-macos.sh create mode 100644 code-samples/c-abi-jump-consistent-hashing-header.c create mode 100644 code-samples/c-abi-jump-consistent-hashing-implementation.c create mode 100644 code-samples/c-abi-jump-consistent-hashing.pony create mode 100644 code-samples/c-abi-pony-use-native-jump-consistent-hashing-c-implementation.pony create mode 100644 code-samples/c-ffi-callbacks-bare-functions-pass-to-c-api.pony create mode 100644 code-samples/c-ffi-callbacks-bare-functions.pony create mode 100644 code-samples/c-ffi-callbacks-bare-lambda-callback.pony create mode 100644 code-samples/c-ffi-callbacks-bare-lambda-struct.pony create mode 100644 code-samples/c-ffi-callbacks-sqlite3-callback-2.pony create mode 100644 code-samples/c-ffi-callbacks-sqlite3-callback.c create mode 100644 code-samples/c-ffi-callbacks-sqlite3-callback.pony create mode 100644 code-samples/c-ffi-callbacks-struct-with-function-pointers.c create mode 100644 code-samples/calling-c-access-list-entry-with-explicit-return-type.pony create mode 100644 code-samples/calling-c-access-list-entry-without-return-type.pony create mode 100644 code-samples/calling-c-addressof.pony create mode 100644 code-samples/calling-c-default-method-in-primitive.pony create mode 100644 code-samples/calling-c-default-method-in-trait.pony create mode 100644 code-samples/calling-c-different-types-of-lists.pony create mode 100644 code-samples/calling-c-ffi-functions-raising-errors.c create mode 100644 code-samples/calling-c-ffi-functions-raising-errors.pony create mode 100644 code-samples/calling-c-file-path.pony create mode 100644 code-samples/calling-c-from-c-struct.pony create mode 100644 code-samples/calling-c-generic-list.c create mode 100644 code-samples/calling-c-generic-list.pony create mode 100644 code-samples/calling-c-ioctl-struct.pony create mode 100644 code-samples/calling-c-memcpy.pony create mode 100644 code-samples/calling-c-pointer-to-opaque-c-type.pony create mode 100644 code-samples/calling-c-type-signature-compatibility.pony create mode 100644 code-samples/calling-c-variadic-c-functions.pony create mode 100644 code-samples/calling-c-writev-struct.pony create mode 100644 code-samples/calling-c-writev-tuple.pony create mode 100644 code-samples/linking-c-use-lib-foo.pony create mode 100644 code-samples/linking-c-use-with-condition.pony diff --git a/code-samples/c-abi-compile-jump-consistent-hashing-for-macos.sh b/code-samples/c-abi-compile-jump-consistent-hashing-for-macos.sh new file mode 100644 index 00000000..6e9b0f4f --- /dev/null +++ b/code-samples/c-abi-compile-jump-consistent-hashing-for-macos.sh @@ -0,0 +1,3 @@ +clang -fPIC -Wall -Wextra -O3 -g -MM jch.c >jch.d +clang -fPIC -Wall -Wextra -O3 -g -c -o jch.o jch.c +clang -shared -lm -o libjch.dylib jch.o \ No newline at end of file diff --git a/code-samples/c-abi-jump-consistent-hashing-header.c b/code-samples/c-abi-jump-consistent-hashing-header.c new file mode 100644 index 00000000..68176f68 --- /dev/null +++ b/code-samples/c-abi-jump-consistent-hashing-header.c @@ -0,0 +1,9 @@ +#ifndef __JCH_H_ +#define __JCH_H_ + +extern "C" +{ + int32_t jch_chash(uint64_t key, uint32_t num_buckets); +} + +#endif \ No newline at end of file diff --git a/code-samples/c-abi-jump-consistent-hashing-implementation.c b/code-samples/c-abi-jump-consistent-hashing-implementation.c new file mode 100644 index 00000000..2a236251 --- /dev/null +++ b/code-samples/c-abi-jump-consistent-hashing-implementation.c @@ -0,0 +1,17 @@ +#include + +// A fast, minimal memory, consistent hash algorithm +// https://arxiv.org/abs/1406.2294 +int32_t jch_chash(uint64_t key, uint32_t num_buckets) +{ + int b = -1; + uint64_t j = 0; + + do { + b = j; + key = key * 2862933555777941757ULL + 1; + j = (b + 1) * ((double)(1LL << 31) / ((double)(key >> 33) + 1)); + } while(j < num_buckets); + + return (int32_t)b; +} \ 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.c b/code-samples/c-ffi-callbacks-sqlite3-callback.c new file mode 100644 index 00000000..07a8ac6d --- /dev/null +++ b/code-samples/c-ffi-callbacks-sqlite3-callback.c @@ -0,0 +1,16 @@ +typedef int (*sqlite3_callback)(void*,int,char**, char**); + +... + +SQLITE_API int SQLITE_STDCALL sqlite3_exec( +sqlite3 *db, /* The database on which the SQL executes */ +const char *zSql, /* The SQL to be executed */ +sqlite3_callback xCallback, /* Invoke this callback routine */ +void *pArg, /* First argument to xCallback() */ +char **pzErrMsg /* Write error messages here */ +) +{ + ... + xCallback(pArg, nCol, azVals, azCols) + ... +} \ 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/c-ffi-callbacks-struct-with-function-pointers.c b/code-samples/c-ffi-callbacks-struct-with-function-pointers.c new file mode 100644 index 00000000..fbf89449 --- /dev/null +++ b/code-samples/c-ffi-callbacks-struct-with-function-pointers.c @@ -0,0 +1,4 @@ +struct S +{ + void(*fun_ptr)(); +}; \ 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.c b/code-samples/calling-c-ffi-functions-raising-errors.c new file mode 100644 index 00000000..d322f98f --- /dev/null +++ b/code-samples/calling-c-ffi-functions-raising-errors.c @@ -0,0 +1,18 @@ +// In pony.h +PONY_API void pony_error(); + +// In socket.c +PONY_API size_t pony_os_send(asio_event_t* ev, const char* buf, size_t len) +{ + ssize_t sent = send(ev->fd, buf, len, 0); + + if(sent < 0) + { + if(errno == EWOULDBLOCK || errno == EAGAIN) + return 0; + + pony_error(); + } + + return (size_t)sent; +} \ 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.c b/code-samples/calling-c-generic-list.c new file mode 100644 index 00000000..2eb40b2c --- /dev/null +++ b/code-samples/calling-c-generic-list.c @@ -0,0 +1,7 @@ +struct List; + +struct List* list_create(); +void list_free(struct List* list); + +void list_push(struct List* list, void *data); +void* list_pop(struct List* list); \ 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/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/docs/c-ffi/c-abi.md b/docs/c-ffi/c-abi.md index 9e57a11b..c8df161c 100644 --- a/docs/c-ffi/c-abi.md +++ b/docs/c-ffi/c-abi.md @@ -9,35 +9,13 @@ 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: ```c -#ifndef __JCH_H_ -#define __JCH_H_ - -extern "C" -{ - int32_t jch_chash(uint64_t key, uint32_t num_buckets); -} - -#endif +--8<-- "c-abi-jump-consistent-hashing-header.c" ``` Note the use of `extern "C"`. If the library is built as C++ then we need to tell the compiler not to mangle the function name, otherwise, Pony won't be able to find it. For libraries built as C, this is not needed, of course. @@ -45,77 +23,19 @@ Note the use of `extern "C"`. If the library is built as C++ then we need to tel The implementation of the previous header would be something like: ```c -#include - -// A fast, minimal memory, consistent hash algorithm -// https://arxiv.org/abs/1406.2294 -int32_t jch_chash(uint64_t key, uint32_t num_buckets) -{ - int b = -1; - uint64_t j = 0; - - do { - b = j; - key = key * 2862933555777941757ULL + 1; - j = (b + 1) * ((double)(1LL << 31) / ((double)(key >> 33) + 1)); - } while(j < num_buckets); - - return (int32_t)b; -} +--8<-- "c-abi-jump-consistent-hashing-implementation.c" ``` We need to compile the native code to a shared library. This example is for MacOS. The exact details may vary on other platforms. ```bash -clang -fPIC -Wall -Wextra -O3 -g -MM jch.c >jch.d -clang -fPIC -Wall -Wextra -O3 -g -c -o jch.o jch.c -clang -shared -lm -o libjch.dylib jch.o +--8<-- "c-abi-compile-jump-consistent-hashing-for-macos.sh" ``` 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..015c5279 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,24 +27,19 @@ 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: ```c -struct S -{ - void(*fun_ptr)(); -}; +--8<-- "c-ffi-callbacks-struct-with-function-pointers.c" ``` In the same vein as bare functions, bare lambdas cannot specify captures, cannot use `this` neither as an identifier nor as a type, and cannot specify a receiver capability. In addition, a bare lambda object always has a `val` capability. @@ -58,22 +51,7 @@ Classic lambda types and bare lambda types can never be subtypes of each other. Consider SQLite, mentioned earlier. When the client code calls `sqlite3_exec`, an SQL query is executed against a database, and the callback function is called for each row returned by the SQL statement. Here's the signature for `sqlite3_exec`: ```c -typedef int (*sqlite3_callback)(void*,int,char**, char**); - -... - -SQLITE_API int SQLITE_STDCALL sqlite3_exec( -sqlite3 *db, /* The database on which the SQL executes */ -const char *zSql, /* The SQL to be executed */ -sqlite3_callback xCallback, /* Invoke this callback routine */ -void *pArg, /* First argument to xCallback() */ -char **pzErrMsg /* Write error messages here */ -) -{ - ... - xCallback(pArg, nCol, azVals, azCols) - ... -} +--8<-- "c-ffi-callbacks-sqlite3-callback.c" ``` `sqlite3_callback` is the type of the callback function that will be called by `sqlite3_exec` for each row returned by the `sql` statement. The first argument to the callback function is the pointer `pArg` that was passed to `sqlite3_exec`, the second argument is the number of columns in the row being processed, the third argument is data for each column, and the fourth argument is the name of each column. @@ -81,39 +59,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..1ea947d3 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`. @@ -172,62 +99,31 @@ As we saw earlier, you can also use a `Pointer[(U16, U16)]` as well. It is the e We mentioned before that you should use the `Pointer[None]` type in Pony when dealing with values of `void*` type in C. This is very useful for function parameters, but when we use `Pointer[None]` for the return type of a C function, we won't be able to access the value that the pointer points to. Let's imagine a generic list in C: ```C -struct List; - -struct List* list_create(); -void list_free(struct List* list); - -void list_push(struct List* list, void *data); -void* list_pop(struct List* list); +--8<-- "calling-c-generic-list.c" ``` 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 +135,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,33 +147,13 @@ 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: ```C -// In pony.h -PONY_API void pony_error(); - -// In socket.c -PONY_API size_t pony_os_send(asio_event_t* ev, const char* buf, size_t len) -{ - ssize_t sent = send(ev->fd, buf, len, 0); - - if(sent < 0) - { - if(errno == EWOULDBLOCK || errno == EAGAIN) - return 0; - - pony_error(); - } - - return (size_t)sent; -} +--8<-- "calling-c-ffi-functions-raising-errors.c" ``` A function that calls the `pony_error` function should only be called from inside a `try` block in Pony. If this is not done, the call to `pony_error` will result in a call to C's `abort` function, which will terminate the program. @@ -296,11 +168,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 +180,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 +188,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). From 5b35814eb36c7c464c0dc39b110e36edc4f88ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 06:30:57 +0200 Subject: [PATCH 23/58] refactor(code-samples): :memo: Turn code samples into snippets for "Appendices" directory --- ...ons-empty-with-nosupertype-annotation.pony | 7 + ...-empty-without-nosupertype-annotation.pony | 7 + ...tions-likely-and-unlikely-annotations.pony | 16 ++ ...pendices-annotations-nodoc-annotation.pony | 4 + ...endices-annotations-packed-annotation.pony | 3 + .../appendices-annotations-syntax.pony | 1 + .../appendices-compiler-args-ponyc.sh | 1 + .../appendices-compiler-args-stdlib-docs.sh | 2 + ...xamples-access-command-line-arguments.pony | 9 + ...es-examples-create-arrays-with-values.pony | 5 + ...ndices-examples-empty-class-functions.pony | 7 + ...numeration-with-values-with-namespace.pony | 3 + ...ices-examples-enumeration-with-values.pony | 2 + ...ndices-examples-iterable-enumerations.pony | 13 ++ ...ically-captured-variable-in-a-closure.pony | 16 ++ ...endices-examples-operator-overloading.pony | 17 ++ ...-examples-pass-array-of-values-to-ffi.pony | 18 ++ .../appendices-examples-test-helper.pony | 15 ++ ...ckage-to-parse-command-line-arguments.pony | 32 ++++ .../appendices-examples-write-tests.pony | 17 ++ .../appendices-platform-dependent-code.pony | 2 + ...ginal-object-with-deserialized-object.pony | 40 +++++ ...dices-serialization-custom-serialization.c | 39 ++++ ...es-serialization-custom-serialization.pony | 42 +++++ .../appendices-whitespace-comments.pony | 3 + ...ce-do-a-then-do-a-unary-negation-of-b.pony | 2 + .../appendices-whitespace-docstrings.pony | 21 +++ ...pendices-whitespace-subtract-b-from-a.pony | 1 + ...s-left-side-is-immutable-error-message.txt | 4 + ...error-messages-left-side-is-immutable.pony | 4 + ...-be-something-that-can-be-assigned-to.pony | 4 + ...-can-be-assigned-to.pony-error-message.txt | 8 + ...a-subtype-of-target-type-error-message.txt | 17 ++ ...-type-is-not-a-subtype-of-target-type.pony | 4 + code-samples/ponypath-unix-mac.sh | 1 + code-samples/ponypath-windows.sh | 1 + docs/appendices/annotations.md | 44 +---- docs/appendices/compiler-args.md | 5 +- docs/appendices/error-messages.md | 47 +---- docs/appendices/examples.md | 166 ++---------------- docs/appendices/platform-dependent-code.md | 3 +- docs/appendices/ponypath.md | 4 +- docs/appendices/serialisation.md | 124 +------------ docs/appendices/whitespace.md | 31 +--- 44 files changed, 424 insertions(+), 388 deletions(-) create mode 100644 code-samples/appendices-annotations-empty-with-nosupertype-annotation.pony create mode 100644 code-samples/appendices-annotations-empty-without-nosupertype-annotation.pony create mode 100644 code-samples/appendices-annotations-likely-and-unlikely-annotations.pony create mode 100644 code-samples/appendices-annotations-nodoc-annotation.pony create mode 100644 code-samples/appendices-annotations-packed-annotation.pony create mode 100644 code-samples/appendices-annotations-syntax.pony create mode 100644 code-samples/appendices-compiler-args-ponyc.sh create mode 100644 code-samples/appendices-compiler-args-stdlib-docs.sh create mode 100644 code-samples/appendices-examples-access-command-line-arguments.pony create mode 100644 code-samples/appendices-examples-create-arrays-with-values.pony create mode 100644 code-samples/appendices-examples-empty-class-functions.pony create mode 100644 code-samples/appendices-examples-enumeration-with-values-with-namespace.pony create mode 100644 code-samples/appendices-examples-enumeration-with-values.pony create mode 100644 code-samples/appendices-examples-iterable-enumerations.pony create mode 100644 code-samples/appendices-examples-modify-a-lexically-captured-variable-in-a-closure.pony create mode 100644 code-samples/appendices-examples-operator-overloading.pony create mode 100644 code-samples/appendices-examples-pass-array-of-values-to-ffi.pony create mode 100644 code-samples/appendices-examples-test-helper.pony create mode 100644 code-samples/appendices-examples-use-cli-package-to-parse-command-line-arguments.pony create mode 100644 code-samples/appendices-examples-write-tests.pony create mode 100644 code-samples/appendices-platform-dependent-code.pony create mode 100644 code-samples/appendices-serialization-compare-original-object-with-deserialized-object.pony create mode 100644 code-samples/appendices-serialization-custom-serialization.c create mode 100644 code-samples/appendices-serialization-custom-serialization.pony create mode 100644 code-samples/appendices-whitespace-comments.pony create mode 100644 code-samples/appendices-whitespace-do-a-then-do-a-unary-negation-of-b.pony create mode 100644 code-samples/appendices-whitespace-docstrings.pony create mode 100644 code-samples/appendices-whitespace-subtract-b-from-a.pony create mode 100644 code-samples/error-messages-left-side-is-immutable-error-message.txt create mode 100644 code-samples/error-messages-left-side-is-immutable.pony create mode 100644 code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony create mode 100644 code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony-error-message.txt create mode 100644 code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt create mode 100644 code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type.pony create mode 100644 code-samples/ponypath-unix-mac.sh create mode 100644 code-samples/ponypath-windows.sh 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-compiler-args-ponyc.sh b/code-samples/appendices-compiler-args-ponyc.sh new file mode 100644 index 00000000..aa401866 --- /dev/null +++ b/code-samples/appendices-compiler-args-ponyc.sh @@ -0,0 +1 @@ +ponyc [OPTIONS] \ No newline at end of file diff --git a/code-samples/appendices-compiler-args-stdlib-docs.sh b/code-samples/appendices-compiler-args-stdlib-docs.sh new file mode 100644 index 00000000..7f4c411d --- /dev/null +++ b/code-samples/appendices-compiler-args-stdlib-docs.sh @@ -0,0 +1,2 @@ + pip install mkdocs + ponyc packages/stdlib --docs && cd stdlib-docs && mkdocs serve \ 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.c b/code-samples/appendices-serialization-custom-serialization.c new file mode 100644 index 00000000..76b9155e --- /dev/null +++ b/code-samples/appendices-serialization-custom-serialization.c @@ -0,0 +1,39 @@ +// custser.c + +#include +#include + +extern char *get_string() +{ + return "hello world\n"; +} + +extern size_t serialise_space(char *s) +{ + // space for the size and the string (without the null) + return 4 + strlen(s); +} + +extern void serialise(char *buff, char *s) +{ + size_t sz = strlen(s); + unsigned char *ubuff = (unsigned char *) buff; + // write the size as a 32-bit big-endian integer + ubuff[0] = (sz >> 24) & 0xFF; + ubuff[1] = (sz >> 16) & 0xFF; + ubuff[2] = (sz >> 8) & 0xFF; + ubuff[3] = sz & 0xFF; + + // copy the string + strncpy(buff + 4, s, sz); +} + +extern char *deserialise(char *buff) +{ + unsigned char *ubuff = (unsigned char *) buff; + size_t sz = (ubuff[0] << 24) + (ubuff[1] << 16) + (ubuff[2] << 8) + ubuff[3]; + char *s = malloc(sizeof(char) * sz + 1); + memcpy(s, buff + 4, sz); + s[sz] = '\0'; + return s; +} \ 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/error-messages-left-side-is-immutable-error-message.txt b/code-samples/error-messages-left-side-is-immutable-error-message.txt new file mode 100644 index 00000000..f34b90ed --- /dev/null +++ b/code-samples/error-messages-left-side-is-immutable-error-message.txt @@ -0,0 +1,4 @@ +Error: +main.pony:4:11: left side is immutable + color = new_color + ^ \ 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-left-side-must-be-something-that-can-be-assigned-to.pony-error-message.txt b/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony-error-message.txt new file mode 100644 index 00000000..6e4a4845 --- /dev/null +++ b/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony-error-message.txt @@ -0,0 +1,8 @@ +Error: +main.pony:4:5: can't assign to a let or embed definition more than once + x = 12 + ^ +Error: +main.pony:4:7: left side must be something that can be assigned to + x = 12 + ^ \ No newline at end of file diff --git a/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt b/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt new file mode 100644 index 00000000..7e61e06b --- /dev/null +++ b/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt @@ -0,0 +1,17 @@ +Error: +main.pony:4:16: receiver type is not a subtype of target type + colors.push(color) + ^ + Info: + main.pony:4:5: receiver type: this->Array[String val] ref (which becomes 'Array[String val] box' in this context) + colors.push(color) + ^ + /root/.local/share/ponyup/ponyc-release-0.58.0-x86_64-linux-musl/packages/builtin/array.pony:623:3: target type: Array[String val] ref^ + fun ref push(value: A) => + ^ + main.pony:2:15: Array[String val] box is not a subtype of Array[String val] ref^: box is not a subcap of ref^ + let colors: Array[String] = Array[String] + ^ + main.pony:3:3: you are trying to change state in a box function; this would be possible in a ref function + fun add_stripe(color: String) => + ^ \ 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/ponypath-unix-mac.sh b/code-samples/ponypath-unix-mac.sh new file mode 100644 index 00000000..7781ea50 --- /dev/null +++ b/code-samples/ponypath-unix-mac.sh @@ -0,0 +1 @@ +export PONYPATH=$PONYPATH:$HOME/pony \ No newline at end of file diff --git a/code-samples/ponypath-windows.sh b/code-samples/ponypath-windows.sh new file mode 100644 index 00000000..e9af5d2c --- /dev/null +++ b/code-samples/ponypath-windows.sh @@ -0,0 +1 @@ +setx PONYPATH %PONYPATH%;%USERPROFILE%\pony \ 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..dd2920b4 100644 --- a/docs/appendices/compiler-args.md +++ b/docs/appendices/compiler-args.md @@ -3,7 +3,7 @@ `ponyc`, the compiler, is usually called in the project directory, where it finds the `.pony` files and its dependencies automatically. There it will create the binary based on the directory name. You can override this and tune the compilation with several options as described via `ponyc --help` and you can pass a separate source directory as an argument. ```bash -ponyc [OPTIONS] +--8<-- "appendices-compiler-args-ponyc.sh" ``` The most useful options are `--debug`, `--path` or just `-p`, `--output` or just `-o` and `--docs` or `-g`. @@ -19,8 +19,7 @@ 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 +--8<-- "appendices-compiler-args-stdlib-docs.sh" ``` 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..675594ab 100644 --- a/docs/appendices/error-messages.md +++ b/docs/appendices/error-messages.md @@ -13,23 +13,13 @@ 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: ```error -Error: -main.pony:4:5: can't assign to a let or embed definition more than once - x = 12 - ^ -Error: -main.pony:4:7: left side must be something that can be assigned to - x = 12 - ^ +--8<-- "error-messages-left-side-must-be-something-that-can-be-assigned-to-error-message.txt" ``` What happened is that you declared `x` as a constant, by writing `let x`, and then tried to assign a new value to it, 12. To fix the error, replace `let` with `var` or reconsider what value you want `x` to have. @@ -41,19 +31,13 @@ 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: ```error -Error: -main.pony:4:11: left side is immutable - color = new_color - ^ +--8<-- "error-messages-left-side-is-immutable-error-message-txt" ``` To understand this error message, you have to have some background. The field `color` is mutable since it is declared with `var`, but the method `dye` does not have an explicit receiver reference capability. The default receiver reference capability is `box`, which allows `dye` to be called on any mutable or immutable `Wombat`; the `box` reference capability says that the method may read from but not write to the receiver. As a result, it is illegal to attempt to modify the receiver in the method. @@ -65,10 +49,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. @@ -76,23 +57,7 @@ In this example, rather than trying to change the value of a field, the code cal The problem is very similar to that of the last section, but the error message is significantly more complicated: ```error -Error: -main.pony:4:16: receiver type is not a subtype of target type - colors.push(color) - ^ - Info: - main.pony:4:5: receiver type: this->Array[String val] ref (which becomes 'Array[String val] box' in this context) - colors.push(color) - ^ - /root/.local/share/ponyup/ponyc-release-0.58.0-x86_64-linux-musl/packages/builtin/array.pony:623:3: target type: Array[String val] ref^ - fun ref push(value: A) => - ^ - main.pony:2:15: Array[String val] box is not a subtype of Array[String val] ref^: box is not a subcap of ref^ - let colors: Array[String] = Array[String] - ^ - main.pony:3:3: you are trying to change state in a box function; this would be possible in a ref function - fun add_stripe(color: String) => - ^ +--8<-- "error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt" ``` Once again, Pony is trying to be helpful. The first few lines describe the error, in general terms that only a programming language maven would like: an incompatibility between the receiver type and the target type. However, Pony provides more information: the lines immediately after "Info:" tell you what it believes the receiver type to be and the next few lines describe what it believes the target type to be. Finally, the last few lines describe in detail what the problem is. 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/ponypath.md b/docs/appendices/ponypath.md index 80391c55..b114ac4b 100644 --- a/docs/appendices/ponypath.md +++ b/docs/appendices/ponypath.md @@ -11,7 +11,7 @@ Assuming you just placed new Pony code under a directory called `pony` in your h Edit/add the `rc` file corresponding to your chosen shell (`echo $SHELL` will tell you what shell you are running). For example, if using bash, add the following to your `~/.bashrc`: ```bash -export PONYPATH=$PONYPATH:$HOME/pony +--8<-- "ponypath-unix-mac.sh" ``` (Then run `source ~/.bashrc` to add this variable to a running session. New terminal session will automatically source `~/.bashrc`.) @@ -30,5 +30,5 @@ export PONYPATH=$PONYPATH:$HOME/pony You can also add to `PONYPATH` from the command prompt via: ```bash -setx PONYPATH %PONYPATH%;%USERPROFILE%\pony +--8<-- "ponypath-windows.sh" ``` diff --git a/docs/appendices/serialisation.md b/docs/appendices/serialisation.md index 7987ce8e..f0b4861a 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,88 +58,9 @@ 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 -// custser.c - -#include -#include - -extern char *get_string() -{ - return "hello world\n"; -} - -extern size_t serialise_space(char *s) -{ - // space for the size and the string (without the null) - return 4 + strlen(s); -} - -extern void serialise(char *buff, char *s) -{ - size_t sz = strlen(s); - unsigned char *ubuff = (unsigned char *) buff; - // write the size as a 32-bit big-endian integer - ubuff[0] = (sz >> 24) & 0xFF; - ubuff[1] = (sz >> 16) & 0xFF; - ubuff[2] = (sz >> 8) & 0xFF; - ubuff[3] = sz & 0xFF; - - // copy the string - strncpy(buff + 4, s, sz); -} - -extern char *deserialise(char *buff) -{ - unsigned char *ubuff = (unsigned char *) buff; - size_t sz = (ubuff[0] << 24) + (ubuff[1] << 16) + (ubuff[2] << 8) + ubuff[3]; - char *s = malloc(sizeof(char) * sz + 1); - memcpy(s, buff + 4, sz); - s[sz] = '\0'; - return s; -} +--8<-- "appendices-serialization-custom-serialization.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" ``` From 420d05a22514ff417f80c09a037c150a8cc17b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 06:33:40 +0200 Subject: [PATCH 24/58] fix(code-samples): :pencil2: Fix snippet file name --- ...e-must-be-something-that-can-be-assigned-to-error-message.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename code-samples/{error-messages-left-side-must-be-something-that-can-be-assigned-to.pony-error-message.txt => error-messages-left-side-must-be-something-that-can-be-assigned-to-error-message.txt} (100%) diff --git a/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony-error-message.txt b/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to-error-message.txt similarity index 100% rename from code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to.pony-error-message.txt rename to code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to-error-message.txt From 1a22c1a59761061a9fb887abcabb6506e1be912e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 06:35:55 +0200 Subject: [PATCH 25/58] fix(code-samples): :pencil2: Fix snippet file embed path --- docs/appendices/error-messages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/appendices/error-messages.md b/docs/appendices/error-messages.md index 675594ab..64c766fd 100644 --- a/docs/appendices/error-messages.md +++ b/docs/appendices/error-messages.md @@ -37,7 +37,7 @@ Suppose you create a class with a mutable field and added a method to change the The error message would be: ```error ---8<-- "error-messages-left-side-is-immutable-error-message-txt" +--8<-- "error-messages-left-side-is-immutable-error-message.txt" ``` To understand this error message, you have to have some background. The field `color` is mutable since it is declared with `var`, but the method `dye` does not have an explicit receiver reference capability. The default receiver reference capability is `box`, which allows `dye` to be called on any mutable or immutable `Wombat`; the `box` reference capability says that the method may read from but not write to the receiver. As a result, it is illegal to attempt to modify the receiver in the method. From 45180f54f928cf88f344848dcd1b3d52f9a341ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= Date: Mon, 22 Apr 2024 07:10:23 +0200 Subject: [PATCH 26/58] .gitignore: + /.vscode --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 From db1eb1183d0746535db719cd537ad8c00a9cc1d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 15 May 2024 17:46:45 +0200 Subject: [PATCH 27/58] =?UTF-8?q?refactor(code-samples):=20=F0=9F=93=9D=20?= =?UTF-8?q?Revert=20non-pony=20code=20samples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: jemc As requested in https://github.com/ponylang/pony-tutorial/pull/544#discussion_r1600454653 --- .../appendices-compiler-args-ponyc.sh | 1 - .../appendices-compiler-args-stdlib-docs.sh | 2 - ...dices-serialization-custom-serialization.c | 39 ------------------ ...mpile-jump-consistent-hashing-for-macos.sh | 3 -- .../c-abi-jump-consistent-hashing-header.c | 9 ----- ...i-jump-consistent-hashing-implementation.c | 17 -------- .../c-ffi-callbacks-sqlite3-callback.c | 16 -------- ...-callbacks-struct-with-function-pointers.c | 4 -- .../calling-c-ffi-functions-raising-errors.c | 18 --------- code-samples/calling-c-generic-list.c | 7 ---- ...tionals-if-else-c-ambiguous-relationship.c | 6 --- ...s-left-side-is-immutable-error-message.txt | 4 -- ...-that-can-be-assigned-to-error-message.txt | 8 ---- ...a-subtype-of-target-type-error-message.txt | 17 -------- code-samples/hello-world-compile.sh | 7 ---- code-samples/hello-world-create-directory.sh | 2 - code-samples/hello-world-run.sh | 2 - code-samples/ponypath-unix-mac.sh | 1 - code-samples/ponypath-windows.sh | 1 - .../reference-capabilities-file-open.c | 1 - code-samples/trust-boundary-safe-packages.sh | 1 - ...t-you-need-compile-pony-other-directory.sh | 1 - code-samples/what-you-need-compile-pony.sh | 1 - .../what-you-need-run-python-shebang.sh | 1 - code-samples/what-you-need-run-python.sh | 1 - docs/appendices/compiler-args.md | 5 ++- docs/appendices/error-messages.md | 32 +++++++++++++-- docs/appendices/ponypath.md | 4 +- docs/appendices/serialisation.md | 40 ++++++++++++++++++- docs/c-ffi/c-abi.md | 32 +++++++++++++-- docs/c-ffi/callbacks.md | 22 +++++++++- docs/c-ffi/calling-c.md | 27 ++++++++++++- docs/expressions/control-structures.md | 7 +++- docs/getting-started/hello-world.md | 14 +++++-- docs/getting-started/what-you-need.md | 8 ++-- docs/object-capabilities/trust-boundary.md | 2 +- .../reference-capabilities.md | 2 +- 37 files changed, 170 insertions(+), 195 deletions(-) delete mode 100644 code-samples/appendices-compiler-args-ponyc.sh delete mode 100644 code-samples/appendices-compiler-args-stdlib-docs.sh delete mode 100644 code-samples/appendices-serialization-custom-serialization.c delete mode 100644 code-samples/c-abi-compile-jump-consistent-hashing-for-macos.sh delete mode 100644 code-samples/c-abi-jump-consistent-hashing-header.c delete mode 100644 code-samples/c-abi-jump-consistent-hashing-implementation.c delete mode 100644 code-samples/c-ffi-callbacks-sqlite3-callback.c delete mode 100644 code-samples/c-ffi-callbacks-struct-with-function-pointers.c delete mode 100644 code-samples/calling-c-ffi-functions-raising-errors.c delete mode 100644 code-samples/calling-c-generic-list.c delete mode 100644 code-samples/control-structures-conditionals-if-else-c-ambiguous-relationship.c delete mode 100644 code-samples/error-messages-left-side-is-immutable-error-message.txt delete mode 100644 code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to-error-message.txt delete mode 100644 code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt delete mode 100644 code-samples/hello-world-compile.sh delete mode 100644 code-samples/hello-world-create-directory.sh delete mode 100644 code-samples/hello-world-run.sh delete mode 100644 code-samples/ponypath-unix-mac.sh delete mode 100644 code-samples/ponypath-windows.sh delete mode 100644 code-samples/reference-capabilities-file-open.c delete mode 100644 code-samples/trust-boundary-safe-packages.sh delete mode 100644 code-samples/what-you-need-compile-pony-other-directory.sh delete mode 100644 code-samples/what-you-need-compile-pony.sh delete mode 100644 code-samples/what-you-need-run-python-shebang.sh delete mode 100644 code-samples/what-you-need-run-python.sh diff --git a/code-samples/appendices-compiler-args-ponyc.sh b/code-samples/appendices-compiler-args-ponyc.sh deleted file mode 100644 index aa401866..00000000 --- a/code-samples/appendices-compiler-args-ponyc.sh +++ /dev/null @@ -1 +0,0 @@ -ponyc [OPTIONS] \ No newline at end of file diff --git a/code-samples/appendices-compiler-args-stdlib-docs.sh b/code-samples/appendices-compiler-args-stdlib-docs.sh deleted file mode 100644 index 7f4c411d..00000000 --- a/code-samples/appendices-compiler-args-stdlib-docs.sh +++ /dev/null @@ -1,2 +0,0 @@ - pip install mkdocs - ponyc packages/stdlib --docs && cd stdlib-docs && mkdocs serve \ No newline at end of file diff --git a/code-samples/appendices-serialization-custom-serialization.c b/code-samples/appendices-serialization-custom-serialization.c deleted file mode 100644 index 76b9155e..00000000 --- a/code-samples/appendices-serialization-custom-serialization.c +++ /dev/null @@ -1,39 +0,0 @@ -// custser.c - -#include -#include - -extern char *get_string() -{ - return "hello world\n"; -} - -extern size_t serialise_space(char *s) -{ - // space for the size and the string (without the null) - return 4 + strlen(s); -} - -extern void serialise(char *buff, char *s) -{ - size_t sz = strlen(s); - unsigned char *ubuff = (unsigned char *) buff; - // write the size as a 32-bit big-endian integer - ubuff[0] = (sz >> 24) & 0xFF; - ubuff[1] = (sz >> 16) & 0xFF; - ubuff[2] = (sz >> 8) & 0xFF; - ubuff[3] = sz & 0xFF; - - // copy the string - strncpy(buff + 4, s, sz); -} - -extern char *deserialise(char *buff) -{ - unsigned char *ubuff = (unsigned char *) buff; - size_t sz = (ubuff[0] << 24) + (ubuff[1] << 16) + (ubuff[2] << 8) + ubuff[3]; - char *s = malloc(sizeof(char) * sz + 1); - memcpy(s, buff + 4, sz); - s[sz] = '\0'; - return s; -} \ No newline at end of file diff --git a/code-samples/c-abi-compile-jump-consistent-hashing-for-macos.sh b/code-samples/c-abi-compile-jump-consistent-hashing-for-macos.sh deleted file mode 100644 index 6e9b0f4f..00000000 --- a/code-samples/c-abi-compile-jump-consistent-hashing-for-macos.sh +++ /dev/null @@ -1,3 +0,0 @@ -clang -fPIC -Wall -Wextra -O3 -g -MM jch.c >jch.d -clang -fPIC -Wall -Wextra -O3 -g -c -o jch.o jch.c -clang -shared -lm -o libjch.dylib jch.o \ No newline at end of file diff --git a/code-samples/c-abi-jump-consistent-hashing-header.c b/code-samples/c-abi-jump-consistent-hashing-header.c deleted file mode 100644 index 68176f68..00000000 --- a/code-samples/c-abi-jump-consistent-hashing-header.c +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __JCH_H_ -#define __JCH_H_ - -extern "C" -{ - int32_t jch_chash(uint64_t key, uint32_t num_buckets); -} - -#endif \ No newline at end of file diff --git a/code-samples/c-abi-jump-consistent-hashing-implementation.c b/code-samples/c-abi-jump-consistent-hashing-implementation.c deleted file mode 100644 index 2a236251..00000000 --- a/code-samples/c-abi-jump-consistent-hashing-implementation.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -// A fast, minimal memory, consistent hash algorithm -// https://arxiv.org/abs/1406.2294 -int32_t jch_chash(uint64_t key, uint32_t num_buckets) -{ - int b = -1; - uint64_t j = 0; - - do { - b = j; - key = key * 2862933555777941757ULL + 1; - j = (b + 1) * ((double)(1LL << 31) / ((double)(key >> 33) + 1)); - } while(j < num_buckets); - - return (int32_t)b; -} \ No newline at end of file diff --git a/code-samples/c-ffi-callbacks-sqlite3-callback.c b/code-samples/c-ffi-callbacks-sqlite3-callback.c deleted file mode 100644 index 07a8ac6d..00000000 --- a/code-samples/c-ffi-callbacks-sqlite3-callback.c +++ /dev/null @@ -1,16 +0,0 @@ -typedef int (*sqlite3_callback)(void*,int,char**, char**); - -... - -SQLITE_API int SQLITE_STDCALL sqlite3_exec( -sqlite3 *db, /* The database on which the SQL executes */ -const char *zSql, /* The SQL to be executed */ -sqlite3_callback xCallback, /* Invoke this callback routine */ -void *pArg, /* First argument to xCallback() */ -char **pzErrMsg /* Write error messages here */ -) -{ - ... - xCallback(pArg, nCol, azVals, azCols) - ... -} \ No newline at end of file diff --git a/code-samples/c-ffi-callbacks-struct-with-function-pointers.c b/code-samples/c-ffi-callbacks-struct-with-function-pointers.c deleted file mode 100644 index fbf89449..00000000 --- a/code-samples/c-ffi-callbacks-struct-with-function-pointers.c +++ /dev/null @@ -1,4 +0,0 @@ -struct S -{ - void(*fun_ptr)(); -}; \ No newline at end of file diff --git a/code-samples/calling-c-ffi-functions-raising-errors.c b/code-samples/calling-c-ffi-functions-raising-errors.c deleted file mode 100644 index d322f98f..00000000 --- a/code-samples/calling-c-ffi-functions-raising-errors.c +++ /dev/null @@ -1,18 +0,0 @@ -// In pony.h -PONY_API void pony_error(); - -// In socket.c -PONY_API size_t pony_os_send(asio_event_t* ev, const char* buf, size_t len) -{ - ssize_t sent = send(ev->fd, buf, len, 0); - - if(sent < 0) - { - if(errno == EWOULDBLOCK || errno == EAGAIN) - return 0; - - pony_error(); - } - - return (size_t)sent; -} \ No newline at end of file diff --git a/code-samples/calling-c-generic-list.c b/code-samples/calling-c-generic-list.c deleted file mode 100644 index 2eb40b2c..00000000 --- a/code-samples/calling-c-generic-list.c +++ /dev/null @@ -1,7 +0,0 @@ -struct List; - -struct List* list_create(); -void list_free(struct List* list); - -void list_push(struct List* list, void *data); -void* list_pop(struct List* list); \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-if-else-c-ambiguous-relationship.c b/code-samples/control-structures-conditionals-if-else-c-ambiguous-relationship.c deleted file mode 100644 index e00e552b..00000000 --- a/code-samples/control-structures-conditionals-if-else-c-ambiguous-relationship.c +++ /dev/null @@ -1,6 +0,0 @@ -// C code -if(a) - if(b) - printf("a and b\n"); -else - printf("not a\n"); \ No newline at end of file diff --git a/code-samples/error-messages-left-side-is-immutable-error-message.txt b/code-samples/error-messages-left-side-is-immutable-error-message.txt deleted file mode 100644 index f34b90ed..00000000 --- a/code-samples/error-messages-left-side-is-immutable-error-message.txt +++ /dev/null @@ -1,4 +0,0 @@ -Error: -main.pony:4:11: left side is immutable - 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-error-message.txt b/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to-error-message.txt deleted file mode 100644 index 6e4a4845..00000000 --- a/code-samples/error-messages-left-side-must-be-something-that-can-be-assigned-to-error-message.txt +++ /dev/null @@ -1,8 +0,0 @@ -Error: -main.pony:4:5: can't assign to a let or embed definition more than once - x = 12 - ^ -Error: -main.pony:4:7: left side must be something that can be assigned to - x = 12 - ^ \ No newline at end of file diff --git a/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt b/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt deleted file mode 100644 index 7e61e06b..00000000 --- a/code-samples/error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt +++ /dev/null @@ -1,17 +0,0 @@ -Error: -main.pony:4:16: receiver type is not a subtype of target type - colors.push(color) - ^ - Info: - main.pony:4:5: receiver type: this->Array[String val] ref (which becomes 'Array[String val] box' in this context) - colors.push(color) - ^ - /root/.local/share/ponyup/ponyc-release-0.58.0-x86_64-linux-musl/packages/builtin/array.pony:623:3: target type: Array[String val] ref^ - fun ref push(value: A) => - ^ - main.pony:2:15: Array[String val] box is not a subtype of Array[String val] ref^: box is not a subcap of ref^ - let colors: Array[String] = Array[String] - ^ - main.pony:3:3: you are trying to change state in a box function; this would be possible in a ref function - fun add_stripe(color: String) => - ^ \ No newline at end of file diff --git a/code-samples/hello-world-compile.sh b/code-samples/hello-world-compile.sh deleted file mode 100644 index d02070a6..00000000 --- a/code-samples/hello-world-compile.sh +++ /dev/null @@ -1,7 +0,0 @@ -$ ponyc -Building . -Building builtin -Generating -Optimising -Writing ./helloworld.o -Linking ./helloworld \ No newline at end of file diff --git a/code-samples/hello-world-create-directory.sh b/code-samples/hello-world-create-directory.sh deleted file mode 100644 index 70d70180..00000000 --- a/code-samples/hello-world-create-directory.sh +++ /dev/null @@ -1,2 +0,0 @@ -mkdir helloworld -cd helloworld \ No newline at end of file diff --git a/code-samples/hello-world-run.sh b/code-samples/hello-world-run.sh deleted file mode 100644 index e72ed104..00000000 --- a/code-samples/hello-world-run.sh +++ /dev/null @@ -1,2 +0,0 @@ -$ ./helloworld -Hello, world! \ No newline at end of file diff --git a/code-samples/ponypath-unix-mac.sh b/code-samples/ponypath-unix-mac.sh deleted file mode 100644 index 7781ea50..00000000 --- a/code-samples/ponypath-unix-mac.sh +++ /dev/null @@ -1 +0,0 @@ -export PONYPATH=$PONYPATH:$HOME/pony \ No newline at end of file diff --git a/code-samples/ponypath-windows.sh b/code-samples/ponypath-windows.sh deleted file mode 100644 index e9af5d2c..00000000 --- a/code-samples/ponypath-windows.sh +++ /dev/null @@ -1 +0,0 @@ -setx PONYPATH %PONYPATH%;%USERPROFILE%\pony \ No newline at end of file diff --git a/code-samples/reference-capabilities-file-open.c b/code-samples/reference-capabilities-file-open.c deleted file mode 100644 index 11f44b39..00000000 --- a/code-samples/reference-capabilities-file-open.c +++ /dev/null @@ -1 +0,0 @@ -int fd = open("/etc/passwd", O_RDWR); \ No newline at end of file diff --git a/code-samples/trust-boundary-safe-packages.sh b/code-samples/trust-boundary-safe-packages.sh deleted file mode 100644 index 0818fb8b..00000000 --- a/code-samples/trust-boundary-safe-packages.sh +++ /dev/null @@ -1 +0,0 @@ -ponyc --safe=files:net:process my_project \ No newline at end of file diff --git a/code-samples/what-you-need-compile-pony-other-directory.sh b/code-samples/what-you-need-compile-pony-other-directory.sh deleted file mode 100644 index fbe3a6f0..00000000 --- a/code-samples/what-you-need-compile-pony-other-directory.sh +++ /dev/null @@ -1 +0,0 @@ -ponyc path/to/my/code \ No newline at end of file diff --git a/code-samples/what-you-need-compile-pony.sh b/code-samples/what-you-need-compile-pony.sh deleted file mode 100644 index f3fe9dd3..00000000 --- a/code-samples/what-you-need-compile-pony.sh +++ /dev/null @@ -1 +0,0 @@ -ponyc \ No newline at end of file diff --git a/code-samples/what-you-need-run-python-shebang.sh b/code-samples/what-you-need-run-python-shebang.sh deleted file mode 100644 index cb693ca3..00000000 --- a/code-samples/what-you-need-run-python-shebang.sh +++ /dev/null @@ -1 +0,0 @@ -./helloworld.py \ No newline at end of file diff --git a/code-samples/what-you-need-run-python.sh b/code-samples/what-you-need-run-python.sh deleted file mode 100644 index 48920769..00000000 --- a/code-samples/what-you-need-run-python.sh +++ /dev/null @@ -1 +0,0 @@ -python helloworld.py \ No newline at end of file diff --git a/docs/appendices/compiler-args.md b/docs/appendices/compiler-args.md index dd2920b4..0452dff1 100644 --- a/docs/appendices/compiler-args.md +++ b/docs/appendices/compiler-args.md @@ -3,7 +3,7 @@ `ponyc`, the compiler, is usually called in the project directory, where it finds the `.pony` files and its dependencies automatically. There it will create the binary based on the directory name. You can override this and tune the compilation with several options as described via `ponyc --help` and you can pass a separate source directory as an argument. ```bash ---8<-- "appendices-compiler-args-ponyc.sh" +ponyc [OPTIONS] ``` The most useful options are `--debug`, `--path` or just `-p`, `--output` or just `-o` and `--docs` or `-g`. @@ -19,7 +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 ---8<-- "appendices-compiler-args-stdlib-docs.sh" +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 64c766fd..effc5fbf 100644 --- a/docs/appendices/error-messages.md +++ b/docs/appendices/error-messages.md @@ -19,7 +19,14 @@ Suppose you wrote: The error message would be: ```error ---8<-- "error-messages-left-side-must-be-something-that-can-be-assigned-to-error-message.txt" +Error: +main.pony:4:5: can't assign to a let or embed definition more than once + x = 12 + ^ +Error: +main.pony:4:7: left side must be something that can be assigned to + x = 12 + ^ ``` What happened is that you declared `x` as a constant, by writing `let x`, and then tried to assign a new value to it, 12. To fix the error, replace `let` with `var` or reconsider what value you want `x` to have. @@ -37,7 +44,10 @@ Suppose you create a class with a mutable field and added a method to change the The error message would be: ```error ---8<-- "error-messages-left-side-is-immutable-error-message.txt" +Error: +main.pony:4:11: left side is immutable + color = new_color + ^ ``` To understand this error message, you have to have some background. The field `color` is mutable since it is declared with `var`, but the method `dye` does not have an explicit receiver reference capability. The default receiver reference capability is `box`, which allows `dye` to be called on any mutable or immutable `Wombat`; the `box` reference capability says that the method may read from but not write to the receiver. As a result, it is illegal to attempt to modify the receiver in the method. @@ -57,7 +67,23 @@ In this example, rather than trying to change the value of a field, the code cal The problem is very similar to that of the last section, but the error message is significantly more complicated: ```error ---8<-- "error-messages-receiver-type-is-not-a-subtype-of-target-type-error-message.txt" +Error: +main.pony:4:16: receiver type is not a subtype of target type + colors.push(color) + ^ + Info: + main.pony:4:5: receiver type: this->Array[String val] ref (which becomes 'Array[String val] box' in this context) + colors.push(color) + ^ + /root/.local/share/ponyup/ponyc-release-0.58.0-x86_64-linux-musl/packages/builtin/array.pony:623:3: target type: Array[String val] ref^ + fun ref push(value: A) => + ^ + main.pony:2:15: Array[String val] box is not a subtype of Array[String val] ref^: box is not a subcap of ref^ + let colors: Array[String] = Array[String] + ^ + main.pony:3:3: you are trying to change state in a box function; this would be possible in a ref function + fun add_stripe(color: String) => + ^ ``` Once again, Pony is trying to be helpful. The first few lines describe the error, in general terms that only a programming language maven would like: an incompatibility between the receiver type and the target type. However, Pony provides more information: the lines immediately after "Info:" tell you what it believes the receiver type to be and the next few lines describe what it believes the target type to be. Finally, the last few lines describe in detail what the problem is. diff --git a/docs/appendices/ponypath.md b/docs/appendices/ponypath.md index b114ac4b..80391c55 100644 --- a/docs/appendices/ponypath.md +++ b/docs/appendices/ponypath.md @@ -11,7 +11,7 @@ Assuming you just placed new Pony code under a directory called `pony` in your h Edit/add the `rc` file corresponding to your chosen shell (`echo $SHELL` will tell you what shell you are running). For example, if using bash, add the following to your `~/.bashrc`: ```bash ---8<-- "ponypath-unix-mac.sh" +export PONYPATH=$PONYPATH:$HOME/pony ``` (Then run `source ~/.bashrc` to add this variable to a running session. New terminal session will automatically source `~/.bashrc`.) @@ -30,5 +30,5 @@ Edit/add the `rc` file corresponding to your chosen shell (`echo $SHELL` will te You can also add to `PONYPATH` from the command prompt via: ```bash ---8<-- "ponypath-windows.sh" +setx PONYPATH %PONYPATH%;%USERPROFILE%\pony ``` diff --git a/docs/appendices/serialisation.md b/docs/appendices/serialisation.md index f0b4861a..1d318776 100644 --- a/docs/appendices/serialisation.md +++ b/docs/appendices/serialisation.md @@ -62,5 +62,43 @@ Assume we have a Pony class with a field that is a pointer to a C string. We wou ``` ```c ---8<-- "appendices-serialization-custom-serialization.c" +// custser.c + +#include +#include + +extern char *get_string() +{ + return "hello world\n"; +} + +extern size_t serialise_space(char *s) +{ + // space for the size and the string (without the null) + return 4 + strlen(s); +} + +extern void serialise(char *buff, char *s) +{ + size_t sz = strlen(s); + unsigned char *ubuff = (unsigned char *) buff; + // write the size as a 32-bit big-endian integer + ubuff[0] = (sz >> 24) & 0xFF; + ubuff[1] = (sz >> 16) & 0xFF; + ubuff[2] = (sz >> 8) & 0xFF; + ubuff[3] = sz & 0xFF; + + // copy the string + strncpy(buff + 4, s, sz); +} + +extern char *deserialise(char *buff) +{ + unsigned char *ubuff = (unsigned char *) buff; + size_t sz = (ubuff[0] << 24) + (ubuff[1] << 16) + (ubuff[2] << 8) + ubuff[3]; + char *s = malloc(sizeof(char) * sz + 1); + memcpy(s, buff + 4, sz); + s[sz] = '\0'; + return s; +} ``` diff --git a/docs/c-ffi/c-abi.md b/docs/c-ffi/c-abi.md index c8df161c..cd9fc54a 100644 --- a/docs/c-ffi/c-abi.md +++ b/docs/c-ffi/c-abi.md @@ -15,7 +15,15 @@ Let's look at a complete example of a C function we may wish to provide to Pony. Let's say we wish to compare the pure Pony performance to an existing C function with the following header: ```c ---8<-- "c-abi-jump-consistent-hashing-header.c" +#ifndef __JCH_H_ +#define __JCH_H_ + +extern "C" +{ + int32_t jch_chash(uint64_t key, uint32_t num_buckets); +} + +#endif ``` Note the use of `extern "C"`. If the library is built as C++ then we need to tell the compiler not to mangle the function name, otherwise, Pony won't be able to find it. For libraries built as C, this is not needed, of course. @@ -23,13 +31,31 @@ Note the use of `extern "C"`. If the library is built as C++ then we need to tel The implementation of the previous header would be something like: ```c ---8<-- "c-abi-jump-consistent-hashing-implementation.c" +#include + +// A fast, minimal memory, consistent hash algorithm +// https://arxiv.org/abs/1406.2294 +int32_t jch_chash(uint64_t key, uint32_t num_buckets) +{ + int b = -1; + uint64_t j = 0; + + do { + b = j; + key = key * 2862933555777941757ULL + 1; + j = (b + 1) * ((double)(1LL << 31) / ((double)(key >> 33) + 1)); + } while(j < num_buckets); + + return (int32_t)b; +} ``` We need to compile the native code to a shared library. This example is for MacOS. The exact details may vary on other platforms. ```bash ---8<-- "c-abi-compile-jump-consistent-hashing-for-macos.sh" +clang -fPIC -Wall -Wextra -O3 -g -MM jch.c >jch.d +clang -fPIC -Wall -Wextra -O3 -g -c -o jch.o jch.c +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. diff --git a/docs/c-ffi/callbacks.md b/docs/c-ffi/callbacks.md index 015c5279..e993dd29 100644 --- a/docs/c-ffi/callbacks.md +++ b/docs/c-ffi/callbacks.md @@ -39,7 +39,10 @@ Bare lambdas can also be used to define structures containing function pointers. This Pony structure is equivalent to the following C structure: ```c ---8<-- "c-ffi-callbacks-struct-with-function-pointers.c" +struct S +{ + void(*fun_ptr)(); +}; ``` In the same vein as bare functions, bare lambdas cannot specify captures, cannot use `this` neither as an identifier nor as a type, and cannot specify a receiver capability. In addition, a bare lambda object always has a `val` capability. @@ -51,7 +54,22 @@ Classic lambda types and bare lambda types can never be subtypes of each other. Consider SQLite, mentioned earlier. When the client code calls `sqlite3_exec`, an SQL query is executed against a database, and the callback function is called for each row returned by the SQL statement. Here's the signature for `sqlite3_exec`: ```c ---8<-- "c-ffi-callbacks-sqlite3-callback.c" +typedef int (*sqlite3_callback)(void*,int,char**, char**); + +... + +SQLITE_API int SQLITE_STDCALL sqlite3_exec( +sqlite3 *db, /* The database on which the SQL executes */ +const char *zSql, /* The SQL to be executed */ +sqlite3_callback xCallback, /* Invoke this callback routine */ +void *pArg, /* First argument to xCallback() */ +char **pzErrMsg /* Write error messages here */ +) +{ + ... + xCallback(pArg, nCol, azVals, azCols) + ... +} ``` `sqlite3_callback` is the type of the callback function that will be called by `sqlite3_exec` for each row returned by the `sql` statement. The first argument to the callback function is the pointer `pArg` that was passed to `sqlite3_exec`, the second argument is the number of columns in the row being processed, the third argument is data for each column, and the fourth argument is the name of each column. diff --git a/docs/c-ffi/calling-c.md b/docs/c-ffi/calling-c.md index 1ea947d3..a0e90b08 100644 --- a/docs/c-ffi/calling-c.md +++ b/docs/c-ffi/calling-c.md @@ -99,7 +99,13 @@ As we saw earlier, you can also use a `Pointer[(U16, U16)]` as well. It is the e We mentioned before that you should use the `Pointer[None]` type in Pony when dealing with values of `void*` type in C. This is very useful for function parameters, but when we use `Pointer[None]` for the return type of a C function, we won't be able to access the value that the pointer points to. Let's imagine a generic list in C: ```C ---8<-- "calling-c-generic-list.c" +struct List; + +struct List* list_create(); +void list_free(struct List* list); + +void list_push(struct List* list, void *data); +void* list_pop(struct List* list); ``` Following the advice from previous sections, we can write the following Pony declarations: @@ -153,7 +159,24 @@ FFI calls to functions that __might__ raise an error __must__ mark it as such by 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: ```C ---8<-- "calling-c-ffi-functions-raising-errors.c" +// In pony.h +PONY_API void pony_error(); + +// In socket.c +PONY_API size_t pony_os_send(asio_event_t* ev, const char* buf, size_t len) +{ + ssize_t sent = send(ev->fd, buf, len, 0); + + if(sent < 0) + { + if(errno == EWOULDBLOCK || errno == EAGAIN) + return 0; + + pony_error(); + } + + return (size_t)sent; +} ``` A function that calls the `pony_error` function should only be called from inside a `try` block in Pony. If this is not done, the call to `pony_error` will result in a call to C's `abort` function, which will terminate the program. diff --git a/docs/expressions/control-structures.md b/docs/expressions/control-structures.md index 2a4044c8..b79e4116 100644 --- a/docs/expressions/control-structures.md +++ b/docs/expressions/control-structures.md @@ -33,7 +33,12 @@ As an alternative Pony provides the `elseif` keyword that combines an `else` and __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: ```c ---8<-- "control-structures-conditionals-if-else-c-ambiguous-relationship.c" +// C code +if(a) + if(b) + printf("a and b\n"); +else + printf("not a\n"); ``` Here it is not obvious whether the `else` is an alternative to the first or the second `if`. In fact here the `else` relates to the `if(b)` so our example contains a bug. Pony avoids this type of bug by handling `if` and `else` differently and the need for `elseif` comes out of that. diff --git a/docs/getting-started/hello-world.md b/docs/getting-started/hello-world.md index f7690419..09cdcc1b 100644 --- a/docs/getting-started/hello-world.md +++ b/docs/getting-started/hello-world.md @@ -3,7 +3,8 @@ Now that you've successfully installed the Pony compiler, let's start programming! Our first program will be a very traditional one. We're going to print "Hello, world!". First, create a directory called `helloworld`: ```bash ---8<-- "hello-world-create-directory.sh" +mkdir helloworld +cd helloworld ``` __Does the name of the directory matter?__ Yes, it does. It's the name of your program! By default when your program is compiled, the resulting executable binary will have the same name as the directory your program lives in. You can also set the name using the --bin-name or -b options on the command line. @@ -25,7 +26,13 @@ In your file, put the following code: Now compile it: ```bash ---8<-- "hello-world-compile.sh" +$ ponyc +Building . +Building builtin +Generating +Optimising +Writing ./helloworld.o +Linking ./helloworld ``` (If you're using Docker, you'd write something like `$ docker run -v Some_Absolute_Path/helloworld:/src/main ponylang/ponyc`, depending of course on what the absolute path to your `helloworld` directory is.) @@ -39,7 +46,8 @@ __Wait, it linked too?__ Yes. You won't need a build system (like `make`) for Po Now we can run the program: ```bash ---8<-- "hello-world-run.sh" +$ ./helloworld +Hello, world! ``` Congratulations, you've written your first Pony program! Next, we'll explain what some of that code does. diff --git a/docs/getting-started/what-you-need.md b/docs/getting-started/what-you-need.md index c0cd24cc..bf5c4151 100644 --- a/docs/getting-started/what-you-need.md +++ b/docs/getting-started/what-you-need.md @@ -19,13 +19,13 @@ What this means is that once you build your program, you can run it over and ove But it also means you need to build your program before you can run it. In an interpreted language or a JIT compiled language, you tend to do things like this to run your program: ```bash ---8<-- "what-you-need-run-python.sh" +python helloworld.py ``` Or maybe you put a __shebang__ in your program (like `#!/usr/bin/env python`), then `chmod` to set the executable bit, and then do: ```bash ---8<-- "what-you-need-run-python-shebang.sh" +./helloworld.py ``` When you use Pony, you don't do any of that! @@ -35,13 +35,13 @@ When you use Pony, you don't do any of that! If you are in the same directory as your program, you can just do: ```bash ---8<-- "what-you-need-compile-pony.sh" +ponyc ``` That tells the Pony compiler that your current working directory contains your source code, and to please compile it. If your source code is in some other directory, you can tell ponyc where it is: ```bash ---8<-- "what-you-need-compile-pony-other-directory.sh" +ponyc path/to/my/code ``` There are other options as well, but we'll cover those later. diff --git a/docs/object-capabilities/trust-boundary.md b/docs/object-capabilities/trust-boundary.md index 1b7d26db..a26165cb 100644 --- a/docs/object-capabilities/trust-boundary.md +++ b/docs/object-capabilities/trust-boundary.md @@ -17,7 +17,7 @@ But we can do better than that. In Pony, you can optionally declare a set of _safe_ packages on the `ponyc` command line, like this: ```sh ---8<-- "trust-boundary-safe-packages.sh" +ponyc --safe=files:net:process my_project ``` Here, we are declaring that only the `files`, `net` and `process` packages are allowed to use C-FFI calls. We've established our trust boundary: any other packages that try to use C-FFI calls will result in a compile-time error. diff --git a/docs/reference-capabilities/reference-capabilities.md b/docs/reference-capabilities/reference-capabilities.md index 20e11e16..011a4958 100644 --- a/docs/reference-capabilities/reference-capabilities.md +++ b/docs/reference-capabilities/reference-capabilities.md @@ -9,7 +9,7 @@ In Pony, we do it with _reference capabilities_. If you open a file in UNIX and get a file descriptor back, that file descriptor is a token that designates an object - but it isn't a capability. To be a capability, we need to open that file with some permission - some access right. For example: ```C ---8<-- "reference-capabilities-file-open.c" +int fd = open("/etc/passwd", O_RDWR); ``` Now we have a token and a set of rights. From bbfe5a360fe0594e23ab954432dc22ae87d2d939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 21 May 2024 00:48:16 +0200 Subject: [PATCH 28/58] Make "classes" and "traits and interfaces" runnable --- code-samples/classes-swap-values-sugar.pony | 8 +++++++- code-samples/classes-swap-values.pony | 12 ++++++++--- ...classes-wombat-constructor-invocation.pony | 2 -- code-samples/classes-wombat-constructors.pony | 7 +++++++ code-samples/classes-wombat.pony | 8 ++++++++ ...zero-argument-constructors-invocation.pony | 3 --- .../classes-zero-argument-constructors.pony | 8 ++++++++ ...traits-and-interfaces-multiple-traits.pony | 5 +++++ .../traits-and-interfaces-nested-traits.pony | 5 +++++ ...aces-nominal-and-structural-subtyping.pony | 7 +++++++ ...-interfaces-nominal-subtyping-in-pony.pony | 5 +++++ ...s-and-interfaces-open-world-interface.pony | 1 - ...aits-and-interfaces-open-world-typing.pony | 19 ++++++++++++++++++ code-samples/traits-and-interfaces-trait.pony | 5 +++++ docs/types/classes.md | 20 +++++++++---------- docs/types/traits-and-interfaces.md | 14 ++++++------- 16 files changed, 102 insertions(+), 27 deletions(-) delete mode 100644 code-samples/classes-wombat-constructor-invocation.pony delete mode 100644 code-samples/classes-zero-argument-constructors-invocation.pony delete mode 100644 code-samples/traits-and-interfaces-open-world-interface.pony diff --git a/code-samples/classes-swap-values-sugar.pony b/code-samples/classes-swap-values-sugar.pony index 9c8eb05c..423765b7 100644 --- a/code-samples/classes-swap-values-sugar.pony +++ b/code-samples/classes-swap-values-sugar.pony @@ -1 +1,7 @@ -a = b = a \ No newline at end of file +actor Main + new create(env: Env) => + var a: U64 = 1 + var b: U64 = 2 + env.out.print("a = " + a.string() + ", b = " + b.string()) + a = b = a + env.out.print("a = " + a.string() + ", b = " + b.string()) \ No newline at end of file diff --git a/code-samples/classes-swap-values.pony b/code-samples/classes-swap-values.pony index 97937f3c..30216ebc 100644 --- a/code-samples/classes-swap-values.pony +++ b/code-samples/classes-swap-values.pony @@ -1,3 +1,9 @@ -var temp = a -a = b -b = temp \ No newline at end of file +actor Main + new create(env: Env) => + var a: U64 = 1 + var b: U64 = 2 + env.out.print("a = " + a.string() + ", b = " + b.string()) + var temp = a + a = b + b = temp + env.out.print("a = " + a.string() + ", b = " + b.string()) \ No newline at end of file diff --git a/code-samples/classes-wombat-constructor-invocation.pony b/code-samples/classes-wombat-constructor-invocation.pony deleted file mode 100644 index ed12e112..00000000 --- a/code-samples/classes-wombat-constructor-invocation.pony +++ /dev/null @@ -1,2 +0,0 @@ -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 index 4e9c3863..f151d095 100644 --- a/code-samples/classes-wombat-constructors.pony +++ b/code-samples/classes-wombat-constructors.pony @@ -1,3 +1,10 @@ +actor Main + new create(env: Env) => + let defaultWombat = Wombat("Fantastibat") // Invokes the create method by default + let hungryWombat = Wombat.hungry("Nomsbat", 12) // Invokes the hungry method + env.out.print("Your default wombat's name is \"" + defaultWombat.name + "\"") + env.out.print("Your hungry wombat's name is \"" + hungryWombat.name + "\"") + class Wombat let name: String var _hunger_level: U64 diff --git a/code-samples/classes-wombat.pony b/code-samples/classes-wombat.pony index 1c647876..4d240360 100644 --- a/code-samples/classes-wombat.pony +++ b/code-samples/classes-wombat.pony @@ -1,3 +1,11 @@ +actor Main + new create(env: Env) => + let defaultWombat = Wombat("Fantastibat") // Invokes the create method by default + let hungryWombat = Wombat.hungry("Nomsbat", 12) // Invokes the hungry method + defaultWombat.set_hunger(5) + env.out.print(defaultWombat.name + " has a hunger level of " + defaultWombat.hunger().string()) + env.out.print(hungryWombat.name + " has a hunger level of " + hungryWombat.hunger().string()) + class Wombat let name: String var _hunger_level: U64 diff --git a/code-samples/classes-zero-argument-constructors-invocation.pony b/code-samples/classes-zero-argument-constructors-invocation.pony deleted file mode 100644 index 67b82087..00000000 --- a/code-samples/classes-zero-argument-constructors-invocation.pony +++ /dev/null @@ -1,3 +0,0 @@ -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 index 58521846..c49825d1 100644 --- a/code-samples/classes-zero-argument-constructors.pony +++ b/code-samples/classes-zero-argument-constructors.pony @@ -1,3 +1,11 @@ +actor Main + new create(env: Env) => + let forest = Forest + +class Forest + let _owl: Owl = Owl + let _hawk: Hawk = Hawk + class Hawk var _hunger_level: U64 = 0 diff --git a/code-samples/traits-and-interfaces-multiple-traits.pony b/code-samples/traits-and-interfaces-multiple-traits.pony index 88b000ba..1547fb62 100644 --- a/code-samples/traits-and-interfaces-multiple-traits.pony +++ b/code-samples/traits-and-interfaces-multiple-traits.pony @@ -1,3 +1,8 @@ +actor Main + new create(env: Env) => + let bob: Bob = Bob + env.out.print("Their name is \"" + bob.name() + "\" and they are " + (if bob.hair() then "bald" else "not bald" end)) + trait Named fun name(): String => "Bob" diff --git a/code-samples/traits-and-interfaces-nested-traits.pony b/code-samples/traits-and-interfaces-nested-traits.pony index 332cb4be..1f00a1fe 100644 --- a/code-samples/traits-and-interfaces-nested-traits.pony +++ b/code-samples/traits-and-interfaces-nested-traits.pony @@ -1,3 +1,8 @@ +actor Main + new create(env: Env) => + let bob: Bob = Bob + env.out.print("Their name is \"" + bob.name() + "\" and they are " + (if bob.hair() then "bald" else "not bald" end)) + trait Named fun name(): String => "Bob" diff --git a/code-samples/traits-and-interfaces-nominal-and-structural-subtyping.pony b/code-samples/traits-and-interfaces-nominal-and-structural-subtyping.pony index da7a2655..df867aac 100644 --- a/code-samples/traits-and-interfaces-nominal-and-structural-subtyping.pony +++ b/code-samples/traits-and-interfaces-nominal-and-structural-subtyping.pony @@ -1,3 +1,10 @@ +actor Main + new create(env: Env) => + let bob: Bob = Bob + let larry: Larry = Larry + env.out.print("Their name is \"" + bob.name() + "\"") + env.out.print("Their name is \"" + larry.name() + "\"") + interface HasName fun name(): String => "Bob" diff --git a/code-samples/traits-and-interfaces-nominal-subtyping-in-pony.pony b/code-samples/traits-and-interfaces-nominal-subtyping-in-pony.pony index e1972c2b..e364af7e 100644 --- a/code-samples/traits-and-interfaces-nominal-subtyping-in-pony.pony +++ b/code-samples/traits-and-interfaces-nominal-subtyping-in-pony.pony @@ -1,2 +1,7 @@ +actor Main + new create(env: Env) => + let larry: Larry = Larry + env.out.print("Their name is \"" + larry.name() + "\"") + class Larry fun name(): String => "Larry" \ 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 deleted file mode 100644 index 6abe430e..00000000 --- a/code-samples/traits-and-interfaces-open-world-interface.pony +++ /dev/null @@ -1 +0,0 @@ -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 index c25cd0d7..3745207f 100644 --- a/code-samples/traits-and-interfaces-open-world-typing.pony +++ b/code-samples/traits-and-interfaces-open-world-typing.pony @@ -1,3 +1,22 @@ +actor Main + new create(env: Env) => + let compactor: Compactor = Compactor(2) + let a = [ as U64: + 1 + 2 + ] + env.out.print("size: " + a.size().string() + ", space: " + a.space().string()) + compactor.try_compacting(a) + env.out.print("size: " + a.size().string() + ", space: " + a.space().string()) + let b = [ as U64: + 1 + 2 + 3 + ] + env.out.print("size: " + b.size().string() + ", space: " + b.space().string()) + compactor.try_compacting(b) + env.out.print("size: " + b.size().string() + ", space: " + b.space().string()) + interface Compactable fun ref compact() fun size(): USize diff --git a/code-samples/traits-and-interfaces-trait.pony b/code-samples/traits-and-interfaces-trait.pony index 0237adbe..e848f934 100644 --- a/code-samples/traits-and-interfaces-trait.pony +++ b/code-samples/traits-and-interfaces-trait.pony @@ -1,3 +1,8 @@ +actor Main + new create(env: Env) => + let bob: Bob = Bob + env.out.print("Their name is \"" + bob.name() + "\"") + trait Named fun name(): String => "Bob" diff --git a/docs/types/classes.md b/docs/types/classes.md index be9f22dd..8a6dd00e 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 ---8<-- "classes-wombat.pony:1:1" +--8<-- "classes-wombat.pony:9:9" ``` __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,7 +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 ---8<-- "classes-wombat.pony:1:3" +--8<-- "classes-wombat.pony:9:11" ``` Here, a `Wombat` has a `name`, which is a `String`, and a `_hunger_level`, which is a `U64` (an unsigned 64-bit integer). @@ -35,13 +35,13 @@ Pony constructors have names. Other than that, they are just like constructors i Constructors are introduced with the __new__ keyword. ```pony ---8<-- "classes-wombat-constructors.pony" +--8<-- "classes-wombat-constructors.pony:8:18" ``` 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 ---8<-- "classes-wombat-constructor-invocation.pony" +--8<-- "classes-wombat.pony:3:4" ``` __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. @@ -51,7 +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 ---8<-- "classes-wombat.pony:1:12" +--8<-- "classes-wombat.pony:9:20" ``` Here, every `Wombat` begins a little bit thirsty, regardless of which constructor is called. @@ -59,7 +59,7 @@ Here, every `Wombat` begins a little bit thirsty, regardless of which constructo ### Zero Argument Constructors ```pony ---8<-- "classes-zero-argument-constructors.pony" +--8<-- "classes-zero-argument-constructors.pony:9:16" ``` 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`. @@ -67,7 +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 ---8<-- "classes-zero-argument-constructors-invocation.pony" +--8<-- "classes-zero-argument-constructors.pony:5:7" ``` This is explained later, in more detail in the [sugar](/expressions/sugar.md) section. @@ -77,7 +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 ---8<-- "classes-wombat.pony" +--8<-- "classes-wombat.pony:9:24" ``` 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`. @@ -107,13 +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 ---8<-- "classes-swap-values.pony" +--8<-- "classes-swap-values.pony:6:8" ``` In Pony, you can just do: ```pony ---8<-- "classes-swap-values-sugar.pony" +--8<-- "classes-swap-values-sugar.pony:6:6" ``` ### Finalisers diff --git a/docs/types/traits-and-interfaces.md b/docs/types/traits-and-interfaces.md index a78baddd..26a45f9f 100644 --- a/docs/types/traits-and-interfaces.md +++ b/docs/types/traits-and-interfaces.md @@ -43,7 +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 ---8<-- "traits-and-interfaces-trait.pony" +--8<-- "traits-and-interfaces-trait.pony:6:9" ``` 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". @@ -53,19 +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 ---8<-- "traits-and-interfaces-multiple-traits.pony" +--8<-- "traits-and-interfaces-multiple-traits.pony:6:12" ``` 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 ---8<-- "traits-and-interfaces-nested-traits.pony" +--8<-- "traits-and-interfaces-nested-traits.pony:6:12" ``` 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 ---8<-- "traits-and-interfaces-nominal-subtyping-in-pony.pony" +--8<-- "traits-and-interfaces-nominal-subtyping-in-pony.pony:6:7" ``` Here, we have a class `Larry` that has a `name` function with the same signature. But `Larry` does __not__ provide `Named`! @@ -75,7 +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 ---8<-- "traits-and-interfaces-nominal-and-structural-subtyping.pony" +--8<-- "traits-and-interfaces-nominal-and-structural-subtyping.pony:8:14" ``` 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`. @@ -123,7 +123,7 @@ 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 ---8<-- "traits-and-interfaces-open-world-interface.pony" +--8<-- "traits-and-interfaces-marker-methods.pony:1:1" ``` 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: @@ -141,7 +141,7 @@ We've covered a couple ways that traits can be better than interfaces, let's clo Here's a contrived example: ```pony ---8<-- "traits-and-interfaces-open-world-typing.pony" +--8<-- "traits-and-interfaces-open-world-typing.pony:20:36" ``` 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. From 61d974541d03e1fdbe32a4aebaab79d64b8f3478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 21 May 2024 08:17:33 +0200 Subject: [PATCH 29/58] Make "type aliases" runnable --- .../type-aliases-complex-types-interface.pony | 12 +++++++++++- code-samples/type-aliases-complex-types-trait.pony | 9 ++++++++- code-samples/type-aliases-enumerations-apply.pony | 12 +++++++++++- .../type-aliases-enumerations-iteration.pony | 7 ------- docs/types/type-aliases.md | 12 ++++++++---- 5 files changed, 38 insertions(+), 14 deletions(-) delete mode 100644 code-samples/type-aliases-enumerations-iteration.pony diff --git a/code-samples/type-aliases-complex-types-interface.pony b/code-samples/type-aliases-complex-types-interface.pony index d0c24dd7..b0089bcd 100644 --- a/code-samples/type-aliases-complex-types-interface.pony +++ b/code-samples/type-aliases-complex-types-interface.pony @@ -1,3 +1,8 @@ +actor Main + new create(env: Env) => + let bob: Person = Bob + env.out.print(bob.name() + ", aged " + bob.age().string() + ", feels \"" + bob.feeling() + "\"") + interface HasName fun name(): String @@ -7,4 +12,9 @@ interface HasAge interface HasFeelings fun feeling(): String -type Person is (HasName & HasAge & HasFeelings) \ No newline at end of file +type Person is (HasName & HasAge & HasFeelings) + +class Bob is Person + fun name(): String => "Bob" + fun age(): U32 => 42 + fun feeling(): String => "Great!" \ 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 index d0a2e5d4..3e8c5a07 100644 --- a/code-samples/type-aliases-complex-types-trait.pony +++ b/code-samples/type-aliases-complex-types-trait.pony @@ -1,3 +1,8 @@ +actor Main + new create(env: Env) => + let bob: Person = Bob + env.out.print(bob.name() + ", aged " + bob.age().string() + ", feels \"" + bob.feeling() + "\"") + trait HasName fun name(): String => "Bob" @@ -7,4 +12,6 @@ trait HasAge trait HasFeelings fun feeling(): String => "Great!" -type Person is (HasName & HasAge & HasFeelings) \ No newline at end of file +type Person is (HasName & HasAge & HasFeelings) + +class Bob is Person \ No newline at end of file diff --git a/code-samples/type-aliases-enumerations-apply.pony b/code-samples/type-aliases-enumerations-apply.pony index 96190412..7f00c4c5 100644 --- a/code-samples/type-aliases-enumerations-apply.pony +++ b/code-samples/type-aliases-enumerations-apply.pony @@ -1,5 +1,15 @@ +actor Main + new create(env: Env) => + for colour in ColourList().values() do + env.out.print(colour().string()) + end + 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 +type Colour is (Red | Blue | Green) + +primitive ColourList + fun apply(): Array[Colour] => + [Red; Green; Blue] diff --git a/code-samples/type-aliases-enumerations-iteration.pony b/code-samples/type-aliases-enumerations-iteration.pony deleted file mode 100644 index ccf62fcb..00000000 --- a/code-samples/type-aliases-enumerations-iteration.pony +++ /dev/null @@ -1,7 +0,0 @@ -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/docs/types/type-aliases.md b/docs/types/type-aliases.md index 7b7fc9ad..7e18b28d 100644 --- a/docs/types/type-aliases.md +++ b/docs/types/type-aliases.md @@ -22,7 +22,7 @@ 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 ---8<-- "type-aliases-enumerations-apply.pony" +--8<-- "type-aliases-enumerations-apply.pony:7:11" ``` or namespace them like this @@ -34,7 +34,11 @@ or namespace them like this You might also want to iterate over the enumeration items like this to print their value for debugging purposes ```pony ---8<-- "type-aliases-enumerations-iteration.pony" +--8<-- +type-aliases-enumerations-apply.pony:13:15 + +type-aliases-enumerations-apply.pony:3:5 +--8<-- ``` ## Complex types @@ -42,13 +46,13 @@ You might also want to iterate over the enumeration items like this to print the 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 ---8<-- "type-aliases-complex-types-interface.pony" +--8<-- "type-aliases-complex-types-interface.pony:6:15" ``` This use of complex types applies to traits, not just interfaces: ```pony ---8<-- "type-aliases-complex-types-trait.pony" +--8<-- "type-aliases-complex-types-trait.pony:6:15" ``` 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`. From 03bc5e6da5836f6823a59e8ef84fb8e343a88567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 21 May 2024 08:19:58 +0200 Subject: [PATCH 30/58] refactor(config): Enable code snippet dedention --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 1b1f648e..b1008e9b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,6 +25,7 @@ markdown_extensions: - pymdownx.snippets: base_path: ['code-samples'] check_paths: true + dedent_subsections: true - smarty - toc: permalink: true From b59449ebb6c7f8ff0fd82b7e2390396daf20d835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 21 May 2024 08:43:34 +0200 Subject: [PATCH 31/58] feat(code-samples): Make "derived authority" runnable --- ...ity-restrict-then-delegate-your-authority.pony | 15 +++++++++++++++ docs/object-capabilities/derived-authority.md | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/code-samples/derived-authority-restrict-then-delegate-your-authority.pony b/code-samples/derived-authority-restrict-then-delegate-your-authority.pony index 9239f4f7..335b278f 100644 --- a/code-samples/derived-authority-restrict-then-delegate-your-authority.pony +++ b/code-samples/derived-authority-restrict-then-delegate-your-authority.pony @@ -1,3 +1,18 @@ +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") diff --git a/docs/object-capabilities/derived-authority.md b/docs/object-capabilities/derived-authority.md index 9a76e44a..afe2eff4 100644 --- a/docs/object-capabilities/derived-authority.md +++ b/docs/object-capabilities/derived-authority.md @@ -37,7 +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 ---8<-- "derived-authority-restrict-then-delegate-your-authority.pony" +--8<-- "derived-authority-restrict-then-delegate-your-authority.pony:16:22" ``` 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__. From d22204a8df9590f315944fe0c38153825c17e449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 21 May 2024 21:30:33 +0200 Subject: [PATCH 32/58] refactor(code-samples): Make "Reference capabilities" runnable --- code-samples/aliasing-iso-to-tag.pony | 10 ++++++++-- ...aliasing-multiple-references-to-an-iso-object.pony | 10 ++++++++-- code-samples/aliasing-trn-to-box.pony | 10 ++++++++-- ...destructive-read-consuming-a-variable-failure.pony | 9 +++++++++ ...ume-and-destructive-read-consuming-a-variable.pony | 11 ++++++++--- .../recovering-capabilities-string-append.pony | 7 +++++-- ...pabilities-with-explicit-reference-capability.pony | 7 ++++++- docs/reference-capabilities/aliasing.md | 6 +++--- .../consume-and-destructive-read.md | 4 ++-- .../reference-capabilities/recovering-capabilities.md | 4 ++-- 10 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 code-samples/consume-and-destructive-read-consuming-a-variable-failure.pony diff --git a/code-samples/aliasing-iso-to-tag.pony b/code-samples/aliasing-iso-to-tag.pony index c7202e93..45ffb9a3 100644 --- a/code-samples/aliasing-iso-to-tag.pony +++ b/code-samples/aliasing-iso-to-tag.pony @@ -1,2 +1,8 @@ -fun test(a: Wombat iso) => - var b: Wombat tag = a // Allowed! \ No newline at end of file +actor Main + new create(env: Env) => + test(Wombat) + + fun test(a: Wombat iso) => + var b: Wombat tag = a // Allowed! + +class Wombat \ 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 index 8acb51c5..d076a8de 100644 --- a/code-samples/aliasing-multiple-references-to-an-iso-object.pony +++ b/code-samples/aliasing-multiple-references-to-an-iso-object.pony @@ -1,2 +1,8 @@ -fun test(a: Wombat iso) => - var b: Wombat iso = a // Not allowed! \ No newline at end of file +actor Main + new create(env: Env) => + test(Wombat) + + fun test(a: Wombat iso) => + var b: Wombat iso = a // Not allowed! + +class Wombat \ No newline at end of file diff --git a/code-samples/aliasing-trn-to-box.pony b/code-samples/aliasing-trn-to-box.pony index 443a799b..fe884f21 100644 --- a/code-samples/aliasing-trn-to-box.pony +++ b/code-samples/aliasing-trn-to-box.pony @@ -1,2 +1,8 @@ -fun test(a: Wombat trn) => - var b: Wombat box = a // Allowed! \ No newline at end of file +actor Main + new create(env: Env) => + test(Wombat) + + fun test(a: Wombat trn) => + var b: Wombat box = a // Allowed! + +class Wombat \ No newline at end of file diff --git a/code-samples/consume-and-destructive-read-consuming-a-variable-failure.pony b/code-samples/consume-and-destructive-read-consuming-a-variable-failure.pony new file mode 100644 index 00000000..e100f4eb --- /dev/null +++ b/code-samples/consume-and-destructive-read-consuming-a-variable-failure.pony @@ -0,0 +1,9 @@ +actor Main + new create(env: Env) => + test(Wombat) + + fun test(a: Wombat iso) => + var b: Wombat iso = consume a // Allowed! + var c: Wombat tag = a // Not allowed! + +class Wombat \ 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 index c4134247..12b34f35 100644 --- a/code-samples/consume-and-destructive-read-consuming-a-variable.pony +++ b/code-samples/consume-and-destructive-read-consuming-a-variable.pony @@ -1,3 +1,8 @@ -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 +actor Main + new create(env: Env) => + test(Wombat) + + fun test(a: Wombat iso) => + var b: Wombat iso = consume a // Allowed! + +class Wombat \ No newline at end of file diff --git a/code-samples/recovering-capabilities-string-append.pony b/code-samples/recovering-capabilities-string-append.pony index 7fd53cbf..c5b0bf56 100644 --- a/code-samples/recovering-capabilities-string-append.pony +++ b/code-samples/recovering-capabilities-string-append.pony @@ -1,2 +1,5 @@ -let s = recover String end -s.append("hi") \ No newline at end of file +actor Main + new create(env: Env) => + let s = recover String end + s.append("hi") + env.out.print(consume s) \ 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 index 7a72b9b9..4adabeac 100644 --- a/code-samples/recovering-capabilities-with-explicit-reference-capability.pony +++ b/code-samples/recovering-capabilities-with-explicit-reference-capability.pony @@ -1 +1,6 @@ -let key = recover val line.substring(0, i).>strip() end \ No newline at end of file +actor Main + new create(env: Env) => + let line = "Lorem ipsum" + let i: ISize = 5 + let key = recover val line.substring(0, i).>strip() end + env.out.print("key: " + key) \ No newline at end of file diff --git a/docs/reference-capabilities/aliasing.md b/docs/reference-capabilities/aliasing.md index 98f0dd85..83f118b0 100644 --- a/docs/reference-capabilities/aliasing.md +++ b/docs/reference-capabilities/aliasing.md @@ -11,7 +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 ---8<-- "aliasing-multiple-references-to-an-iso-object.pony" +--8<-- "aliasing-multiple-references-to-an-iso-object.pony:5:6" ``` 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. @@ -19,13 +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 ---8<-- "aliasing-iso-to-tag.pony" +--8<-- "aliasing-iso-to-tag.pony:5:6" ``` __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 ---8<-- "aliasing-trn-to-box.pony" +--8<-- "aliasing-trn-to-box.pony:5:6" ``` __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. diff --git a/docs/reference-capabilities/consume-and-destructive-read.md b/docs/reference-capabilities/consume-and-destructive-read.md index f5dd126a..1ee495c4 100644 --- a/docs/reference-capabilities/consume-and-destructive-read.md +++ b/docs/reference-capabilities/consume-and-destructive-read.md @@ -9,13 +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 ---8<-- "consume-and-destructive-read-consuming-a-variable.pony:1:2" +--8<-- "consume-and-destructive-read-consuming-a-variable.pony:5:6" ``` 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 ---8<-- "consume-and-destructive-read-consuming-a-variable.pony" +--8<-- "consume-and-destructive-read-consuming-a-variable.pony-failure:5:7" ``` Here's an example of that. When you try to assign `a` to `c`, the compiler will complain. diff --git a/docs/reference-capabilities/recovering-capabilities.md b/docs/reference-capabilities/recovering-capabilities.md index 842d1e11..80b09347 100644 --- a/docs/reference-capabilities/recovering-capabilities.md +++ b/docs/reference-capabilities/recovering-capabilities.md @@ -31,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 ---8<-- "recovering-capabilities-with-explicit-reference-capability.pony" +--8<-- "recovering-capabilities-with-explicit-reference-capability.pony:5:5" ``` 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`. @@ -55,7 +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 ---8<-- "recovering-capabilities-string-append.pony" +--8<-- "recovering-capabilities-string-append.pony:3:4" ``` 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. From c1487399552ec42d4b7a651a91a679ff924886ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 21 May 2024 21:34:09 +0200 Subject: [PATCH 33/58] fix(code-snippets): Typo --- docs/reference-capabilities/consume-and-destructive-read.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference-capabilities/consume-and-destructive-read.md b/docs/reference-capabilities/consume-and-destructive-read.md index 1ee495c4..8b525cff 100644 --- a/docs/reference-capabilities/consume-and-destructive-read.md +++ b/docs/reference-capabilities/consume-and-destructive-read.md @@ -15,7 +15,7 @@ You can do this by using `consume`. When you `consume` a variable you take the v 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 ---8<-- "consume-and-destructive-read-consuming-a-variable.pony-failure:5:7" +--8<-- "consume-and-destructive-read-consuming-a-variable-failure.pony:5:7" ``` Here's an example of that. When you try to assign `a` to `c`, the compiler will complain. From a56e55dc9d92e0ed13128e530539aaf357581f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 22 May 2024 22:30:41 +0000 Subject: [PATCH 34/58] feat(code-samples): Add latin-1 sample --- code-samples/literals-string-literals-encoding-latin-1.pony | 1 + 1 file changed, 1 insertion(+) create mode 100644 code-samples/literals-string-literals-encoding-latin-1.pony diff --git a/code-samples/literals-string-literals-encoding-latin-1.pony b/code-samples/literals-string-literals-encoding-latin-1.pony new file mode 100644 index 00000000..fd108693 --- /dev/null +++ b/code-samples/literals-string-literals-encoding-latin-1.pony @@ -0,0 +1 @@ +let u_umlaut = "" \ No newline at end of file From a3b8b676de475b117a62ff3ba1413fa9f2b3f055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Thu, 23 May 2024 02:12:40 +0200 Subject: [PATCH 35/58] Make "Literals" runnable --- code-samples/literals-character-literals.pony | 12 +++++++++--- code-samples/literals-number-types.pony | 9 ++++++--- ...literals-string-literals-encoding-latin-1.pony | 6 +++++- .../literals-string-literals-encoding.pony | 6 +++++- .../literals-string-literals-instances.pony | 15 ++++++++++----- code-samples/type-aliases-enumerations-apply.pony | 12 ++++++++++-- docs/expressions/literals.md | 11 +++++++---- docs/types/type-aliases.md | 6 +++--- 8 files changed, 55 insertions(+), 22 deletions(-) diff --git a/code-samples/literals-character-literals.pony b/code-samples/literals-character-literals.pony index f2ebc19b..8e60d143 100644 --- a/code-samples/literals-character-literals.pony +++ b/code-samples/literals-character-literals.pony @@ -1,3 +1,9 @@ -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 +actor Main + new create(env: Env) => + let big_a: U8 = 'A' // 65 + let hex_escaped_big_a: U8 = '\x41' // 65 + let newline: U32 = '\n' // 10 + env.out.print( + "\"" + String.from_array([big_a]) + "\" (char " + big_a.string()+ ") = " + + "\"" + String.from_array([hex_escaped_big_a]) + "\" (char " + hex_escaped_big_a.string() + ") = " + + "\"" + String.from_array([newline.u8()]) + "\" (char " + newline.string() + ")") \ No newline at end of file diff --git a/code-samples/literals-number-types.pony b/code-samples/literals-number-types.pony index bd9fa719..b206a130 100644 --- a/code-samples/literals-number-types.pony +++ b/code-samples/literals-number-types.pony @@ -1,3 +1,6 @@ -let my_decimal_int: I32 = 1024 -let my_hexadecimal_int: I32 = 0x400 -let my_binary_int: I32 = 0b10000000000 \ No newline at end of file +actor Main + new create(env: Env) => + let my_decimal_int: I32 = 1024 + let my_hexadecimal_int: I32 = 0x400 + let my_binary_int: I32 = 0b10000000000 + env.out.print(my_decimal_int.string() + " = " + my_hexadecimal_int.string() + " = " + my_binary_int.string()) \ No newline at end of file diff --git a/code-samples/literals-string-literals-encoding-latin-1.pony b/code-samples/literals-string-literals-encoding-latin-1.pony index fd108693..907ea26b 100644 --- a/code-samples/literals-string-literals-encoding-latin-1.pony +++ b/code-samples/literals-string-literals-encoding-latin-1.pony @@ -1 +1,5 @@ -let u_umlaut = "" \ No newline at end of file +actor Main + new create(env: Env) => + let u_umlaut = "�" + + env.out.print(u_umlaut) \ No newline at end of file diff --git a/code-samples/literals-string-literals-encoding.pony b/code-samples/literals-string-literals-encoding.pony index b16d5df6..321f7061 100644 --- a/code-samples/literals-string-literals-encoding.pony +++ b/code-samples/literals-string-literals-encoding.pony @@ -1 +1,5 @@ -let u_umlaut = "ü" \ No newline at end of file +actor Main + new create(env: Env) => + let u_umlaut = "ü" + + env.out.print(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 index ff1cbb42..cf427c20 100644 --- a/code-samples/literals-string-literals-instances.pony +++ b/code-samples/literals-string-literals-instances.pony @@ -1,5 +1,10 @@ -let pony = "🐎" -let another_pony = "🐎" -if pony is another_pony then - // True, therefore this line will run. -end \ No newline at end of file +actor Main + new create(env: Env) => + let pony = "🐎" + let another_pony = "🐎" + if pony is another_pony then + // True, therefore this line will run. + env.out.print("Same pony") + else + env.out.print("A different pony") + end \ No newline at end of file diff --git a/code-samples/type-aliases-enumerations-apply.pony b/code-samples/type-aliases-enumerations-apply.pony index e816ea59..8d4e8578 100644 --- a/code-samples/type-aliases-enumerations-apply.pony +++ b/code-samples/type-aliases-enumerations-apply.pony @@ -1,14 +1,22 @@ +use "format" +use "collections/persistent" + actor Main new create(env: Env) => + let colorMap = Map[U32, String].concat([ + (0xFF0000FF, "red") + (0x00FF00FF, "green") + (0x0000FFFF, "blue") + ].values()) for colour in ColourList().values() do - env.out.print(colour().string()) + env.out.print(colorMap.get_or_else(colour(), "color") + ": #" + Format.int[U32](colour(), FormatHexBare)) end primitive Red fun apply(): U32 => 0xFF0000FF primitive Green fun apply(): U32 => 0x00FF00FF primitive Blue fun apply(): U32 => 0x0000FFFF -type Colour is (Red | Blue | Green) +type Colour is (Red | Blue | Green) primitive ColourList fun apply(): Array[Colour] => diff --git a/docs/expressions/literals.md b/docs/expressions/literals.md index f220d34d..af63cf57 100644 --- a/docs/expressions/literals.md +++ b/docs/expressions/literals.md @@ -37,7 +37,7 @@ It is possible to help the compiler determine the concrete type of the literal u Integer literals can be given as decimal, hexadecimal or binary: ```pony ---8<-- "literals-number-types.pony" +--8<-- "literals-number-types.pony:3:5" ``` Floating Point literals are expressed as standard floating point or scientific notation: @@ -53,7 +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 ---8<-- "literals-character-literals.pony" +--8<-- "literals-character-literals.pony:3:5" ``` The following escape sequences are supported: @@ -97,7 +97,7 @@ String Literals contain the bytes that were read from their source code file. Th Consider the following example: ```pony ---8<-- "literals-string-literals-encoding.pony" +--8<-- "literals-string-literals-encoding.pony:3:3" ``` 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`. @@ -115,7 +115,10 @@ For embedding multi-line text in string literals, there are triple quoted string 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 ---8<-- "literals-string-literals-instances.pony" +--8<-- +literals-string-literals-instances.pony:3:6 +literals-string-literals-instances.pony:10:10 +--8<-- ``` ## Array Literals diff --git a/docs/types/type-aliases.md b/docs/types/type-aliases.md index 7e18b28d..97bab5a9 100644 --- a/docs/types/type-aliases.md +++ b/docs/types/type-aliases.md @@ -22,7 +22,7 @@ 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 ---8<-- "type-aliases-enumerations-apply.pony:7:11" +--8<-- "type-aliases-enumerations-apply.pony:15:19" ``` or namespace them like this @@ -35,9 +35,9 @@ You might also want to iterate over the enumeration items like this to print the ```pony --8<-- -type-aliases-enumerations-apply.pony:13:15 +type-aliases-enumerations-apply.pony:21:23 -type-aliases-enumerations-apply.pony:3:5 +type-aliases-enumerations-apply.pony:11:13 --8<-- ``` From 88998a4f82ecc2ce685f47acacda23f66d070d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Thu, 23 May 2024 22:40:11 +0200 Subject: [PATCH 36/58] Make "literals" and "variables" runnable --- code-samples/literals-array-literals.pony | 36 ++++++++++++++++--- ...rals-string-literals-encoding-latin-1.pony | 8 ++++- .../literals-string-literals-encoding.pony | 8 ++++- ...riables-fields-constructor-assignment.pony | 9 ++++- ...ariables-fields-definition-assignment.pony | 9 ++++- .../variables-fields-let-reassignment.pony | 4 +++ code-samples/variables-let-reassignment.pony | 8 +++-- code-samples/variables-scope.pony | 15 +++++--- code-samples/variables-var-vs-let.pony | 10 +++--- docs/expressions/literals.md | 2 +- docs/expressions/variables.md | 12 +++---- 11 files changed, 93 insertions(+), 28 deletions(-) diff --git a/code-samples/literals-array-literals.pony b/code-samples/literals-array-literals.pony index dd3bf10d..1e24dd7b 100644 --- a/code-samples/literals-array-literals.pony +++ b/code-samples/literals-array-literals.pony @@ -1,5 +1,31 @@ -let my_literal_array = - [ - "first"; "second" - "third one on a new line" - ] \ No newline at end of file +actor Main + new create(env: Env) => + let my_literal_array = + [ + "first"; "second" + "third one on a new line" + ] + + try + env.out.print(my_literal_array(0)? + ", " + my_literal_array(1)? + ", " + my_literal_array(2)?) + end + + // or: + for entry in my_literal_array.values() do + env.out.print(entry) + end + + // or: + for pair in my_literal_array.pairs() do + env.out.write(pair._2 + (if (pair._1 + 1) < my_literal_array.size() then ", " else "" end)) + end + env.out.print("") + + // or: + env.out.print(", ".join(my_literal_array.values())) + + // or: + while my_literal_array.size() > 0 do + let entry = try my_literal_array.shift()? end + env.out.print(entry.string()) + end diff --git a/code-samples/literals-string-literals-encoding-latin-1.pony b/code-samples/literals-string-literals-encoding-latin-1.pony index 907ea26b..0541cc0e 100644 --- a/code-samples/literals-string-literals-encoding-latin-1.pony +++ b/code-samples/literals-string-literals-encoding-latin-1.pony @@ -2,4 +2,10 @@ actor Main new create(env: Env) => let u_umlaut = "�" - env.out.print(u_umlaut) \ No newline at end of file + let runes = u_umlaut.runes() + try + if runes.has_next() then + let rune = u_umlaut.runes().next()? + env.out.print(u_umlaut + ": " + Format.int[U32](rune, FormatHex)) + end + end \ No newline at end of file diff --git a/code-samples/literals-string-literals-encoding.pony b/code-samples/literals-string-literals-encoding.pony index 321f7061..4e873ace 100644 --- a/code-samples/literals-string-literals-encoding.pony +++ b/code-samples/literals-string-literals-encoding.pony @@ -2,4 +2,10 @@ actor Main new create(env: Env) => let u_umlaut = "ü" - env.out.print(u_umlaut) \ No newline at end of file + let runes = u_umlaut.runes() + try + if runes.has_next() then + let rune = u_umlaut.runes().next()? + env.out.print(u_umlaut + ": " + Format.int[U32](rune, FormatHex)) + end + end \ No newline at end of file diff --git a/code-samples/variables-fields-constructor-assignment.pony b/code-samples/variables-fields-constructor-assignment.pony index 88263d91..6cfd9967 100644 --- a/code-samples/variables-fields-constructor-assignment.pony +++ b/code-samples/variables-fields-constructor-assignment.pony @@ -1,7 +1,14 @@ +actor Main + new create(env: Env) => + let wombat = Wombat(5) + env.out.print(wombat.name + " has a hunger level of " + wombat.get_hunger().string()) + class Wombat let name: String var _hunger_level: U32 new create(hunger: U32) => name = "Fantastibat" - _hunger_level = hunger \ No newline at end of file + _hunger_level = hunger + + fun get_hunger(): U32 => _hunger_level \ No newline at end of file diff --git a/code-samples/variables-fields-definition-assignment.pony b/code-samples/variables-fields-definition-assignment.pony index b9c7fc18..55c781a1 100644 --- a/code-samples/variables-fields-definition-assignment.pony +++ b/code-samples/variables-fields-definition-assignment.pony @@ -1,3 +1,10 @@ +actor Main + new create(env: Env) => + let wombat = Wombat + env.out.print(wombat.name + " has a hunger level of " + wombat.hunger().string()) + class Wombat let name: String = "Fantastibat" - var _hunger_level: U32 = 0 \ No newline at end of file + var _hunger_level: U32 = 0 + + fun hunger(): U32 => _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 index 4771bc4d..b13d3229 100644 --- a/code-samples/variables-fields-let-reassignment.pony +++ b/code-samples/variables-fields-let-reassignment.pony @@ -1,3 +1,7 @@ +actor Main + new create(env: Env) => + let wombat = Wombat("Fantastibat", 5) + class Wombat let name: String var _hunger_level: U64 diff --git a/code-samples/variables-let-reassignment.pony b/code-samples/variables-let-reassignment.pony index 46d96247..53bba625 100644 --- a/code-samples/variables-let-reassignment.pony +++ b/code-samples/variables-let-reassignment.pony @@ -1,3 +1,5 @@ -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 +actor Main + new create(env: Env) => + 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-scope.pony b/code-samples/variables-scope.pony index 2b27bbbb..659a259e 100644 --- a/code-samples/variables-scope.pony +++ b/code-samples/variables-scope.pony @@ -1,6 +1,11 @@ -if a > b then - var x = "a is bigger" - env.out.print(x) // OK -end +actor Main + new create(env: Env) => + let a = 2 + let b = 1 -env.out.print(x) // Illegal \ No newline at end of file + 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 index 285cc055..47480f45 100644 --- a/code-samples/variables-var-vs-let.pony +++ b/code-samples/variables-var-vs-let.pony @@ -1,4 +1,6 @@ -var x: U32 = 3 -let y: U32 = 4 -x = 5 // OK -y = 6 // Error, y is let \ No newline at end of file +actor Main + new create(env: Env) => + 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/expressions/literals.md b/docs/expressions/literals.md index af63cf57..8dbee81b 100644 --- a/docs/expressions/literals.md +++ b/docs/expressions/literals.md @@ -126,7 +126,7 @@ literals-string-literals-instances.pony:10:10 Array literals are enclosed by square brackets. Array literal elements can be any kind of expressions. They are separated by semicolon or newline: ```pony ---8<-- "literals-array-literals.pony" +--8<-- "literals-array-literals.pony:3:7" ``` ### Type inference diff --git a/docs/expressions/variables.md b/docs/expressions/variables.md index 740083b3..1b109f6c 100644 --- a/docs/expressions/variables.md +++ b/docs/expressions/variables.md @@ -31,7 +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 ---8<-- "variables-scope.pony" +--8<-- "variables-scope.pony:6:1" ``` 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. @@ -41,13 +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 ---8<-- "variables-var-vs-let.pony" +--8<-- "variables-var-vs-let.pony:3:6" ``` Using `let` instead of `var` also means the variable has to be assigned immediately. ```pony ---8<-- "variables-let-reassignment.pony" +--8<-- "variables-let-reassignment.pony:3:5" ``` 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. @@ -70,13 +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 ---8<-- "variables-fields-definition-assignment.pony" +--8<-- "variables-fields-definition-assignment.pony:6:8" ``` Alternatively, these fields could be assigned in the constructor method: ```pony ---8<-- "variables-fields-constructor-assignment.pony" +--8<-- "variables-fields-constructor-assignment.pony:6:12" ``` 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. @@ -92,7 +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 ---8<-- "variables-fields-let-reassignment.pony" +--8<-- "variables-fields-let-reassignment.pony:5:17" ``` __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 From 24fe11e1414db88a02121cee3f46ef7c7bf701b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Fri, 24 May 2024 03:07:54 +0200 Subject: [PATCH 37/58] Make "ops" and "arithmetic" runnable --- ...ithmetic-partial-and-check-arithmetic.pony | 41 +++++++++++-------- code-samples/operators-add.pony | 10 +++++ ...d-unary-operators-without-parentheses.pony | 4 +- ...rators-precedence-without-parentheses.pony | 4 +- docs/expressions/arithmetic.md | 7 +++- docs/expressions/ops.md | 9 ++-- 6 files changed, 53 insertions(+), 22 deletions(-) diff --git a/code-samples/arithmetic-partial-and-check-arithmetic.pony b/code-samples/arithmetic-partial-and-check-arithmetic.pony index 14ed8b78..d089278c 100644 --- a/code-samples/arithmetic-partial-and-check-arithmetic.pony +++ b/code-samples/arithmetic-partial-and-check-arithmetic.pony @@ -1,17 +1,26 @@ -// partial arithmetic -let result = - try - USize.max_value() +? env.args.size() - else - env.out.print("overflow detected") - end +actor Main + new create(env: Env) => + partial(env) + checked(env) + + fun partial(env: Env) => + // 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 + fun checked(env: Env) => + // checked arithmetic + let result = + match USize.max_value().addc(env.args.size()) + | (let result: USize, false) => + // use result + env.out.print(result.string())/* + ... + */ + | (_, true) => + env.out.print("overflow detected") + end \ No newline at end of file diff --git a/code-samples/operators-add.pony b/code-samples/operators-add.pony index b577cac8..f090b5da 100644 --- a/code-samples/operators-add.pony +++ b/code-samples/operators-add.pony @@ -1,3 +1,10 @@ +actor Main + new create(env: Env) => + var x = Pair(1, 2) + var y = Pair(3, 4) + var z = x + y + env.out.print(z.string()) + // Define a suitable type class Pair var _x: U32 = 0 @@ -10,6 +17,9 @@ class Pair // Define a + function fun add(other: Pair): Pair => Pair(_x + other._x, _y + other._y) + + fun string(): String => + "(" + _x.string() + ", " + _y.string() + ")" // Now let's use it class Foo 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 index f338bc68..e7ea5f48 100644 --- a/code-samples/operators-precedence-infix-and-unary-operators-without-parentheses.pony +++ b/code-samples/operators-precedence-infix-and-unary-operators-without-parentheses.pony @@ -1 +1,3 @@ -1 + 2 * -3 // Compilation failed. \ No newline at end of file +actor Main + new create(env: Env) => + 1 + 2 * -3 // Compilation failed. \ No newline at end of file diff --git a/code-samples/operators-precedence-without-parentheses.pony b/code-samples/operators-precedence-without-parentheses.pony index 6d6b2bf8..f84c4c74 100644 --- a/code-samples/operators-precedence-without-parentheses.pony +++ b/code-samples/operators-precedence-without-parentheses.pony @@ -1 +1,3 @@ -1 + 2 * 3 // Compilation failed. \ No newline at end of file +actor Main + new create(env: Env) => + 1 + 2 * 3 // Compilation failed. \ No newline at end of file diff --git a/docs/expressions/arithmetic.md b/docs/expressions/arithmetic.md index d666abb6..1c3bf826 100644 --- a/docs/expressions/arithmetic.md +++ b/docs/expressions/arithmetic.md @@ -104,7 +104,12 @@ 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 ---8<-- "arithmetic-partial-and-check-arithmetic.pony" +--8<-- +arithmetic-partial-and-check-arithmetic.pony:7:14 +arithmetic-partial-and-check-arithmetic.pony:16:20 +arithmetic-partial-and-check-arithmetic.pony:22:22 +arithmetic-partial-and-check-arithmetic.pony:24:26 +--8<-- ``` 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/ops.md b/docs/expressions/ops.md index 287449b8..95d9f99f 100644 --- a/docs/expressions/ops.md +++ b/docs/expressions/ops.md @@ -25,7 +25,10 @@ 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 ---8<-- "operators-add.pony" +--8<-- +operators-add.pony:8:19 +operators-add.pony:23:29 +--8<-- ``` 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. @@ -115,7 +118,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 ---8<-- "operators-precedence-without-parentheses.pony" +--8<-- "operators-precedence-without-parentheses.pony:3:3" ``` 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. @@ -139,7 +142,7 @@ Repeated use of a single operator, however, is fine: 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 ---8<-- "operators-precedence-infix-and-unary-operators-without-parentheses.pony" +--8<-- "operators-precedence-infix-and-unary-operators-without-parentheses.pony:3:3" ``` We would still need parentheses to remove the ambiguity for our infix operators like we did above, but not for the unary arithmetic negative (`-`): From 8306f56e61826df6ccce026effb43566a84818d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Fri, 24 May 2024 18:23:44 +0200 Subject: [PATCH 38/58] Make "control structures" runnable --- ...conditionals-expression-implicit-none.pony | 12 ++++-- ...es-conditionals-expression-union-type.pony | 16 +++++--- ...l-structures-conditionals-expressions.pony | 7 +++- ...ntrol-structures-conditionals-if-else.pony | 14 ++++--- ...tructures-conditionals-if-elseif-else.pony | 18 +++++---- .../control-structures-conditionals-if.pony | 10 +++-- ...tructures-conditionals-nested-if-else.pony | 22 ++++++----- ...structures-loops-for-while-comparison.pony | 14 ++++--- .../control-structures-loops-for.pony | 8 ++-- ...rol-structures-loops-while-break-else.pony | 39 ++++++++++++++----- .../control-structures-loops-while.pony | 12 +++--- docs/expressions/control-structures.md | 22 +++++------ 12 files changed, 126 insertions(+), 68 deletions(-) diff --git a/code-samples/control-structures-conditionals-expression-implicit-none.pony b/code-samples/control-structures-conditionals-expression-implicit-none.pony index bb9d0837..b0afb94d 100644 --- a/code-samples/control-structures-conditionals-expression-implicit-none.pony +++ b/code-samples/control-structures-conditionals-expression-implicit-none.pony @@ -1,4 +1,8 @@ -var x: (String | None) = - if friendly then - "Hello" - end \ No newline at end of file +actor Main + new create(env: Env) => + let friendly = false + var x: (String | None) = + if friendly then + "Hello" + end + env.out.print(x.string()) \ 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 index ee2ea45a..91969306 100644 --- a/code-samples/control-structures-conditionals-expression-union-type.pony +++ b/code-samples/control-structures-conditionals-expression-union-type.pony @@ -1,6 +1,10 @@ -var x: (String | Bool) = - if friendly then - "Hello" - else - false - end \ No newline at end of file +actor Main + new create(env: Env) => + let friendly = false + var x: (String | Bool) = + if friendly then + "Hello" + else + false + end + env.out.print(x.string()) \ No newline at end of file diff --git a/code-samples/control-structures-conditionals-expressions.pony b/code-samples/control-structures-conditionals-expressions.pony index fb1ca3e1..b41f7c65 100644 --- a/code-samples/control-structures-conditionals-expressions.pony +++ b/code-samples/control-structures-conditionals-expressions.pony @@ -1 +1,6 @@ -x = 1 + if lots then 100 else 2 end \ No newline at end of file +actor Main + new create(env: Env) => + let lots = true + var x: U32 + x = 1 + if lots then 100 else 2 end + env.out.print("x = " + x.string() + "—that's " + (if lots then "lots" else "not a lot" 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 index 14033612..2d61737c 100644 --- a/code-samples/control-structures-conditionals-if-else.pony +++ b/code-samples/control-structures-conditionals-if-else.pony @@ -1,5 +1,9 @@ -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 +actor Main + new create(env: Env) => + let a: U8 = 1 + let b: U8 = 2 + 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 index f9d0a01f..824f8056 100644 --- a/code-samples/control-structures-conditionals-if-elseif-else.pony +++ b/code-samples/control-structures-conditionals-if-elseif-else.pony @@ -1,7 +1,11 @@ -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 +actor Main + new create(env: Env) => + let a: U8 = 2 + let b: U8 = 1 + 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 index a2a672f2..7c006a4d 100644 --- a/code-samples/control-structures-conditionals-if.pony +++ b/code-samples/control-structures-conditionals-if.pony @@ -1,3 +1,7 @@ -if a > b then - env.out.print("a is bigger") -end \ No newline at end of file +actor Main + new create(env: Env) => + let a: U8 = 2 + let b: U8 = 1 + 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 index df7477bd..5f796257 100644 --- a/code-samples/control-structures-conditionals-nested-if-else.pony +++ b/code-samples/control-structures-conditionals-nested-if-else.pony @@ -1,9 +1,13 @@ -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 +actor Main + new create(env: Env) => + let a: U8 = 2 + let b: U8 = 2 + 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-loops-for-while-comparison.pony b/code-samples/control-structures-loops-for-while-comparison.pony index 91d327cb..e8acf0a9 100644 --- a/code-samples/control-structures-loops-for-while-comparison.pony +++ b/code-samples/control-structures-loops-for-while-comparison.pony @@ -1,5 +1,9 @@ -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 +actor Main + new create(env: Env) => + try + let iterator = ["Bob"; "Fred"; "Sarah"].values() + while iterator.has_next() do + let name = iterator.next()? + env.out.print(name) + end + 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 index 64d97f3a..d8ee843d 100644 --- a/code-samples/control-structures-loops-for.pony +++ b/code-samples/control-structures-loops-for.pony @@ -1,3 +1,5 @@ -for name in ["Bob"; "Fred"; "Sarah"].values() do - env.out.print(name) -end \ No newline at end of file +actor Main + new create(env: Env) => + 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-while-break-else.pony b/code-samples/control-structures-loops-while-break-else.pony index 357cd5c0..32ab90d0 100644 --- a/code-samples/control-structures-loops-while-break-else.pony +++ b/code-samples/control-structures-loops-while-break-else.pony @@ -1,10 +1,31 @@ -var name = - while moreNames() do - var name' = getName() - if name' == "Jack" or name' == "Jill" then - break name' - end - name' - else +actor Main + let names: Array[String] = [ + "Jack" "Herbert" - end \ No newline at end of file + "Jill" + ] + var current_name: String = "" + + new create(env: Env) => + var name = + while moreNames() do + var name' = getName() + if (name' == "Jack") or (name' == "Jill") then + break name' + end + name' + else + "Herbert" + end + env.out.print("name = " + name) + + fun ref moreNames(): Bool => + try + current_name = names.shift()? + else + return false + end + true + + fun getName(): String => + current_name \ No newline at end of file diff --git a/code-samples/control-structures-loops-while.pony b/code-samples/control-structures-loops-while.pony index 881ef4b5..cfe99ab1 100644 --- a/code-samples/control-structures-loops-while.pony +++ b/code-samples/control-structures-loops-while.pony @@ -1,6 +1,8 @@ -var count: U32 = 1 +actor Main + new create(env: Env) => + var count: U32 = 1 -while count <= 10 do - env.out.print(count.string()) - count = count + 1 -end \ No newline at end of file + while count <= 10 do + env.out.print(count.string()) + count = count + 1 + end \ No newline at end of file diff --git a/docs/expressions/control-structures.md b/docs/expressions/control-structures.md index b79e4116..f103d8c3 100644 --- a/docs/expressions/control-structures.md +++ b/docs/expressions/control-structures.md @@ -7,7 +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 ---8<-- "control-structures-conditionals-if.pony" +--8<-- "control-structures-conditionals-if.pony:5:7" ``` __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. @@ -15,19 +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 ---8<-- "control-structures-conditionals-if-else.pony" +--8<-- "control-structures-conditionals-if-else.pony:5:9" ``` 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 ---8<-- "control-structures-conditionals-nested-if-else.pony" +--8<-- "control-structures-conditionals-nested-if-else.pony:5:13" ``` 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 ---8<-- "control-structures-conditionals-if-elseif-else.pony" +--8<-- "control-structures-conditionals-if-elseif-else.pony:5:11" ``` __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: @@ -52,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 ---8<-- "control-structures-conditionals-expressions.pony" +--8<-- "control-structures-conditionals-expressions.pony:5:5" ``` This will give __x__ a value of either 3 or 101, depending on the variable __lots__. @@ -60,13 +60,13 @@ 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 ---8<-- "control-structures-conditionals-expression-union-type.pony" +--8<-- "control-structures-conditionals-expression-union-type.pony:4:9" ``` __But what if my if doesn't have an else?__ Any `else` branch that doesn't exist gives an implicit `None`. ```pony ---8<-- "control-structures-conditionals-expression-implicit-none.pony" +--8<-- "control-structures-conditionals-expression-implicit-none.pony:4:7" ``` 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: @@ -102,7 +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 ---8<-- "control-structures-loops-while.pony" +--8<-- "control-structures-loops-while.pony:3:8" ``` 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. @@ -120,7 +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 ---8<-- "control-structures-loops-while-break-else.pony" +--8<-- "control-structures-loops-while-break-else.pony:10:19" ``` 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. @@ -150,7 +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 ---8<-- "control-structures-loops-for.pony" +--8<-- "control-structures-loops-for.pony:3:5" ``` Note the call to `values()` on the array — this is because the loop needs an iterator, not an array. @@ -166,7 +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 ---8<-- "control-structures-loops-for-while-comparison.pony" +--8<-- "control-structures-loops-for-while-comparison.pony:4:8" ``` Note that the variable __name__ is declared _let_, so you cannot assign to the control variable within the loop. From 9e7d7f3c86bf4716c7e3c1d9cb6d8a9907590410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Fri, 24 May 2024 19:09:23 +0200 Subject: [PATCH 39/58] Make "match" runnable --- code-samples/match-captures.pony | 21 ++++++++++------ code-samples/match-guards.pony | 25 +++++++++++-------- .../match-tuples-ignore-elements.pony | 22 +++++++++------- code-samples/match-tuples.pony | 22 +++++++++------- code-samples/match-type-and-value.pony | 23 ++++++++++------- code-samples/match-values.pony | 23 ++++++++++------- docs/expressions/match.md | 12 ++++----- 7 files changed, 88 insertions(+), 60 deletions(-) diff --git a/code-samples/match-captures.pony b/code-samples/match-captures.pony index 2d96a6ba..70a9ea99 100644 --- a/code-samples/match-captures.pony +++ b/code-samples/match-captures.pony @@ -1,8 +1,13 @@ -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 +actor Main + new create(env: Env) => + env.out.print(f(2)) + env.out.print(f(42)) + + 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-guards.pony b/code-samples/match-guards.pony index 50799738..8a824fe2 100644 --- a/code-samples/match-guards.pony +++ b/code-samples/match-guards.pony @@ -1,10 +1,15 @@ -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 +actor Main + new create(env: Env) => + env.out.print(f("one", 5)) + env.out.print(f("two", 42)) + + 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 index 34f7057d..9b4be3ef 100644 --- a/code-samples/match-tuples-ignore-elements.pony +++ b/code-samples/match-tuples-ignore-elements.pony @@ -1,9 +1,13 @@ -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 +actor Main + new create(env: Env) => + env.out.print(f("one", 42)) + + 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 index 74e0bd0d..28e7c8ab 100644 --- a/code-samples/match-tuples.pony +++ b/code-samples/match-tuples.pony @@ -1,9 +1,13 @@ -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 +actor Main + new create(env: Env) => + env.out.print(f("one", 2)) + + 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 index 1a4f01ad..a83b7c10 100644 --- a/code-samples/match-type-and-value.pony +++ b/code-samples/match-type-and-value.pony @@ -1,9 +1,14 @@ -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 +actor Main + new create(env: Env) => + env.out.print(f(2)) + env.out.print(f(42)) + + 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-values.pony b/code-samples/match-values.pony index 12050157..7e9e2c7e 100644 --- a/code-samples/match-values.pony +++ b/code-samples/match-values.pony @@ -1,9 +1,14 @@ -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 +actor Main + new create(env: Env) => + env.out.print(f(2)) + env.out.print(f(42)) + + 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/docs/expressions/match.md b/docs/expressions/match.md index fb9c188a..07750895 100644 --- a/docs/expressions/match.md +++ b/docs/expressions/match.md @@ -37,7 +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 ---8<-- "match-values.pony" +--8<-- "match-values.pony:6:14" ``` 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. @@ -53,7 +53,7 @@ The compiler calls the `eq()` function on the operand, passing the pattern as th 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 ---8<-- "match-type-and-value.pony" +--8<-- "match-type-and-value.pony:6:14" ``` In many languages using runtime type information is very expensive and so it is generally avoided whenever possible. @@ -79,7 +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 ---8<-- "match-captures.pony" +--8<-- "match-captures.pony:6:13" ``` __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. @@ -105,13 +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 ---8<-- "match-tuples.pony" +--8<-- "match-tuples.pony:5:13" ``` __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 ---8<-- "match-tuples-ignore-elements.pony" +--8<-- "match-tuples-ignore-elements.pony:5:13" ``` ## Guards @@ -123,5 +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 ---8<-- "match-guards.pony" +--8<-- "match-guards.pony:6:15" ``` From 9c8036106c8718ab8e0740399d45e9043cff8fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Fri, 24 May 2024 21:12:49 +0200 Subject: [PATCH 40/58] Make "as" runnable --- ...s-operator-match-statement-comparison.pony | 10 +++++++ ...-operator-match-statement-without-try.pony | 10 +++++++ ...c-interface-with-reference-capability.pony | 10 +++++++ code-samples/as-operator-unrelated-type.pony | 29 ++++++++++++++----- docs/expressions/as.md | 8 ++--- 5 files changed, 56 insertions(+), 11 deletions(-) diff --git a/code-samples/as-operator-match-statement-comparison.pony b/code-samples/as-operator-match-statement-comparison.pony index c3cd6a45..3791f57b 100644 --- a/code-samples/as-operator-match-statement-comparison.pony +++ b/code-samples/as-operator-match-statement-comparison.pony @@ -1,3 +1,13 @@ +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 anys = Array[Any ref].>push(Wombat).>push(Capybara) diff --git a/code-samples/as-operator-match-statement-without-try.pony b/code-samples/as-operator-match-statement-without-try.pony index 16d6bb73..bc167ab5 100644 --- a/code-samples/as-operator-match-statement-without-try.pony +++ b/code-samples/as-operator-match-statement-without-try.pony @@ -1,3 +1,13 @@ +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 anys = Array[Any ref].>push(Wombat).>push(Capybara) 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 index 30960f93..e3913378 100644 --- a/code-samples/as-operator-more-specific-interface-with-reference-capability.pony +++ b/code-samples/as-operator-more-specific-interface-with-reference-capability.pony @@ -1,3 +1,13 @@ +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 anys = Array[Any ref].>push(Wombat).>push(Capybara) diff --git a/code-samples/as-operator-unrelated-type.pony b/code-samples/as-operator-unrelated-type.pony index 5102043a..2cac3a36 100644 --- a/code-samples/as-operator-unrelated-type.pony +++ b/code-samples/as-operator-unrelated-type.pony @@ -1,10 +1,25 @@ - trait Alive +trait Alive - trait Well +trait Well - class Person is (Alive & 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 +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 + +class Dog is Alive + +actor Main + new create(env: Env) => + try + LifeSigns.is_all_good(Person)? + else + env.err.print("Person is alive but not well") + end + try + LifeSigns.is_all_good(Dog)? + else + env.err.print("Dog is alive but not well") + end diff --git a/docs/expressions/as.md b/docs/expressions/as.md index 08838c91..8ca90207 100644 --- a/docs/expressions/as.md +++ b/docs/expressions/as.md @@ -19,7 +19,7 @@ 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 ---8<-- "as-operator-unrelated-type.pony" +--8<-- "as-operator-unrelated-type.pony:1:10" ``` `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. @@ -37,19 +37,19 @@ The programmer uses this library to create a `Wombat` and a `Capybara` class. Bu 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 ---8<-- "as-operator-more-specific-interface-with-reference-capability.pony" +--8<-- "as-operator-more-specific-interface-with-reference-capability.pony:11:18" ``` 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 ---8<-- "as-operator-match-statement-comparison.pony" +--8<-- "as-operator-match-statement-comparison.pony:11:23" ``` 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 ---8<-- "as-operator-match-statement-without-try.pony" +--8<-- "as-operator-match-statement-without-try.pony:11:19" ``` You can learn more about matching on type in the [captures section of the match documentation](/expressions/match.md#captures). From 6e71c4a26d42ae873ca735d59b21487f7c1f6907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Sat, 25 May 2024 23:01:57 +0200 Subject: [PATCH 41/58] Make "methods" runnable --- code-samples/methods-constructors.pony | 10 +++++++++- code-samples/methods-functions.pony | 5 +++++ docs/expressions/methods.md | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/code-samples/methods-constructors.pony b/code-samples/methods-constructors.pony index 0bedcff0..638ea7d6 100644 --- a/code-samples/methods-constructors.pony +++ b/code-samples/methods-constructors.pony @@ -1,3 +1,8 @@ +actor Main + new create(env: Env) => + env.out.print("create: " + Foo.get_x().string()) + env.out.print("from_int: " + Foo.from_int(42).get_x().string()) + class Foo var _x: U32 @@ -5,4 +10,7 @@ class Foo _x = 0 new from_int(x: U32) => - _x = x \ No newline at end of file + _x = x + + fun get_x(): U32 => + _x \ No newline at end of file diff --git a/code-samples/methods-functions.pony b/code-samples/methods-functions.pony index 8909482e..2d5e9551 100644 --- a/code-samples/methods-functions.pony +++ b/code-samples/methods-functions.pony @@ -1,3 +1,8 @@ +actor Main + new create(env: Env) => + env.out.print("add: " + C.add(1, 2).string()) + env.out.print("nop: " + C.nop().string()) + class C fun add(x: U32, y: U32): U32 => x + y diff --git a/docs/expressions/methods.md b/docs/expressions/methods.md index 2af9c590..f5cb3001 100644 --- a/docs/expressions/methods.md +++ b/docs/expressions/methods.md @@ -11,7 +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 ---8<-- "methods-functions.pony" +--8<-- "methods-functions.pony:6:11" ``` 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. @@ -31,7 +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 ---8<-- "methods-constructors.pony" +--8<-- "methods-constructors.pony:6:13" ``` 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. From d7302cdafdc6a84206acedec7b0b8d5539e7ed65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Sun, 26 May 2024 06:13:04 +0200 Subject: [PATCH 42/58] Make "errors" runnable --- code-samples/errors-partial-functions.pony | 29 +++++++++++---- code-samples/errors-try-else.pony | 34 ++++++++++++++---- code-samples/errors-try-then.pony | 41 +++++++++++++++++----- docs/expressions/errors.md | 6 ++-- 4 files changed, 84 insertions(+), 26 deletions(-) diff --git a/code-samples/errors-partial-functions.pony b/code-samples/errors-partial-functions.pony index 54dccb3b..72d50cd3 100644 --- a/code-samples/errors-partial-functions.pony +++ b/code-samples/errors-partial-functions.pony @@ -1,7 +1,22 @@ -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 +actor Main + new create(env: Env) => + try + let fac5 = factorial(5)? + env.out.print("factorial(5) results in " + fac5.string()) + else + env.err.print("factorial(5) failed") + end + try + let facNeg5 = factorial(-5)? + env.out.print("factorial(-5) results in " + facNeg5.string()) + else + env.err.print("factorial(-5) failed") + end + + 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 index 551688d3..b4cad70d 100644 --- a/code-samples/errors-try-else.pony +++ b/code-samples/errors-try-else.pony @@ -1,7 +1,27 @@ -try - callA() - if not callB() then error end - callC() -else - callD() -end \ No newline at end of file +actor Main + var _err: OutStream + var _out: OutStream + + new create(env: Env) => + _err = env.err + _out = env.out + + try + callA() + if not callB() then error end + callC() + else + callD() + end + + fun callA(): Bool => + true + + fun callB(): Bool => + false + + fun callC() => + _out.print("callB() executed successfully") + + fun callD() => + _err.print("callB() resulted in an error") diff --git a/code-samples/errors-try-then.pony b/code-samples/errors-try-then.pony index 3e3db5f0..8857761f 100644 --- a/code-samples/errors-try-then.pony +++ b/code-samples/errors-try-then.pony @@ -1,9 +1,32 @@ -try - callA() - if not callB() then error end - callC() -else - callD() -then - callE() -end \ No newline at end of file +actor Main + var _err: OutStream + var _out: OutStream + + new create(env: Env) => + _err = env.err + _out = env.out + + try + callA() + if not callB() then error end + callC() + else + callD() + then + callE() + end + + fun callA(): Bool => + true + + fun callB(): Bool => + false + + fun callC() => + _out.print("callB() executed successfully") + + fun callD() => + _err.print("callB() resulted in an error") + + fun callE() => + _out.print("I don't know whether callB() was executed successfully ¯\\_ (ツ)_/¯ I get executed either way") \ No newline at end of file diff --git a/docs/expressions/errors.md b/docs/expressions/errors.md index 1676deea..c5185805 100644 --- a/docs/expressions/errors.md +++ b/docs/expressions/errors.md @@ -9,7 +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 ---8<-- "errors-try-else.pony" +--8<-- "errors-try-else.pony:9:15" ``` 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. @@ -37,7 +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 ---8<-- "errors-partial-functions.pony" +--8<-- "errors-partial-functions.pony:16:22" ``` 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. @@ -57,7 +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 ---8<-- "errors-try-then.pony" +--8<-- "errors-try-then.pony:9:17" ``` 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()`. From 77ccaad1dc5112bee6352f54926202ff55d139af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Sun, 26 May 2024 08:42:44 +0200 Subject: [PATCH 43/58] Make "sugar" runnable --- .../sugar-update-additional-parameters.pony | 31 +++++++++++++++++-- code-samples/sugar-update-explicit.pony | 20 +++++++++++- code-samples/sugar-update-implicit.pony | 20 +++++++++++- docs/expressions/sugar.md | 6 ++-- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/code-samples/sugar-update-additional-parameters.pony b/code-samples/sugar-update-additional-parameters.pony index d638542f..f9ef90cc 100644 --- a/code-samples/sugar-update-additional-parameters.pony +++ b/code-samples/sugar-update-additional-parameters.pony @@ -1,3 +1,28 @@ -foo1(2, 3) = x -foo2() = x -foo3(37, "Hello", 3.5 where a = 2, b = 3) = x \ No newline at end of file +class Foo + var _x: (U32|F32) + + new create(x: U32) => + _x = x + + fun ref update(x: U32 = 0, y: (String|U32) = "", z: F32 = 0.0, value: U32, b: U32 = 0, a: U32 = 0) => + _x = (value * x).f32() + (a * b).f32() + z + match y + | let u: U32 => _x = _x.f32() + u.f32() + end + + fun get_value(): (U32|F32) => + _x + +actor Main + new create(env: Env) => + let foo1 = Foo(5) + let foo2 = Foo(5) + let foo3 = Foo(5) + let x: U32 = 10 + env.out.print("foo = " + foo1.get_value().string()) + foo1(2, 3) = x + foo2() = x + foo3(37, "Hello", 3.5 where a = 2, b = 3) = x + env.out.print("foo1 = " + foo1.get_value().string()) + env.out.print("foo2 = " + foo2.get_value().string()) + env.out.print("foo3 = " + foo3.get_value().string()) \ No newline at end of file diff --git a/code-samples/sugar-update-explicit.pony b/code-samples/sugar-update-explicit.pony index 4c25764c..2332422d 100644 --- a/code-samples/sugar-update-explicit.pony +++ b/code-samples/sugar-update-explicit.pony @@ -1 +1,19 @@ -foo.update(37 where value = x) \ No newline at end of file +class Foo + var _x: U32 = 0 + + new create(x: U32) => + update(x, 2) + + fun ref update(x: U32, value: U32) => + _x = value * x + + fun get_value(): U32 => + _x + +actor Main + new create(env: Env) => + let foo = Foo(5) + let x: U32 = 10 + env.out.print("foo = " + foo.get_value().string()) + foo.update(37 where value = x) + env.out.print("foo = " + foo.get_value().string()) \ No newline at end of file diff --git a/code-samples/sugar-update-implicit.pony b/code-samples/sugar-update-implicit.pony index 5e1c6311..52db1eea 100644 --- a/code-samples/sugar-update-implicit.pony +++ b/code-samples/sugar-update-implicit.pony @@ -1 +1,19 @@ -foo(37) = x \ No newline at end of file +class Foo + var _x: U32 = 0 + + new create(x: U32) => + update(x, 2) + + fun ref update(x: U32, value: U32) => + _x = value * x + + fun get_value(): U32 => + _x + +actor Main + new create(env: Env) => + let foo = Foo(5) + let x: U32 = 10 + env.out.print("foo = " + foo.get_value().string()) + foo(37) = x + env.out.print("foo = " + foo.get_value().string()) \ No newline at end of file diff --git a/docs/expressions/sugar.md b/docs/expressions/sugar.md index 3daf4d5e..b263cde8 100644 --- a/docs/expressions/sugar.md +++ b/docs/expressions/sugar.md @@ -87,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 ---8<-- "sugar-update-implicit.pony" +--8<-- "sugar-update-implicit.pony:18:18" ``` becomes: ```pony ---8<-- "sugar-update-explicit.pony" +--8<-- "sugar-update-explicit.pony:18:18" ``` 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`. @@ -101,7 +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 ---8<-- "sugar-update-additional-parameters.pony" +--8<-- "sugar-update-additional-parameters.pony:23:25" ``` __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. From cdb59e64279d23ece2e1ad08f69b0d3977131fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Sun, 26 May 2024 09:23:54 +0200 Subject: [PATCH 44/58] Make "object literals" runnable --- .../object-literals-actor-literal.pony | 10 +++++--- .../object-literals-closing-over-values.pony | 7 +++++- .../object-literals-fields-assignment.pony | 7 +++++- ...als-lambda-as-explicit-object-literal.pony | 10 +++++--- ...rals-lambda-capture-and-rename-values.pony | 10 +++++++- ...object-literals-lambda-capture-values.pony | 4 ++++ ...capability-as-explicit-object-literal.pony | 10 +++++--- ...rals-lambda-with-reference-capability.pony | 6 ++++- code-samples/object-literals-lambda.pony | 6 ++++- ...iterals-object-literal-with-interface.pony | 14 +++++++---- .../object-literals-object-literal.pony | 10 +++++--- .../object-literals-reference-capability.pony | 7 +++++- docs/expressions/object-literals.md | 24 +++++++++---------- 13 files changed, 91 insertions(+), 34 deletions(-) diff --git a/code-samples/object-literals-actor-literal.pony b/code-samples/object-literals-actor-literal.pony index d914ddf5..a5855f30 100644 --- a/code-samples/object-literals-actor-literal.pony +++ b/code-samples/object-literals-actor-literal.pony @@ -1,3 +1,7 @@ -object - be apply() => env.out.print("hi") -end \ No newline at end of file +actor Main + new create(env: Env) => + let lambda = + object + be apply() => env.out.print("hi") + end + lambda() \ 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 index 908c47c8..79e2b96a 100644 --- a/code-samples/object-literals-closing-over-values.pony +++ b/code-samples/object-literals-closing-over-values.pony @@ -5,4 +5,9 @@ class Foo object iso is Hashable fun apply(): String => str fun hash(): USize => str.hash() - end \ No newline at end of file + end + +actor Main + new create(env: Env) => + let x = "hello world" + env.out.print(x + ": " + Foo.foo(x).hash().string()) \ No newline at end of file diff --git a/code-samples/object-literals-fields-assignment.pony b/code-samples/object-literals-fields-assignment.pony index d83fd3db..5dbfd10f 100644 --- a/code-samples/object-literals-fields-assignment.pony +++ b/code-samples/object-literals-fields-assignment.pony @@ -6,4 +6,9 @@ class Foo let s: String = str fun apply(): String => s fun hash(): USize => s.hash() - end \ No newline at end of file + end + +actor Main + new create(env: Env) => + let x = "hello world" + env.out.print(x + ": " + Foo.foo(x).hash().string()) \ 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 index 069efd52..e58823e3 100644 --- a/code-samples/object-literals-lambda-as-explicit-object-literal.pony +++ b/code-samples/object-literals-lambda-as-explicit-object-literal.pony @@ -1,3 +1,7 @@ -object - fun apply(s: String): String => "lambda: " + s -end \ No newline at end of file +actor Main + new create(env: Env) => + let lambda = + object + fun apply(s: String): String => "lambda: " + s + end + env.out.print(lambda("hello world")) \ 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 index ea8bd637..9cc80dbb 100644 --- a/code-samples/object-literals-lambda-capture-and-rename-values.pony +++ b/code-samples/object-literals-lambda-capture-and-rename-values.pony @@ -1,2 +1,10 @@ +actor Main new create(env: Env) => - foo({(s: String)(myenv = env) => myenv.out.print(s) }) \ No newline at end of file + Foo(env) + +class Foo + new create(env: Env) => + foo({(s: String)(myenv = env) => myenv.out.print(s) }) + + fun foo(f: {(String)}) => + f("Hello World") \ 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 index 86e5734d..34bfea8e 100644 --- a/code-samples/object-literals-lambda-capture-values.pony +++ b/code-samples/object-literals-lambda-capture-values.pony @@ -1,3 +1,7 @@ +actor Main + new create(env: Env) => + Foo(env) + class Foo new create(env: Env) => foo({(s: String)(env) => env.out.print(s) }) 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 index b318098e..2ba1e315 100644 --- 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 @@ -1,3 +1,7 @@ -object iso - fun apply(s: String): String => "lambda: " + s -end \ No newline at end of file +actor Main + new create(env: Env) => + let lambda = + object iso + fun apply(s: String): String => "lambda: " + s + end + env.out.print(lambda("hello world")) \ 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 index 96f7d281..39223ab9 100644 --- a/code-samples/object-literals-lambda-with-reference-capability.pony +++ b/code-samples/object-literals-lambda-with-reference-capability.pony @@ -1 +1,5 @@ -{(s: String): String => "lambda: " + s } iso \ No newline at end of file +actor Main + new create(env: Env) => + let lambda = + {(s: String): String => "lambda: " + s } iso + env.out.print(lambda("hello world")) \ No newline at end of file diff --git a/code-samples/object-literals-lambda.pony b/code-samples/object-literals-lambda.pony index 7d74078f..15eb308a 100644 --- a/code-samples/object-literals-lambda.pony +++ b/code-samples/object-literals-lambda.pony @@ -1 +1,5 @@ -{(s: String): String => "lambda: " + s } \ No newline at end of file +actor Main + new create(env: Env) => + let lambda = + {(s: String): String => "lambda: " + s } + env.out.print(lambda("hello world")) \ 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 index 1a27f264..97088678 100644 --- a/code-samples/object-literals-object-literal-with-interface.pony +++ b/code-samples/object-literals-object-literal-with-interface.pony @@ -1,4 +1,10 @@ -object is Hashable - fun apply(): String => "hi" - fun hash(): USize => this().hash() -end \ No newline at end of file +use "collections" + +actor Main + new create(env: Env) => + let sayHi = + object is Hashable + fun apply(): String => "hi" + fun hash(): USize => this().hash() + end + env.out.print(sayHi() + ": " + sayHi.hash().string()) \ No newline at end of file diff --git a/code-samples/object-literals-object-literal.pony b/code-samples/object-literals-object-literal.pony index 602c3eab..c3abaf41 100644 --- a/code-samples/object-literals-object-literal.pony +++ b/code-samples/object-literals-object-literal.pony @@ -1,3 +1,7 @@ -object - fun apply(): String => "hi" -end \ No newline at end of file +actor Main + new create(env: Env) => + let sayHi = + object + fun apply(): String => "hi" + end + env.out.print(sayHi()) \ No newline at end of file diff --git a/code-samples/object-literals-reference-capability.pony b/code-samples/object-literals-reference-capability.pony index 99522c1e..0a39a8e7 100644 --- a/code-samples/object-literals-reference-capability.pony +++ b/code-samples/object-literals-reference-capability.pony @@ -6,4 +6,9 @@ class Foo let s: String = str fun apply(): String => s fun hash(): USize => s.hash() - end \ No newline at end of file + end + +actor Main + new create(env: Env) => + let x = "hello world" + env.out.print(x + ": " + Foo.foo(x).hash().string()) \ No newline at end of file diff --git a/docs/expressions/object-literals.md b/docs/expressions/object-literals.md index eafe9d00..464b50b4 100644 --- a/docs/expressions/object-literals.md +++ b/docs/expressions/object-literals.md @@ -9,19 +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 ---8<-- "object-literals-object-literal.pony" +--8<-- "object-literals-object-literal.pony:4:6" ``` 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 ---8<-- "object-literals-object-literal-with-interface.pony" +--8<-- "object-literals-object-literal-with-interface.pony:6:9" ``` 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 ---8<-- "object-literals-fields-assignment.pony" +--8<-- "object-literals-fields-assignment.pony:1:9" ``` 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). @@ -29,13 +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 ---8<-- "object-literals-reference-capability.pony" +--8<-- "object-literals-reference-capability.pony:1:9" ``` 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 ---8<-- "object-literals-closing-over-values.pony" +--8<-- "object-literals-closing-over-values.pony:1:8" ``` ## Lambdas @@ -43,37 +43,37 @@ We can also implicitly capture values from the lexical scope by using them in th 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 ---8<-- "object-literals-lambda.pony" +--8<-- "object-literals-lambda.pony:5:5" ``` This produces the same code as: ```pony ---8<-- "object-literals-lambda-as-explicit-object-literal.pony" +--8<-- "object-literals-lambda-as-explicit-object-literal.pony:4:6" ``` The reference capability of the lambda object can be declared by appending it after the closing curly bracket: ```pony ---8<-- "object-literals-lambda-with-reference-capability.pony" +--8<-- "object-literals-lambda-with-reference-capability.pony:4:4" ``` This produces the same code as: ```pony ---8<-- "object-literals-lambda-with-reference-capability-as-explicit-object-literal.pony" +--8<-- "object-literals-lambda-with-reference-capability-as-explicit-object-literal.pony:4:6" ``` 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 ---8<-- "object-literals-lambda-capture-values.pony" +--8<-- "object-literals-lambda-capture-values.pony:5:10" ``` 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 ---8<-- "object-literals-lambda-capture-and-rename-values.pony" +--8<-- "object-literals-lambda-capture-and-rename-values.pony:6:7" ``` 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. @@ -101,7 +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 ---8<-- "object-literals-actor-literal.pony" +--8<-- "object-literals-actor-literal.pony:4:6" ``` An actor literal is always returned as a `tag`. From 9efb8ea137db4f02bdd8dce29e31610dd76fad9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Mon, 27 May 2024 18:56:08 +0200 Subject: [PATCH 45/58] Make "examples" runnable --- ...es-examples-create-arrays-with-values.pony | 30 +++++++++++++++---- ...numeration-with-values-with-namespace.pony | 19 +++++++++++- ...ices-examples-enumeration-with-values.pony | 19 +++++++++++- docs/appendices/examples.md | 6 ++-- 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/code-samples/appendices-examples-create-arrays-with-values.pony b/code-samples/appendices-examples-create-arrays-with-values.pony index 905c3859..f9206e84 100644 --- a/code-samples/appendices-examples-create-arrays-with-values.pony +++ b/code-samples/appendices-examples-create-arrays-with-values.pony @@ -1,5 +1,25 @@ -let dice: Array[U32] = [1; 2; 3 - 4 - 5 - 6 -] \ No newline at end of file +use "random" + +actor Main + new create(env: Env) => + let dice: Array[U32] = [1; 2; 3 + 4 + 5 + 6 + ] + Rand.shuffle[U32](dice) + for numberOfSpots in dice.values() do + env.out.print("You rolled a " + _ordinal(numberOfSpots)) + end + + fun _ordinal(number: U32): String => + match number + | 1 => "one" + | 2 => "two" + | 3 => "three" + | 4 => "four" + | 5 => "five" + | 6 => "six" + else + "out of range" + end \ 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 index 35f4c222..dafeb7c9 100644 --- a/code-samples/appendices-examples-enumeration-with-values-with-namespace.pony +++ b/code-samples/appendices-examples-enumeration-with-values-with-namespace.pony @@ -1,3 +1,20 @@ +use "format" +use "collections/persistent" + primitive Colours fun black(): U32 => 0xFF000000 - fun red(): U32 => 0xFFFF0000 \ No newline at end of file + fun red(): U32 => 0xFFFF0000 + +interface val Applyable + fun apply(): U32 + +actor Main + new create(env: Env) => + let colorMap: Map[String, Applyable] = Map[String, Applyable].concat([ + ("red", Colours~red()) + ("black", Colours~black()) + ].values()) + + for (colorName, color) in colorMap.pairs() do + env.out.print(colorName + ": #" + Format.int[U32](color(), FormatHexBare)) + end \ 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 index 1be5b0e2..e17df830 100644 --- a/code-samples/appendices-examples-enumeration-with-values.pony +++ b/code-samples/appendices-examples-enumeration-with-values.pony @@ -1,2 +1,19 @@ +use "format" +use "collections/persistent" + primitive Black fun apply(): U32 => 0xFF000000 -primitive Red fun apply(): U32 => 0xFFFF0000 \ No newline at end of file +primitive Red fun apply(): U32 => 0xFFFF0000 + +type Color is (Red | Black) + +actor Main + new create(env: Env) => + + let colorMap: Map[String, Color] = Map[String, Color].concat([ + ("red", Red) + ("black", Black) + ].values()) + + for (colorName, color) in colorMap.pairs() do + env.out.print(colorName + ": #" + Format.int[U32](color(), FormatHexBare)) + end \ No newline at end of file diff --git a/docs/appendices/examples.md b/docs/appendices/examples.md index 437d0979..1a451653 100644 --- a/docs/appendices/examples.md +++ b/docs/appendices/examples.md @@ -5,13 +5,13 @@ Small _how do I_ examples for Pony. These will eventually find another home. Unt ## Enumeration with values ```pony ---8<-- "appendices-examples-enumeration-with-values.pony" +--8<-- "appendices-examples-enumeration-with-values.pony:4:5" ``` ## Enumeration with values with namespace ```pony ---8<-- "appendices-examples-enumeration-with-values-with-namespace.pony" +--8<-- "appendices-examples-enumeration-with-values-with-namespace.pony:4:6" ``` ## Enumeration which can be iterated @@ -69,7 +69,7 @@ Some assertions you can make with `TestHelper` are Single values can be separated by semicolon or newline. ```pony ---8<-- "appendices-examples-create-arrays-with-values.pony" +--8<-- "appendices-examples-create-arrays-with-values.pony:5:9" ``` ## How to modify a lexically captured variable in a closure From c4cdf7d3e25b8e1d4d82820f4f24df1483b9ea20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Mon, 27 May 2024 19:25:38 +0200 Subject: [PATCH 46/58] Make "whitespace" runnable --- ...ace-do-a-then-do-a-unary-negation-of-b.pony | 18 ++++++++++++++++-- ...ppendices-whitespace-subtract-b-from-a.pony | 16 +++++++++++++++- docs/appendices/whitespace.md | 4 ++-- 3 files changed, 33 insertions(+), 5 deletions(-) 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 index 045ea728..a69e2399 100644 --- 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 @@ -1,2 +1,16 @@ -a --b \ No newline at end of file +use "format" +use "collections/persistent" + +primitive Black fun apply(): U32 => 0xFF000000 +primitive Red fun apply(): U32 => 0xFFFF0000 + +type Color is (Red | Black) + +actor Main + new create(env: Env) => + let a: I8 = 1 + let b: I8 = 3 + let c: I8 = + a + -b + env.out.print(c.string()) \ 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 index f76d71bf..5baa9b23 100644 --- a/code-samples/appendices-whitespace-subtract-b-from-a.pony +++ b/code-samples/appendices-whitespace-subtract-b-from-a.pony @@ -1 +1,15 @@ -a - b \ No newline at end of file +use "format" +use "collections/persistent" + +primitive Black fun apply(): U32 => 0xFF000000 +primitive Red fun apply(): U32 => 0xFFFF0000 + +type Color is (Red | Black) + +actor Main + new create(env: Env) => + let a: I8 = 1 + let b: I8 = 3 + let c: I8 = + a - b + env.out.print(c.string()) \ No newline at end of file diff --git a/docs/appendices/whitespace.md b/docs/appendices/whitespace.md index e909b432..e7bec4b0 100644 --- a/docs/appendices/whitespace.md +++ b/docs/appendices/whitespace.md @@ -19,13 +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 ---8<-- "appendices-whitespace-subtract-b-from-a.pony" +--8<-- "appendices-whitespace-subtract-b-from-a.pony:14:14" ``` That means "subtract b from a". ```pony ---8<-- "appendices-whitespace-do-a-then-do-a-unary-negation-of-b.pony" +--8<-- "appendices-whitespace-do-a-then-do-a-unary-negation-of-b.pony:14:15" ``` That means "first do a, then, in a new expression, do a unary negation of b". From 00bc87f6bc06235ae81ea300bd5a942d991ff268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Mon, 27 May 2024 20:50:35 +0200 Subject: [PATCH 47/58] Make "annotations" runnable --- ...ons-empty-with-nosupertype-annotation.pony | 7 +++- ...tions-likely-and-unlikely-annotations.pony | 42 +++++++++++++------ ...endices-annotations-packed-annotation.pony | 10 ++++- docs/appendices/annotations.md | 11 +++-- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/code-samples/appendices-annotations-empty-with-nosupertype-annotation.pony b/code-samples/appendices-annotations-empty-with-nosupertype-annotation.pony index 10d96d82..31889564 100644 --- a/code-samples/appendices-annotations-empty-with-nosupertype-annotation.pony +++ b/code-samples/appendices-annotations-empty-with-nosupertype-annotation.pony @@ -4,4 +4,9 @@ class Foo fun foo[A: Any](a: (A | Empty val)) => match consume a | let a': A => None - end \ No newline at end of file + end + +actor Main + new create(env: Env) => + let foo: Foo = Foo + env.out.print(foo.foo[Any]("Something").string()) \ 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 index ea00d0af..00c802ab 100644 --- a/code-samples/appendices-annotations-likely-and-unlikely-annotations.pony +++ b/code-samples/appendices-annotations-likely-and-unlikely-annotations.pony @@ -1,16 +1,32 @@ -if \likely\ cond then - foo -end +type T is (U32|U8) -while \unlikely\ cond then - bar -end +actor Main + new create(env: Env) => + let foo = "foo" + let bar = "bar" + let baz = "baz" + var cond = true + let obj: U32 = 42 + let expr: U32 = 42 + + if \likely\ cond then + foo + end -repeat - baz -until \likely\ cond end + cond = false + while \unlikely\ cond do + bar + end -match obj -| \likely\ expr => foo -| \unlikely\ let capt: T => bar -end \ No newline at end of file + cond = true + repeat + baz + until \likely\ cond end + + let res = + match obj + | \likely\ expr => foo + | \unlikely\ let capt: T => bar + end + + env.out.print("res = " + res) \ No newline at end of file diff --git a/code-samples/appendices-annotations-packed-annotation.pony b/code-samples/appendices-annotations-packed-annotation.pony index e13d8926..e99a08fb 100644 --- a/code-samples/appendices-annotations-packed-annotation.pony +++ b/code-samples/appendices-annotations-packed-annotation.pony @@ -1,3 +1,11 @@ struct \packed\ MyPackedStruct var x: U8 - var y: U32 \ No newline at end of file + var y: U32 + + new create() => + x = 0 + y = 1 + +actor Main + new create(env: Env) => + env.out.print("{\n\t\"x\": " + MyPackedStruct.x.string() + ",\n\t\"y\": " + MyPackedStruct.y.string() + "\n}") \ No newline at end of file diff --git a/docs/appendices/annotations.md b/docs/appendices/annotations.md index d30175be..4d002e6e 100644 --- a/docs/appendices/annotations.md +++ b/docs/appendices/annotations.md @@ -51,7 +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 ---8<-- "appendices-annotations-packed-annotation.pony" +--8<-- "appendices-annotations-packed-annotation.pony:1:3" ``` #### `likely` and `unlikely` @@ -59,7 +59,12 @@ Recognised on a `struct` declaration. Removes padding in the associated `struct` 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 ---8<-- "appendices-annotations-likely-and-unlikely-annotations.pony" +--8<-- +appendices-annotations-likely-and-unlikely-annotations.pony:12:14 +appendices-annotations-likely-and-unlikely-annotations.pony:17:19 +appendices-annotations-likely-and-unlikely-annotations.pony:22:24 +appendices-annotations-likely-and-unlikely-annotations.pony:27:30 +--8<-- ``` ### `nodoc` @@ -85,7 +90,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 ---8<-- "appendices-annotations-empty-with-nosupertype-annotation.pony" +--8<-- "appendices-annotations-empty-with-nosupertype-annotation.pony:1:7" ``` `nosupertype` is particularly valuable when constructing generic classes like collections that need a marker class to describe "lack of an item". From 3c597a2546f5169ff549d747c7b71f05ad20815f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 28 May 2024 01:42:00 +0200 Subject: [PATCH 48/58] Make "Calling C" runnable --- code-samples/calling-c-from-c-struct.pony | 12 +++++++++- code-samples/calling-c-ioctl-struct.pony | 14 +++++++----- .../calling-c-variadic-c-functions.pony | 13 ++++++++--- code-samples/calling-c-writev-struct.pony | 12 +++++----- code-samples/calling-c-writev-tuple.pony | 8 ++++--- docs/c-ffi/calling-c.md | 22 ++++++++++++++----- 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/code-samples/calling-c-from-c-struct.pony b/code-samples/calling-c-from-c-struct.pony index fccbeb2b..a9ca33d9 100644 --- a/code-samples/calling-c-from-c-struct.pony +++ b/code-samples/calling-c-from-c-struct.pony @@ -2,4 +2,14 @@ use @from_c[Rect]() struct Rect var length: U16 - var width: U16 \ No newline at end of file + var width: U16 + + new create(length': U16, width': U16) => + length = length' + width = width' + +actor Main + new create(env: Env) => + let rect = Rect(2, 3) + + env.out.print("This rect is " + rect.length.string() + " cm x " + rect.width.string() + " cm") \ No newline at end of file diff --git a/code-samples/calling-c-ioctl-struct.pony b/code-samples/calling-c-ioctl-struct.pony index b7debc8b..e104ee78 100644 --- a/code-samples/calling-c-ioctl-struct.pony +++ b/code-samples/calling-c-ioctl-struct.pony @@ -5,9 +5,11 @@ struct Winsize 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 + +actor Main + new create(env: Env) => + 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-variadic-c-functions.pony b/code-samples/calling-c-variadic-c-functions.pony index f8e59f50..f252e654 100644 --- a/code-samples/calling-c-variadic-c-functions.pony +++ b/code-samples/calling-c-variadic-c-functions.pony @@ -1,5 +1,12 @@ 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 + +actor Main + let _current_t: I64 = 5 + let _last_t: I64 = 1 + let _partial_count: F64 = 42.0 + + new create(env: Env) => + 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) diff --git a/code-samples/calling-c-writev-struct.pony b/code-samples/calling-c-writev-struct.pony index 125be5b2..37244865 100644 --- a/code-samples/calling-c-writev-struct.pony +++ b/code-samples/calling-c-writev-struct.pony @@ -10,8 +10,10 @@ 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 +actor Main + new create(env: Env) => + 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 index 531f5965..89bae9f5 100644 --- a/code-samples/calling-c-writev-tuple.pony +++ b/code-samples/calling-c-writev-tuple.pony @@ -1,5 +1,7 @@ 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 +actor Main + new create(env: Env) => + 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/docs/c-ffi/calling-c.md b/docs/c-ffi/calling-c.md index a0e90b08..e2d41f87 100644 --- a/docs/c-ffi/calling-c.md +++ b/docs/c-ffi/calling-c.md @@ -61,13 +61,19 @@ 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 ---8<-- "calling-c-writev-struct.pony" +--8<-- +calling-c-writev-struct.pony:1:12 +calling-c-writev-struct.pony:15:19 +--8<-- ``` 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 ---8<-- "calling-c-writev-tuple.pony" +--8<-- +calling-c-writev-tuple.pony:1:2 +calling-c-writev-tuple.pony:5:7 +--8<-- ``` 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_. @@ -79,7 +85,10 @@ 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 ---8<-- "calling-c-ioctl-struct.pony" +--8<-- +calling-c-ioctl-struct.pony:1:8 +calling-c-ioctl-struct.pony:11:15 +--8<-- ``` 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. @@ -87,7 +96,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 ---8<-- "calling-c-from-c-struct.pony" +--8<-- "calling-c-from-c-struct.pony:1:5" ``` As we saw earlier, you can also use a `Pointer[(U16, U16)]` as well. It is the equivalent to our `Rect`. @@ -141,7 +150,10 @@ 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 ---8<-- "calling-c-variadic-c-functions.pony" +--8<-- +calling-c-variadic-c-functions.pony:1:2 +calling-c-variadic-c-functions.pony:10:12 +--8<-- ``` 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. From 191fc0945b66b64c7cb9fced255039c94e61ec87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 28 May 2024 01:59:12 +0200 Subject: [PATCH 49/58] Making "C ABI" runnable --- .../c-abi-jump-consistent-hashing.pony | 30 +++++++++++-------- docs/c-ffi/c-abi.md | 2 +- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/code-samples/c-abi-jump-consistent-hashing.pony b/code-samples/c-abi-jump-consistent-hashing.pony index dbb9920c..daf54193 100644 --- a/code-samples/c-abi-jump-consistent-hashing.pony +++ b/code-samples/c-abi-jump-consistent-hashing.pony @@ -1,15 +1,19 @@ -// Jump consistent hashing in Pony, with an inline pseudo random generator -// https://arxiv.org/abs/1406.2294 +actor Main + new create(env: Env) => + env.out.print("jch: " + jch(U64(10), U32(20)).string()) -fun jch(key: U64, buckets: U32): I32 => - var k = key - var b = I64(0) - var j = I64(0) + // Jump consistent hashing in Pony, with an inline pseudo random generator + // https://arxiv.org/abs/1406.2294 - 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 + 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/docs/c-ffi/c-abi.md b/docs/c-ffi/c-abi.md index cd9fc54a..04b59c77 100644 --- a/docs/c-ffi/c-abi.md +++ b/docs/c-ffi/c-abi.md @@ -9,7 +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 ---8<-- "c-abi-jump-consistent-hashing.pony" +--8<-- "c-abi-jump-consistent-hashing.pony:5:18" ``` Let's say we wish to compare the pure Pony performance to an existing C function with the following header: From 0908c13bfcf38ee38a6813ac3a3f0e0255ae8a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 29 May 2024 08:51:15 +0200 Subject: [PATCH 50/58] Make "Generics Overview" runnable --- code-samples/generics-foo-string.pony | 7 ++++++- .../generics-generic-class-initialization.pony | 3 --- ...nerics-type-parameter-defaults-definition.pony | 9 --------- ...cs-type-parameter-defaults-initialization.pony | 3 --- .../generics-type-parameter-defaults.pony | 15 +++++++++++++++ docs/generics/index.md | 12 ++++++++---- 6 files changed, 29 insertions(+), 20 deletions(-) delete mode 100644 code-samples/generics-generic-class-initialization.pony delete mode 100644 code-samples/generics-type-parameter-defaults-definition.pony delete mode 100644 code-samples/generics-type-parameter-defaults-initialization.pony create mode 100644 code-samples/generics-type-parameter-defaults.pony diff --git a/code-samples/generics-foo-string.pony b/code-samples/generics-foo-string.pony index 4d4fa9c5..eb599108 100644 --- a/code-samples/generics-foo-string.pony +++ b/code-samples/generics-foo-string.pony @@ -6,4 +6,9 @@ class FooString fun get(): String val => _c - fun ref set(c: String val) => _c = c \ No newline at end of file + fun ref set(c: String val) => _c = c + +actor Main + new create(env:Env) => + let c = FooString("Hello") + env.out.print(c.get()) \ No newline at end of file diff --git a/code-samples/generics-generic-class-initialization.pony b/code-samples/generics-generic-class-initialization.pony deleted file mode 100644 index fd97d742..00000000 --- a/code-samples/generics-generic-class-initialization.pony +++ /dev/null @@ -1,3 +0,0 @@ -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-type-parameter-defaults-definition.pony b/code-samples/generics-type-parameter-defaults-definition.pony deleted file mode 100644 index c6f4ac5d..00000000 --- a/code-samples/generics-type-parameter-defaults-definition.pony +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 94ccad6c..00000000 --- a/code-samples/generics-type-parameter-defaults-initialization.pony +++ /dev/null @@ -1,3 +0,0 @@ -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/generics-type-parameter-defaults.pony b/code-samples/generics-type-parameter-defaults.pony new file mode 100644 index 00000000..6f70dcaa --- /dev/null +++ b/code-samples/generics-type-parameter-defaults.pony @@ -0,0 +1,15 @@ +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 + +actor Main + new create(env:Env) => + let a = Bar(42) + let b = Bar[USize](42) + let c = Bar[F32](1.5) \ No newline at end of file diff --git a/docs/generics/index.md b/docs/generics/index.md index 93799b00..5311af92 100644 --- a/docs/generics/index.md +++ b/docs/generics/index.md @@ -28,13 +28,17 @@ 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 ---8<-- "generics-generic-class-initialization.pony" +--8<-- +generics-foo-with-any-val.pony:13:13 +generics-foo-with-any-val.pony:18:18 +generics-foo-with-any-val.pony:21:21 +--8<-- ``` 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 ---8<-- "generics-foo-string.pony" +--8<-- "generics-foo-string.pony:1:9" ``` ### Type parameter defaults @@ -43,13 +47,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 ---8<-- "generics-type-parameter-defaults-definition.pony" +--8<-- "generics-type-parameter-defaults.pony:1:9" ``` 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 ---8<-- "generics-type-parameter-defaults-initialization.pony" +--8<-- "generics-type-parameter-defaults.pony:13:15" ``` Note that we could simply write `class Bar[A: Any box = USize]` because `val` is the default reference capability of the `USize` type. From ed0ca981ef2c9565d8b94f9c9e9d2ab51a2f393f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 29 May 2024 09:02:21 +0200 Subject: [PATCH 51/58] Move error message back into markdown file --- ...s-and-reference-capabilities-foo-iso-error-message.txt | 7 ------- docs/generics/generics-and-reference-capabilities.md | 8 +++++++- 2 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 code-samples/generics-and-reference-capabilities-foo-iso-error-message.txt 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 deleted file mode 100644 index 4c372132..00000000 --- a/code-samples/generics-and-reference-capabilities-foo-iso-error-message.txt +++ /dev/null @@ -1,7 +0,0 @@ -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/docs/generics/generics-and-reference-capabilities.md b/docs/generics/generics-and-reference-capabilities.md index fa7e634b..3b99cbfd 100644 --- a/docs/generics/generics-and-reference-capabilities.md +++ b/docs/generics/generics-and-reference-capabilities.md @@ -50,7 +50,13 @@ That compiles and runs, so `ref` is valid now. The real test though is `iso`. Le This fails to compile. The first error is: ```error ---8<-- "generics-and-reference-capabilities-foo-iso-error-message.txt" +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) => + ^ ``` 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: From 60308f438e986413da499857cb49208ec655db5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 29 May 2024 09:56:35 +0200 Subject: [PATCH 52/58] Make "ponycheck" runnable --- code-samples/ponycheck-ponytest.pony | 9 --------- code-samples/ponycheck-usage.pony | 10 +++++++++- docs/testing/ponycheck.md | 7 +++++-- 3 files changed, 14 insertions(+), 12 deletions(-) delete mode 100644 code-samples/ponycheck-ponytest.pony diff --git a/code-samples/ponycheck-ponytest.pony b/code-samples/ponycheck-ponytest.pony deleted file mode 100644 index 961a19ae..00000000 --- a/code-samples/ponycheck-ponytest.pony +++ /dev/null @@ -1,9 +0,0 @@ -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.pony b/code-samples/ponycheck-usage.pony index abb56168..455fd35e 100644 --- a/code-samples/ponycheck-usage.pony +++ b/code-samples/ponycheck-usage.pony @@ -1,4 +1,5 @@ use "pony_test" +use "pony_check" class _MyFirstProperty is Property1[String] fun name(): String => @@ -8,4 +9,11 @@ class _MyFirstProperty is Property1[String] Generators.ascii() fun property(arg1: String, ph: PropertyHelper) => - ph.assert_eq[String](arg1, arg1) \ No newline at end of file + ph.assert_eq[String](arg1, arg1) + +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/docs/testing/ponycheck.md b/docs/testing/ponycheck.md index fc01ec8f..8bcc7575 100644 --- a/docs/testing/ponycheck.md +++ b/docs/testing/ponycheck.md @@ -19,7 +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 ---8<-- "ponycheck-usage.pony" +--8<-- "ponycheck-usage.pony:2:12" ``` 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 @@ -35,7 +35,10 @@ Below are two classic list reverse properties from the QuickCheck paper adapted 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 ---8<-- "ponycheck-ponytest.pony" +--8<-- +ponycheck-usage.pony:1:3 +ponycheck-usage.pony:14:19 +--8<-- ``` It is also possible to integrate any number of properties directly into one From 93c3572fdec129efed463afa00ea15c6a25f4f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 29 May 2024 10:34:07 +0200 Subject: [PATCH 53/58] Make "gotchas" runnable --- code-samples/divide-by-zero-floats.pony | 6 ++++- code-samples/divide-by-zero.pony | 5 ++++- code-samples/recursion.pony | 30 +++++++++++++++---------- docs/gotchas/divide-by-zero.md | 4 ++-- docs/gotchas/recursion.md | 2 +- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/code-samples/divide-by-zero-floats.pony b/code-samples/divide-by-zero-floats.pony index f608e0da..09685970 100644 --- a/code-samples/divide-by-zero-floats.pony +++ b/code-samples/divide-by-zero-floats.pony @@ -1 +1,5 @@ -let x = F64(1.5) /~ F64(0.5) \ No newline at end of file +actor Main + new create(env: Env) => + // the value of x is undefined + let x = F64(1.5) /~ F64(0.5) + env.out.print("1.5÷0.5 = " + x.string()) \ No newline at end of file diff --git a/code-samples/divide-by-zero.pony b/code-samples/divide-by-zero.pony index b0f8757f..e34ec879 100644 --- a/code-samples/divide-by-zero.pony +++ b/code-samples/divide-by-zero.pony @@ -1 +1,4 @@ -let x = I64(1) / I64(0) \ No newline at end of file +actor Main + new create(env: Env) => + let x = I64(1) / I64(0) + env.out.print("1÷0 = " + x.string()) \ No newline at end of file diff --git a/code-samples/recursion.pony b/code-samples/recursion.pony index d2e92ce9..040af95f 100644 --- a/code-samples/recursion.pony +++ b/code-samples/recursion.pony @@ -1,13 +1,19 @@ -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 +actor Main + new create(env: Env) => + env.out.print(recursive_factorial(42).string()) + env.out.print(tail_recursive_factorial(42, 24).string()) + + 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/docs/gotchas/divide-by-zero.md b/docs/gotchas/divide-by-zero.md index 2dd35729..a6ba648f 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 ---8<-- "divide-by-zero.pony" +--8<-- "divide-by-zero.pony:3:3" ``` 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. @@ -37,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 ---8<-- "divide-by-zero-floats.pony" +--8<-- "divide-by-zero-floats.pony:4:4" ``` diff --git a/docs/gotchas/recursion.md b/docs/gotchas/recursion.md index 7448e855..56c7e1ca 100644 --- a/docs/gotchas/recursion.md +++ b/docs/gotchas/recursion.md @@ -5,7 +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 ---8<-- "recursion.pony" +--8<-- "recursion.pony:7:19" ``` 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. From f554f8013d818d8d0bd8a3e95dac6b3c938e7eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 29 May 2024 18:02:34 +0200 Subject: [PATCH 54/58] Fix whitespace --- code-samples/recursion.pony | 1 - docs/gotchas/recursion.md | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/code-samples/recursion.pony b/code-samples/recursion.pony index 040af95f..e0a0de48 100644 --- a/code-samples/recursion.pony +++ b/code-samples/recursion.pony @@ -1,4 +1,3 @@ - actor Main new create(env: Env) => env.out.print(recursive_factorial(42).string()) diff --git a/docs/gotchas/recursion.md b/docs/gotchas/recursion.md index 56c7e1ca..68e34d5a 100644 --- a/docs/gotchas/recursion.md +++ b/docs/gotchas/recursion.md @@ -5,7 +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 ---8<-- "recursion.pony:7:19" +--8<-- "recursion.pony:6:18" ``` 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. From 0f8e2257094ed548c2f7802e22b25b5805a038f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Wed, 29 May 2024 18:40:24 +0200 Subject: [PATCH 55/58] Use stdlib examples directly from ponylang/ponyc repository --- code-samples/calling-c-addressof.pony | 4 ---- ...rived-authority-authority-hierarchies.pony | 23 ------------------ .../recovering-capabilities-format-int.pony | 21 ---------------- ...s-and-interfaces-structural-subtyping.pony | 24 ++++++++++++------- code-samples/type-aliases-set-is.pony | 1 - code-samples/type-expressions-combined.pony | 1 - docs/c-ffi/calling-c.md | 6 ++++- docs/object-capabilities/derived-authority.md | 4 ++-- .../recovering-capabilities.md | 2 +- docs/types/traits-and-interfaces.md | 6 ++++- docs/types/type-aliases.md | 2 +- docs/types/type-expressions.md | 2 +- 12 files changed, 31 insertions(+), 65 deletions(-) delete mode 100644 code-samples/calling-c-addressof.pony delete mode 100644 code-samples/derived-authority-authority-hierarchies.pony delete mode 100644 code-samples/recovering-capabilities-format-int.pony delete mode 100644 code-samples/type-aliases-set-is.pony delete mode 100644 code-samples/type-expressions-combined.pony diff --git a/code-samples/calling-c-addressof.pony b/code-samples/calling-c-addressof.pony deleted file mode 100644 index d7f28aaa..00000000 --- a/code-samples/calling-c-addressof.pony +++ /dev/null @@ -1,4 +0,0 @@ -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/derived-authority-authority-hierarchies.pony b/code-samples/derived-authority-authority-hierarchies.pony deleted file mode 100644 index 65975fa4..00000000 --- a/code-samples/derived-authority-authority-hierarchies.pony +++ /dev/null @@ -1,23 +0,0 @@ -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/recovering-capabilities-format-int.pony b/code-samples/recovering-capabilities-format-int.pony deleted file mode 100644 index 8e978a1d..00000000 --- a/code-samples/recovering-capabilities-format-int.pony +++ /dev/null @@ -1,21 +0,0 @@ -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/traits-and-interfaces-structural-subtyping.pony b/code-samples/traits-and-interfaces-structural-subtyping.pony index 22b0cb4e..51c80835 100644 --- a/code-samples/traits-and-interfaces-structural-subtyping.pony +++ b/code-samples/traits-and-interfaces-structural-subtyping.pony @@ -1,11 +1,19 @@ -interface box Stringable - """ - Things that can be turned into a String. - """ - fun string(): String iso^ - """ - Generate a string representation of this object. - """ +actor Main + new create(env: Env) => + let divident: U8 = 1 + let divisor: U8 = 0 + let result = div_by_zero(divident, divisor) + match result + | let res: U8 => env.out.print(divident.string() + "/" + divisor.string() + " = " + res.string()) + | let err: ExecveError => env.err.print(divident.string() + " cannot be divided by " + divisor.string()) + end + + fun div_by_zero(divident: U8, divisor: U8): (U8 | ExecveError) => + try divident / divisor + return divident /? divisor + else + return ExecveError + end primitive ExecveError fun string(): String iso^ => "ExecveError".clone() \ No newline at end of file diff --git a/code-samples/type-aliases-set-is.pony b/code-samples/type-aliases-set-is.pony deleted file mode 100644 index 7555b9d2..00000000 --- a/code-samples/type-aliases-set-is.pony +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index f700f40d..00000000 --- a/code-samples/type-expressions-combined.pony +++ /dev/null @@ -1 +0,0 @@ -var _array: Array[((K, V) | _MapEmpty | _MapDeleted)] \ No newline at end of file diff --git a/docs/c-ffi/calling-c.md b/docs/c-ffi/calling-c.md index e2d41f87..eccad52f 100644 --- a/docs/c-ffi/calling-c.md +++ b/docs/c-ffi/calling-c.md @@ -43,7 +43,11 @@ 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 ---8<-- "calling-c-addressof.pony" +--8<-- +https://raw.githubusercontent.com/ponylang/ponyc/main/packages/builtin/float.pony:59 +// ... +https://raw.githubusercontent.com/ponylang/ponyc/main/packages/builtin/float.pony:432:424 +--8<-- ``` ### Get and Pass Pointers to FFI diff --git a/docs/object-capabilities/derived-authority.md b/docs/object-capabilities/derived-authority.md index afe2eff4..1fbad47b 100644 --- a/docs/object-capabilities/derived-authority.md +++ b/docs/object-capabilities/derived-authority.md @@ -53,13 +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 ---8<-- "derived-authority-authority-hierarchies.pony" +--8<-- "https://raw.githubusercontent.com/ponylang/ponyc/main/packages/net/auth.pony" ``` Look at the constructor for `TCPConnectAuth`: ```pony ---8<-- "derived-authority-authority-hierarchies.pony:22:22" +--8<-- "https://raw.githubusercontent.com/ponylang/ponyc/main/packages/net/auth.pony:22:22" ``` you might notice that this looks like a hierarchy of authorities: diff --git a/docs/reference-capabilities/recovering-capabilities.md b/docs/reference-capabilities/recovering-capabilities.md index 80b09347..589c24dc 100644 --- a/docs/reference-capabilities/recovering-capabilities.md +++ b/docs/reference-capabilities/recovering-capabilities.md @@ -23,7 +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 ---8<-- "recovering-capabilities-format-int.pony" +--8<-- "https://raw.githubusercontent.com/ponylang/ponyc/master/packages/format/_format_int.pony:81:101" ``` 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`. diff --git a/docs/types/traits-and-interfaces.md b/docs/types/traits-and-interfaces.md index 26a45f9f..6fdb73ef 100644 --- a/docs/types/traits-and-interfaces.md +++ b/docs/types/traits-and-interfaces.md @@ -29,7 +29,11 @@ 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 ---8<-- "traits-and-interfaces-structural-subtyping.pony" +--8<-- +https://raw.githubusercontent.com/ponylang/ponyc/main/packages/builtin/stringable.pony + +traits-and-interfaces-structural-subtyping.pony:18:19 +--8<-- ``` ## Nominal and structural subtyping in Pony diff --git a/docs/types/type-aliases.md b/docs/types/type-aliases.md index 97bab5a9..35ee3af1 100644 --- a/docs/types/type-aliases.md +++ b/docs/types/type-aliases.md @@ -62,7 +62,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 ---8<-- "type-aliases-set-is.pony" +--8<-- "https://raw.githubusercontent.com/ponylang/ponyc/main/packages/collections/set.pony:3:3" ``` 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. diff --git a/docs/types/type-expressions.md b/docs/types/type-expressions.md index 39d56dd9..8da2d2fc 100644 --- a/docs/types/type-expressions.md +++ b/docs/types/type-expressions.md @@ -61,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 ---8<-- "type-expressions-combined.pony" +--8<-- "https://raw.githubusercontent.com/ponylang/ponyc/main/packages/collections/map.pony:22" ``` Here we have an array where each element is either a tuple of `(K, V)` or a `_MapEmpty` or a `_MapDeleted`. From ee5490b9e762881829a2e1774e66234a3c52f60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:09:19 +0200 Subject: [PATCH 56/58] Enable url_download option for snippets from stdlib --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 5ba20cb9..f7217dc6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,6 +26,7 @@ markdown_extensions: base_path: ['code-samples'] check_paths: true dedent_subsections: true + url_download: true - smarty - toc: permalink: true From 51dca916005298cccc42b8b7f3dfc11c110b427c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:20:13 +0000 Subject: [PATCH 57/58] Revert "Enable url_download option for snippets from stdlib" This reverts commit ee5490b9e762881829a2e1774e66234a3c52f60c. --- mkdocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index f7217dc6..5ba20cb9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,7 +26,6 @@ markdown_extensions: base_path: ['code-samples'] check_paths: true dedent_subsections: true - url_download: true - smarty - toc: permalink: true From f3c555243bcf24ea093142427588539226d95b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4drich?= <11225821+shaedrich@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:20:31 +0000 Subject: [PATCH 58/58] Revert "Use stdlib examples directly from ponylang/ponyc repository" This reverts commit 0f8e2257094ed548c2f7802e22b25b5805a038f3. --- code-samples/calling-c-addressof.pony | 4 ++++ ...rived-authority-authority-hierarchies.pony | 23 ++++++++++++++++++ .../recovering-capabilities-format-int.pony | 21 ++++++++++++++++ ...s-and-interfaces-structural-subtyping.pony | 24 +++++++------------ code-samples/type-aliases-set-is.pony | 1 + code-samples/type-expressions-combined.pony | 1 + docs/c-ffi/calling-c.md | 6 +---- docs/object-capabilities/derived-authority.md | 4 ++-- .../recovering-capabilities.md | 2 +- docs/types/traits-and-interfaces.md | 6 +---- docs/types/type-aliases.md | 2 +- docs/types/type-expressions.md | 2 +- 12 files changed, 65 insertions(+), 31 deletions(-) create mode 100644 code-samples/calling-c-addressof.pony create mode 100644 code-samples/derived-authority-authority-hierarchies.pony create mode 100644 code-samples/recovering-capabilities-format-int.pony create mode 100644 code-samples/type-aliases-set-is.pony create mode 100644 code-samples/type-expressions-combined.pony 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/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/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/traits-and-interfaces-structural-subtyping.pony b/code-samples/traits-and-interfaces-structural-subtyping.pony index 51c80835..22b0cb4e 100644 --- a/code-samples/traits-and-interfaces-structural-subtyping.pony +++ b/code-samples/traits-and-interfaces-structural-subtyping.pony @@ -1,19 +1,11 @@ -actor Main - new create(env: Env) => - let divident: U8 = 1 - let divisor: U8 = 0 - let result = div_by_zero(divident, divisor) - match result - | let res: U8 => env.out.print(divident.string() + "/" + divisor.string() + " = " + res.string()) - | let err: ExecveError => env.err.print(divident.string() + " cannot be divided by " + divisor.string()) - end - - fun div_by_zero(divident: U8, divisor: U8): (U8 | ExecveError) => - try divident / divisor - return divident /? divisor - else - return ExecveError - end +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/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/docs/c-ffi/calling-c.md b/docs/c-ffi/calling-c.md index eccad52f..e2d41f87 100644 --- a/docs/c-ffi/calling-c.md +++ b/docs/c-ffi/calling-c.md @@ -43,11 +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 ---8<-- -https://raw.githubusercontent.com/ponylang/ponyc/main/packages/builtin/float.pony:59 -// ... -https://raw.githubusercontent.com/ponylang/ponyc/main/packages/builtin/float.pony:432:424 ---8<-- +--8<-- "calling-c-addressof.pony" ``` ### Get and Pass Pointers to FFI diff --git a/docs/object-capabilities/derived-authority.md b/docs/object-capabilities/derived-authority.md index 1fbad47b..afe2eff4 100644 --- a/docs/object-capabilities/derived-authority.md +++ b/docs/object-capabilities/derived-authority.md @@ -53,13 +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 ---8<-- "https://raw.githubusercontent.com/ponylang/ponyc/main/packages/net/auth.pony" +--8<-- "derived-authority-authority-hierarchies.pony" ``` Look at the constructor for `TCPConnectAuth`: ```pony ---8<-- "https://raw.githubusercontent.com/ponylang/ponyc/main/packages/net/auth.pony:22:22" +--8<-- "derived-authority-authority-hierarchies.pony:22:22" ``` you might notice that this looks like a hierarchy of authorities: diff --git a/docs/reference-capabilities/recovering-capabilities.md b/docs/reference-capabilities/recovering-capabilities.md index 589c24dc..80b09347 100644 --- a/docs/reference-capabilities/recovering-capabilities.md +++ b/docs/reference-capabilities/recovering-capabilities.md @@ -23,7 +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 ---8<-- "https://raw.githubusercontent.com/ponylang/ponyc/master/packages/format/_format_int.pony:81:101" +--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`. diff --git a/docs/types/traits-and-interfaces.md b/docs/types/traits-and-interfaces.md index 6fdb73ef..26a45f9f 100644 --- a/docs/types/traits-and-interfaces.md +++ b/docs/types/traits-and-interfaces.md @@ -29,11 +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 ---8<-- -https://raw.githubusercontent.com/ponylang/ponyc/main/packages/builtin/stringable.pony - -traits-and-interfaces-structural-subtyping.pony:18:19 ---8<-- +--8<-- "traits-and-interfaces-structural-subtyping.pony" ``` ## Nominal and structural subtyping in Pony diff --git a/docs/types/type-aliases.md b/docs/types/type-aliases.md index 35ee3af1..97bab5a9 100644 --- a/docs/types/type-aliases.md +++ b/docs/types/type-aliases.md @@ -62,7 +62,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 ---8<-- "https://raw.githubusercontent.com/ponylang/ponyc/main/packages/collections/set.pony:3:3" +--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. diff --git a/docs/types/type-expressions.md b/docs/types/type-expressions.md index 8da2d2fc..39d56dd9 100644 --- a/docs/types/type-expressions.md +++ b/docs/types/type-expressions.md @@ -61,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 ---8<-- "https://raw.githubusercontent.com/ponylang/ponyc/main/packages/collections/map.pony:22" +--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`.