Skip to content

Commit

Permalink
wip: docs
Browse files Browse the repository at this point in the history
  • Loading branch information
emil14 committed Feb 10, 2024
1 parent 72a71f4 commit 379bc9c
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 171 deletions.
43 changes: 43 additions & 0 deletions content/docs/faq/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,46 @@ type struct Point {
```

So why you define struct/map literals with colons and commas? Well, the answer is that this way it almost valid JSON just like in Python in JS. Except (optional) trailing comma (afterall JSON compatibility is not our goal). This also good to distinguish easily between type and const expressions. Finally this is how we do in JS/TS, Python and Go.

## Can't we have shorter syntax for connections?

There was an idea to have sugar for connections where sender and receiver has same ports

```
in:sig -> scanner:sig
scanner:data -> logger:data
logger:sig -> out:sig
```

To look like this

```neva
in -> scanner
scanner -> logger
logger -> out
```

The problem is that this

```neva
foo:x -> bar:x
foo:y -> bar:y
```

becomes this

```neva
foo -> bar
foo -> bar
```

So it's impossible to tell what port we mean. This could make sense only for components with single in/out port but there's not a lot of such components.

We could in theory limit this syntax to a form where you can have only one `foo ->` connection but that would lead to inconsistency

```
foo -> bar
foo:y -> bar:y
```

And the last but not least. How am I suppose to figure out what port `foo -> bar` these two use? I have to look into their interfaces which is not cool.
9 changes: 5 additions & 4 deletions content/docs/style-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ The Nevalang style guide outlined in this document sets the standards for organi
A general rule is that an entity's name should inherit context from its parent scope, eliminating the need for redundant context duplication

- **Packages**: Use `lower_snake_case` for package names up to 3 words.
- **Entities**: Use descriptive (up to 3 words) names `CamelCase` for types, interfaces and components, and for constants use `lowerCamelCase`.
- **Files**: Exactly the same as for files
- **Entities**: Use descriptive `CamelCase` names for types, interfaces and components. For constants use `lowerCamelCase`.
- **Components**: Use actionable noun with "er" ending if possible.
- **Interfaces**: Use same rules as for components but add `I` prefix.
- **Ports**: Keep port names short, ideally under five characters, and use intuitive abbreviations.
- **Nodes**: Name nodes similarly to their components, in lowerCamelCase, and distinguish multiple instances of the same component with specific identifiers.
- **Interfaces**: Exactly the same as for components but add `I` prefix.
- **Ports**: Use short `lowerCamelCase` up to 5 letters.
- **Nodes**: Name nodes similarly to their components/interfaces, but in `lowerCamelCase`. Distinguish multiple instances of the same component with specific identifiers.
8 changes: 8 additions & 0 deletions content/docs/tutorial/02/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ component Main(start any) (stop any) {
}
```

Now make sure that you're in the `nevalang_test` directory and run `neva run`. Terminal should block until you type something. Type anything e.g. "how are you?". If everything is okay you should see this output:

```bash
> how are you?
```

The program printer what you've entered and quit. That's all it does.

## Packages, Std Module, Builtin Package

Let's pay attention to this line:
Expand Down
20 changes: 10 additions & 10 deletions content/docs/tutorial/03/index.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
---

title: Hello World
weight: 3

---

Isn't it odd to reach the "Hello, World!" moment only in the third lesson - the starting point that most tutorials begin with? Well, many peculiarities await us in Nevalang. However, we hope that by the end of this tutorial, they will no longer seem like oddities. Who knows, you might even start thinking, "Could it have been any other way?". Of course, we could have started with "Hello, World!" too, without delving into the intricate details of how every little thing works, but our goal, once again, is to achieve a deep understanding of how Nevalang is structured. And, actually, "Hello, World!" is not as straightforward as it seems.

Go to your `nevalang_test` directory and replace content of the `main.neva` with this:

```neva
const greeting string = 'Hello, World!'
Expand All @@ -26,6 +30,8 @@ component Main(start any) (stop any) {

You might be thinking right now - "This is the most verbose 'Hello, World!' I've ever seen!" And, quite likely, you are correct. But don't rush to close the page; by dissecting this example, we'll learn how to write code more concisely. Without going through this example and jumping straight to the shorter version, we would never understand how the short version actually works.

Now run `neva run` and if everything is fine you should be able to see `Hello, World!` in the terminal.

## Entities, Constants

Have a look at this line:
Expand All @@ -38,9 +44,7 @@ Let's take a step back and ask ourselves, what exactly are packages? Well, they

Declaring a constant begins with the keyword `const`, followed by an arbitrary name. Incidentally, an entity's name must be unique within its package. This means, for example, that a constant could not be named `Main` in our case, because there is already a component with that name. After the name comes the type expression; in this case, we simply refer to the familiar type `string`. Then comes the `=` symbol, and finally, the value of the constant - a so-called _literal_, in our case the string `'Hello, World!'`

```
const <name> <type_expr> = <literal_expr>
```
const <name> <type_expr> = <literal_expr>

So, what exactly is a constant? It's an entity that describes a _static message_ - a message whose value is known at the time of writing the program (at _compile time_) and is directly present in the program's source code. The value of a constant must be explicitly set; it cannot be computed in any way. The values of constants are _immutable_ - if a constant describes the string message `"Hello, world!"` then it will remain so throughout the program. It's impossible for a constant to change in any way. Note that in Nevalang, there are no variables and, consequently, no mutable state.

Expand All @@ -55,9 +59,7 @@ emitter Emitter<string>

The second line should be clear to us - a `greeting` node that is an instance of the `Emitter` component _parameterized_ with `string`. But what about the first line?

```
#bind(greeting)
```
#bind(greeting)

This is what's known as a _directive_ - a special instruction for the compiler. There are several directives, and each tells the compiler some information on how to correctly compile the program. The syntax for any directive is as follows:

Expand All @@ -69,10 +71,8 @@ This is what's known as a _directive_ - a special instruction for the compiler.

To understand what the `#bind` directive does, let's look into the standard library's code, at the definition of the Emitter component (let's ignore `pub` keyword once again):

```
#extern(Emitter)
pub Emitter<T>() (msg T)
```
#extern(Emitter)
pub Emitter<T>() (msg T)

Firstly, the Emitter has no input ports! This is only possible in the standard library. The compiler will not allow us to do this ourselves - any component outside the standard library must have at least one input and one output port. Anyway, the Emitter is the only component without input ports. We need at least one such component, and soon we'll understand why. Let's move on to the `#extern` directive:

Expand Down
12 changes: 8 additions & 4 deletions content/docs/tutorial/04/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ component Main(start any) (stop any) {
}
```

And it works just the same. What you're now observing is _syntactic sugar_ called _const senders_. In a component's network, as a sender, you can specify not only a port address but also refer to a constant. This is done using the `$` symbol. Naturally, the constant must be within scope - declared in this package or imported.
And it works just the same. What you're now observing is *syntactic sugar* called *const senders*. In a component's network, as a sender, you can specify not only a port address but also refer to a constant. This is done using the `$` symbol. Naturally, the constant must be within scope - declared in this package or imported.

Seeing `$greeting`, the compiler understands that it needs to create a node named "greeting", instantiate it with the `builtin.Emitter` component, parameterize it with the type of the corresponding constant (in this case, `string`), and bind the constant through the `#bind` directive. In other words, do everything that we manually did in the previous lesson.

Make sure that everything is correct by running `neva run` in `nevalang_test` directory. You should again see `Hello, World!` output.

## Then Connections

Alright, we've shortened the code by two lines. But it seems this is still the longest hello world in the world, or at least one of them. It appears that the main complexity stems from the necessity to use blockers. And if there are multiple constants, does that mean a blocker is needed for each? And what if the conditions are nested (X must happen after Y, and Z after both X AND Y)? No worries, we have a solution for this. Introduce another form of syntactic sugar called _then connections_.
Alright, we've shortened the code by two lines. But it seems this is still the longest hello world in the world, or at least one of them. It appears that the main complexity stems from the necessity to use blockers. And if there are multiple constants, does that mean a blocker is needed for each? And what if the conditions are nested (X must happen after Y, and Z after both X AND Y)? No worries, we have a solution for this. Introduce another form of syntactic sugar called *then connections*.

```neva
const greeting string = 'Hello, World!'
Expand Down Expand Up @@ -85,7 +87,7 @@ These two variants - with blockers and with then connections - are absolutely id

## Literal Senders

Indeed, that's much better! Yet, our Hello World can't exactly be called succinct. It seems that in conventional languages, we don't create constants just to pass them once. It would be great to send the string directly to print, without making it a separate entity. Well, specifically for this, Nevalang has another form of syntactic sugar, the last one we'll look at in this lesson, and it's called _literal senders_.
Indeed, that's much better! Yet, our Hello World can't exactly be called succinct. It seems that in conventional languages, we don't create constants just to pass them once. It would be great to send the string directly to print, without making it a separate entity. Well, specifically for this, Nevalang has another form of syntactic sugar, the last one we'll look at in this lesson, and it's called *literal senders*.

```neva
component Main(start any) (stop any) {
Expand Down Expand Up @@ -117,6 +119,8 @@ in:start -> ('Hello, World!' -> printer:data)

For the compiler, this variant is only slightly more complex than the one with const senders. It will unfold this into the verbose primitive form we started with, where we have an emitter and `#bind`. This time, however, it will also create a constant because it needs something to pass in the bind as a configuration message. The name of the constant will be generated automatically.

Finally run `neva run` again and check that our `Hello, World!` is still there.

## Putting It All Together

We've gone from this:
Expand Down Expand Up @@ -154,4 +158,4 @@ component Main(start any) (stop any) {

Cutting down the amount of code by more than half - not bad!

And you deserve a medal for persistence. Unfortunately, the medals have run out, but do you know what hasn't? Lessons in Nevalang! Onward to new, even more thrilling adventures in the world of flow-based programming!
And you deserve a medal for persistence. Unfortunately, the medals have run out, but do you know what hasn't? Lessons in Nevalang! Onward to new, even more thrilling adventures in the world of flow-based programming!
154 changes: 1 addition & 153 deletions content/docs/tutorial/05/index.md
Original file line number Diff line number Diff line change
@@ -1,157 +1,5 @@
---
title: Better Hello World
title: Logger
weight: 5
---

## Const Senders

We promised that "hello world" could be simplified. After all, if writing such a basic program is so complex, what does real programming look like? Fear not, our promise will be fulfilled. Let's start simplifying immediately, beginning with eliminating the explicit mention of the emitter.

We remove the `emitter` node along with its directive:

```neva
#bind(greeting)
emitter Emitter<string>
```

Then we modify this connection:

```neva
emitter:msg -> blocker:data
```

This way:

```neva
$greeting -> blocker:data
```

Voilà, our program is now two lines shorter:

```neva
const greeting string = 'Hello, World!'
component Main(start any) (stop any) {
nodes {
printer Printer<string>
blocker Blocker<string>
}
net {
in:start -> blocker:sig
$greeting -> blocker:data
blocker:data -> printer:msg
printer:msg -> out:stop
}
}
```

And it works just the same. What you're now observing is _syntactic sugar_ called _const senders_. In a component's network, as a sender, you can specify not only a port address but also refer to a constant. This is done using the `$` symbol. Naturally, the constant must be within scope - declared in this package or imported.

Seeing `$greeting`, the compiler understands that it needs to create a node named "greeting", instantiate it with the `builtin.Emitter` component, parameterize it with the type of the corresponding constant (in this case, `string`), and bind the constant through the `#bind` directive. In other words, do everything that we manually did in the previous lesson.

## Then Connections

Alright, we've shortened the code by two lines. But it seems this is still the longest hello world in the world, or at least one of them. It appears that the main complexity stems from the necessity to use blockers. And if there are multiple constants, does that mean a blocker is needed for each? And what if the conditions are nested (X must happen after Y, and Z after both X AND Y)? No worries, we have a solution for this. Introduce another form of syntactic sugar called _then connections_.

```neva
const greeting string = 'Hello, World!'
component Main(start any) (stop any) {
nodes { printer Printer<string> }
net {
in:start -> ($greeting -> printer:msg)
printer:msg -> out:stop
}
}
```

First, the `blocker Blocker<string>` node has disappeared, and second, three connections were replaced by one. It used to be:

```neva
in:start -> blocker:sig
$greeting -> blocker:data
blocker:data -> printer:msg
```

Now, it's just:

```neva
in:start -> ($greeting -> printer:msg)
```

This "then connection" syntax `... -> (...)` indicates that the `$greeting` can reach `printer:msg` only after the `in:start` signal is sent, ensuring that the sequence of events is maintained without the need for explicit blockers.

These two variants - with blockers and with then connections - are absolutely identical in function. The "then connections" variant actually unfolds into the blocker variant during compilation.

## Literal Senders

Indeed, that's much better! Yet, our Hello World can't exactly be called succinct. It seems that in conventional languages, we don't create constants just to pass them once. It would be great to send the string directly to print, without making it a separate entity. Well, specifically for this, Nevalang has another form of syntactic sugar, the last one we'll look at in this lesson, and it's called _literal senders_.

```neva
component Main(start any) (stop any) {
nodes { printer Printer<string> }
net {
in:start -> ('Hello, World!' -> printer:data)
printer:sig -> out:stop
}
}
```

We've removed the line:

```neva
const greeting string = 'Hello, World!'
```

And changed:

```neva
in:start -> ($greeting -> printer:msg)
```

to:

```neva
in:start -> ('Hello, World!' -> printer:data)
```

For the compiler, this variant is only slightly more complex than the one with const senders. It will unfold this into the verbose primitive form we started with, where we have an emitter and `#bind`. This time, however, it will also create a constant because it needs something to pass in the bind as a configuration message. The name of the constant will be generated automatically.

## Putting It All Together

We've gone from this:

```neva
const greeting string = 'Hello, World!'
component Main(start any) (stop any) {
nodes {
#bind(greeting)
emitter Emitter<string>
blocker Blocker<string>
printer Printer<string>
}
net {
in:start -> blocker:sig
emitter:msg -> blocker:data
blocker:data -> printer:msg
printer:msg -> out:stop
}
}
```

To this:

```neva
component Main(start any) (stop any) {
nodes { printer Printer<string> }
net {
in:start -> ('Hello, World!' -> printer:data)
printer:sig -> out:stop
}
}
```

Cutting down the amount of code by more than half - not bad!

And you deserve a medal for persistence. Unfortunately, the medals have run out, but do you know what hasn't? Lessons in Nevalang! Onward to new, even more thrilling adventures in the world of flow-based programming!

0 comments on commit 379bc9c

Please sign in to comment.