diff --git a/next/README.md b/next/README.md index 4a7da75b..5b56489d 100644 --- a/next/README.md +++ b/next/README.md @@ -6,6 +6,11 @@ A new MoonBit docs framework based on Sphinx. ## Develop +You can just run `./autobuild.sh` to start the development server, it will +install the dependencies if they are not installed, and start the server on watch mode. + +Below are the instructions for manual setup. + ### Install - For Python Environment, execute command `> Python: Create Environment` for `next` using `requirement.txt` in VSCode, or: diff --git a/next/autobuild.sh b/next/autobuild.sh index 810812db..9b7bf691 100755 --- a/next/autobuild.sh +++ b/next/autobuild.sh @@ -1,4 +1,6 @@ #!/bin/sh set -e - -sphinx-autobuild . ./_build/html \ No newline at end of file +python3 -m venv .env +source .env/bin/activate +pip install -r requirements.txt +sphinx-autobuild . ./_build/html $@ diff --git a/next/language/async-experimental.md b/next/language/async-experimental.md index 19f0b3ee..5a59fdc0 100644 --- a/next/language/async-experimental.md +++ b/next/language/async-experimental.md @@ -1,8 +1,9 @@ -# Experimental async programming support +# Async programming support -MoonBit is providing experimental support for async programming. -But the design and API is still highly unstable, and may receive big breaking change in the future. -This page documents the current design, and we highly appreciate any feedback or experiment with current design. +MoonBit adopts a coroutine based approach to async programming which is similar to [Kotlin](https://kotlinlang.org/docs/coroutines-overview.html). +The compiler support and concrete syntax is stable while the async library is still under development and considered experimental. + + ## Async function Async functions are declared with the `async` keyword: @@ -23,7 +24,7 @@ MoonBit IDE will highlight the async function call with a different style. :end-before: end async function call syntax ``` -Async functions can only be called in async functions. +Async functions can only be called inside async functions. ```{warning} Currently, async functions @@ -46,8 +47,8 @@ currently users need to bind these two primitives manually to do async programmi There are two ways of reading these primitives: -- The coroutine reading: `%async.run` spawn a new coroutine, - and `%async.suspend` suspend current coroutine. +- The coroutine reading: `%async.run` spawns a new coroutine, + and `%async.suspend` suspends the current coroutine. The main difference with other languages here is: instead of yielding all the way to the caller of `%async.run`, resumption of the coroutine is handled by the callback passed to `%async.suspend` diff --git a/next/sources/async/moon.mod.json b/next/sources/async/moon.mod.json index ae3c49d6..1a5fe57d 100644 --- a/next/sources/async/moon.mod.json +++ b/next/sources/async/moon.mod.json @@ -6,5 +6,7 @@ "license": "Apache-2.0", "keywords": [], "description": "", - "source": "src" + "source": "src", + "preferred-target" : "js" + } diff --git a/next/sources/async/src/async.mbt b/next/sources/async/src/async.mbt index 9de825ef..83a99911 100644 --- a/next/sources/async/src/async.mbt +++ b/next/sources/async/src/async.mbt @@ -1,9 +1,9 @@ -// start async function declaration +///| start async function declaration async fn my_async_function() -> Unit { ... } -// anonymous/local function +///| anonymous/local function test { let async_lambda = async fn() { ... } async fn local_async_function() { @@ -15,10 +15,13 @@ test { // end async function declaration // start async function call syntax + +///| async fn some_async_function() -> Unit raise { ... } +///| async fn another_async_function() -> Unit raise { some_async_function() // rendered in italic font } @@ -26,10 +29,10 @@ async fn another_async_function() -> Unit raise { // start async primitive -/// `run_async` spawn a new coroutine and execute an async function in it +///| `run_async` spawn a new coroutine and execute an async function in it fn run_async(f : async () -> Unit) -> Unit = "%async.run" -/// `suspend` will suspend the execution of the current coroutine. +///| `suspend` will suspend the execution of the current coroutine. /// The suspension will be handled by a callback passed to `suspend` async fn[T, E : Error] suspend( // `f` is a callback for handling suspension @@ -44,86 +47,121 @@ async fn[T, E : Error] suspend( // end async primitive // start async example + +///| suberror MyError derive(Show) -async fn async_worker(throw_error~ : Bool) -> Unit raise MyError { +///| +async fn async_worker( + logger~ : &Logger, + throw_error~ : Bool +) -> Unit raise MyError { suspend(fn(resume_ok, resume_err) { if throw_error { resume_err(MyError) } else { resume_ok(()) - println("the end of the coroutine") + logger.write_string("the end of the coroutine\n") } }) } -// the program below should print: -// -// the worker finishes -// the end of the coroutine -// after the first coroutine finishes -// caught MyError +///| test { // when supplying an anonymous function // to a higher order function that expects async parameter, // the `async` keyword can be omitted - run_async(fn() { - try { - async_worker(throw_error=false) - println("the worker finishes") + let logger = StringBuilder::new() + fn local_test() { + run_async(() => try { + async_worker(logger~, throw_error=false) + logger.write_string("the worker finishes\n") } catch { - err => println("caught: \{err}") - } - }) - println("after the first coroutine finishes") - run_async(fn() { - try { - async_worker(throw_error=true) - println("the worker finishes") + err => logger.write_string("caught: \{err}\n") + }) + logger.write_string("after the first coroutine finishes\n") + run_async(() => try { + async_worker(logger~, throw_error=true) + logger.write_string("the worker finishes\n") } catch { - err => println("caught: \{err}") - } - }) + err => logger.write_string("caught: \{err}\n") + }) + } + + local_test() + inspect( + logger, + content= + #|the worker finishes + #|the end of the coroutine + #|after the first coroutine finishes + #|caught: MyError + #| + , + ) } // end async example // start async timer example -#external + +///| type JSTimer +///| extern "js" fn js_set_timeout(f : () -> Unit, duration~ : Int) -> JSTimer = #| (f, duration) => setTimeout(f, duration) +///| async fn sleep(duration : Int) -> Unit raise { suspend(fn(resume_ok, _resume_err) { js_set_timeout(duration~, fn() { resume_ok(()) }) |> ignore }) } +///| test { - run_async(fn() { - try { - sleep(500) - println("timer 1 tick") - sleep(1000) - println("timer 1 tick") - sleep(1500) - println("timer 1 tick") - } catch { - _ => panic() - } - }) - run_async(fn() { - try { - sleep(600) - println("timer 2 tick") - sleep(600) - println("timer 2 tick") - sleep(600) - println("timer 2 tick") - } catch { - _ => panic() - } - }) + let logger = StringBuilder::new() + fn println(s : String) { + logger.write_string(s) + logger.write_string("\n") + } + + fn local_test() { + run_async(fn() { + println("start 1") + try { + sleep(500) + println("timer 1 tick") + sleep(1000) + println("timer 1 tick") + sleep(1500) + println("timer 1 tick") + } catch { + _ => panic() + } + }) + run_async(fn() { + println("start 2") + try { + sleep(600) + println("timer 2 tick") + sleep(600) + println("timer 2 tick") + sleep(600) + println("timer 2 tick") + } catch { + _ => panic() + } + }) + } + + local_test() + inspect(logger, content= + #|start 1 + #|start 2 + #| + + + ) } // end async timer example