Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added err outport for Println. #860

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Skipped e2e-test for_with_range_and_if. Adjusted neva new template fo…
…r new println syntax. Updated documentation with new println syntax.
  • Loading branch information
MDH0 committed Feb 6, 2025
commit 678b7c869c18d040c7ae164a43691fb2858289ba
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,19 @@ import { fmt }
def Main(start any) (stop any) {
println fmt.Println<string>
---
:start -> 'Hello, World!' -> println -> :stop
:start -> 'Hello, World!' -> println
[println:res, println:err] -> :stop
}
```

What's happening here:

- `import { fmt }` loads the `fmt` package for printing
- `def Main` defines the main component with input port `start` and output port `stop` of type `any` (it's safe since it's only used as a signal)
- `:start -> 'Hello, World!' -> println -> :stop` defines a connection that sends the string to `println` when program starts and terminates after printing (runtime sends a message to `Main:start` at startup and waits for `Main:stop` to terminate)
- `:start -> 'Hello, World!' -> println` defines a connection that sends the string to `println` when the program starts
- The runtime sends a message to `Main:start` at startup and waits for `Main:stop` to terminate
- `[println:res, println:err] -> :stop` defines a connection that sends either println:res or println:err as the termination signal, depending on which case returned from println.
- As in Go, errors need to be handled. Removing `println:err` from this example would result in a compiler error.

## 🔥 Features

Expand Down
15 changes: 10 additions & 5 deletions docs/book/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ Normal component `Main` with a `println` node (instance of `Println`):
def Main(start any) (stop any) {
Println
---
:start -> { 42 -> println -> :stop }
:start -> { 42 -> println }
[println:res, println:err] -> :stop
}
```

Expand All @@ -65,7 +66,9 @@ def Main(start any) (stop any) {
p1 Println
p2 Println
---
:start -> { 42 -> p1 -> p2 -> :stop }
:start -> { 42 -> p1 }
[p1:res, p1:err] -> p2
[p2:res, p2:err] -> :stop
}
```

Expand Down Expand Up @@ -101,7 +104,7 @@ def App(data any, prod bool) (sig any) {

This not only makes the code more complex but also means we have to initialize both implementations: `ProdLogger` in the test environment and `MockLogger` in the production environment, even though they are not needed in those respective contexts. What if you need to read environment variables to initialize a component? For example, your logger might need to send requests to a third-party service to collect errors. And finally, imagine if it were not a boolean flag but an enum with several possible states. The complexity would increase dramatically.

> As you can see it's possible to write nodes in a single line, separated by comma: `Cond, Logic, Println, Mock`. Don't abuse this style - Nevalang is not about clever one-liners.
> As you can see it's possible to write nodes in a single line, separated by comma: `Cond, Logic, Mock`. Don't abuse this style - Nevalang is not about clever one-liners, as you can see with `Println`.

Let's implement this using dependency injection. First, define an interface:

Expand Down Expand Up @@ -182,9 +185,11 @@ Components can pass type parameters from their interface to node expressions:

```neva
def Bar<T>(data T) (sig any) {
Println<T>
Println<T>, Panic
---
:data -> println -> :sig
:data -> println
println:res -> :sig
println:err -> panic
}
```

Expand Down
4 changes: 2 additions & 2 deletions docs/book/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Tells compiler a component lacks source code implementation and requires a runti

```neva
#extern(println)
pub def Println<T>(data T) (sig T)
pub def Println<T>(data T) (res T, err error)
```

### Overloading
Expand Down Expand Up @@ -36,7 +36,7 @@ def Main(start any) (stop any) {
:start -> lock:sig
greeting:res -> lock:data
lock:data -> println:data
println:res -> :stop
[println:res, println:err] -> :stop
}
```

Expand Down
28 changes: 15 additions & 13 deletions docs/book/networks.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ type Person struct { age int }
def Foo(person Person) (sig any) {
fmt.Println
---
:person -> .age -> println -> :sig
:person -> .age -> println
[println:res, println:err] -> :sig
}
```

Expand Down Expand Up @@ -426,17 +427,17 @@ Here's an example using this feature:

```neva
def Foo(data) (sig) {
Println
Print
---
:data -> println -> :sig
:data -> print -> :sig
}
```

`:data -> println -> :sig` combines port-address on the sender-side and chained connection on the receiver-side. `println -> :stop` is chained to `:data ->`. Here's a desugared version:
`:data -> print -> :sig` combines port-address on the sender-side and chained connection on the receiver-side. `print -> :stop` is chained to `:data ->`. Here's a desugared version:

```neva
:data -> println
println -> :sig
:data -> print
print -> :sig
```

Components don't need matching inport and outport names. Chained connections require one port per side. Both `def Foo(bar) (bar)` and `def Foo (bar) (baz)` are valid.
Expand All @@ -462,20 +463,20 @@ In controlflow programming, instructions execute sequentially. In Nevalang's dat
Let's say we want to print 42 and then terminate.

```neva
42 -> println -> :stop
42 -> print -> :stop
```

Turns out, this program is indeterministic and could give different outputs. The problem is that `42 ->` acts like an emitter sending messages in an infinite loop. Therefore, `42` might reach `println` twice if the program doesn't terminate quickly enough:
Turns out, this program is indeterministic and could give different outputs. The problem is that `42 ->` acts like an emitter sending messages in an infinite loop. Therefore, `42` might reach `print` twice if the program doesn't terminate quickly enough:

1. `42` received and printed by `println`
2. signal sent from `println` to `:stop`
1. `42` received and printed by `print`
2. signal sent from `print` to `:stop`
3. new `42` sent and printed again
4. runtime processed `:stop` signal and terminated the program

To ensure `42` is printed once, synchronize it with `:start` using "defer". Here's the fix:

```neva
:start -> { 42 -> println -> :stop }
:start -> { 42 -> print -> :stop }
```

This syntax sugar inserts a `Lock` node between `:start` and `42`. Here's the desugared version:
Expand All @@ -486,7 +487,7 @@ def Main(start any) (stop any) {
---
:start -> lock:sig
42 -> lock:data
lock:data -> println -> :stop
lock:data -> print -> :stop
}
```

Expand Down Expand Up @@ -705,7 +706,8 @@ def Main() () {
1 -> wrap[0]
2 -> wrap[1]
3 -> wrap[2]
wrap -> println -> :stop
wrap -> println
[println:res, println:err] -> :stop
}
```

Expand Down
3 changes: 2 additions & 1 deletion docs/book/program_structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ import {
def Main(start any) (stop any) {
bar.AddP, Println
---
:start -> { $foo.p -> addP:right -> println -> :stop }
:start -> { $foo.p -> addP:right -> println }
[println:res, println:err] -> :stop
}
```

Expand Down
Loading
Loading