Skip to content

Commit

Permalink
Merge pull request #696 from nevalang/connectionless
Browse files Browse the repository at this point in the history
Connectionless!
  • Loading branch information
emil14 authored Sep 20, 2024
2 parents 4652071 + d2b7f87 commit 4a86048
Show file tree
Hide file tree
Showing 133 changed files with 2,162 additions and 1,080 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ thirdparty
#go package cache
vendor

tmp
tmp
tmp.md
10 changes: 5 additions & 5 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
"version": "0.2.0",
"configurations": [
{
"name": "GET",
"name": "RUN",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/neva",
"cwd": "${workspaceFolder}/examples",
"args": ["get", "github.com/nevalang/x"]
"args": ["run", "select"]
},
{
"name": "RUN",
"name": "GET",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/neva",
"cwd": "${workspaceFolder}",
"args": ["run", "-debug", "examples/select"]
"cwd": "${workspaceFolder}/examples",
"args": ["get", "github.com/nevalang/x"]
},
{
"name": "LSP",
Expand Down
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
"designs",
"designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField",
"designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ",
"dotc",
"feeded",
"Field",
"gqlgen",
"graphql",
"introspection",
"intrprtr",
"irprotosdk",
"nativec",
"pmod",
"preceiver",
"pworkers",
Expand All @@ -26,6 +29,7 @@
"rndv",
"rports",
"vvin",
"wasmc",
"типобезопасности"
],
"workbench.colorCustomizations": {
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ antlr:
tygo:
@tygo generate

# === Release Artifacts ===
# === Release Build ===

# build neva cli for all target platforms
.PHONY: build
Expand Down Expand Up @@ -67,7 +67,7 @@ build-windows-amd64:
build-windows-arm64:
@GOOS=windows GOARCH=arm64 go build -ldflags="-s -w" -o neva-windows-arm64.exe ./cmd/neva

# === Tool Artifacts ===
# === LSP ===

# build neva lsp for all target platforms
.PHONY: build-lsp
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
![Big Header](./assets/header/big.svg "Big header with nevalang logo")

**<p align="center">Flow-Based Programming Language</p>**
**<p align="center">Dataflow Programming Language</p>**

![tests](https://github.com/nevalang/neva/actions/workflows/test.yml/badge.svg?branch=main) ![lint](https://github.com/nevalang/neva/actions/workflows/lint.yml/badge.svg?branch=main)

> ⚠️ Warning: This project is currently under heavy development and is not yet ready for production use.
# Neva
# Nevalang

A general-purpose dataflow programming language with static types and implicit parallelism. Compiles to machine code and Go.

Expand Down Expand Up @@ -37,7 +37,7 @@ curl -sSL https://raw.githubusercontent.com/nevalang/neva/main/scripts/install.s
If your device is connected to a chinese network:

```bash
curl -sSL https://raw.githubusercontent.com/nevalang/neva/main/scripts/install.sh | bash
curl -sSL https://raw.githubusercontent.com/nevalang/neva/main/scripts/cnina/install.sh | bash
```

For Windows (please note there's an WIP [issue](https://github.com/nevalang/neva/issues/499) with Windows Defender, try manual download from releases if installed won't work):
Expand Down
26 changes: 21 additions & 5 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
# Nevalang
# Documentation

Welcome to Nevalang - general purpose dataflow programming language.
Welcome to Nevalang's documentation!

In this language we don't have control-flow. It means we can't call, return, break, continue or goto. There are no functions, no for loops, no variables. Instead we have nodes connected through their input and output ports for message passing. This is what's called dataflow programming.
It describes general purpose dataflow compiled language with static types. Here you'll find motivation behind the language, it's philosophy, differences between dataflow and controlflow paradigms and of course language abstractions and relations between them.

There are no coroutines, channels or mutexes. You write concurrent program just by having parallel connections in your network. If there is machine capacity, code will be executed in parallel. It's called implicit parallelism.
## About this Document

Compiler performs strong static type-checking. Language has interfaces and generics for polymorphic code. You can emit ready to deploy machine code or Go, to integrate with existing codebase.
1. This document describes finite state of the language. Some features (e.g. visual editor) might not be implemented at the time of writing, but they yet important for concept so they will be mentioned
2. It was written as an attempt to create at least something. I needed to dump all the information I collected about the language with years. So it's far from perfect
3. This document doesn't teach you how to write Nevalang programs, because Nevalang is very immature language and it is changins all the time. This is especially true for stdlib components. However, a lot about Nevalang is already clear and will never change. We are talking about more fundamental stuff such as philosophy, abstractions, execution model, etc.

## Table of contents

- [About](./about.md)
- [Motivation](./motivation.md)
- [Paradigm](./paradigm.md)
- [Flow-Based-Programming](./fbp.md)
- [Program Structure](./program_structure.md)
- [Type](./type_entity.md)
- [Constant](./const_entity.md)
- [Interface](./interface_entity.md)
- [Component](./component_entity.md)
- [IO](./component_io.md)
- [Network](./component_net.md)
67 changes: 67 additions & 0 deletions docs/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# About

Nevalang is a general purpose programming language: documentation, compiler, runtime, cli, editor plugins, etc. What kind of language it is?

- Pure Dataflow (FBP-like)
- Compiled (Machine code, Golang, WASM)
- Statically (Strongly) Typed
- Concurrent/Parallel by default
- Hybrid (textual/visual)
- Pure Declarative
- Go Interop (call Neva from Go and vice versa)

We will discover what this means what piece by piece.

## Pure Dataflow

Pure Dataflow means that your program is expressed in terms of dataflow abstractions such as nodes, ports, connections and messages.

There are no variables, no functions, no classes, no objects, no methods, no loops, no ifs, no switches, no exceptions, no mutable state.

Nevalang's dataflow mostly influenced by Flow-Based Programming paradigm but Nevalang is not pure FBP. You can find more info about this in specialized section.

## Compilation

Nevalang uses Go as low-level IR (Intermediate Representation). This means your Nevalang program is firstly compiled to Go and then Go compiler is used to compile it to any target that Go compiler supports, such as machine code or WASM.

This firstly has performance benifits and secondly it's easier to deploy if all you need to ship is a binary, unlike Python or Java where you need interpreter/VM of a specific (correct) version.

There's a temptation to say that Nevalang is as fast as Go but that statement is incorrect because Nevalang is higher abstraction. It has it's own (very-very thin, but still) runtime on top of Go's.

## Strong Static Typing

Nevalang has _strong static structural_ type-system. What does it mean?

- _Strong_ - means there are no implicit type-casts. E.g. `float` will never be implicitly converted to `int` and you will always have to convert (cast) them explicitly, by using special components
- _Static_ - means that types are known at compile time. E.g. - you can (and you must) explicitly define what data type your port is sending or receiving, or what data-type some constant have
- _Structural_ - means _structural sub-typing_ is used. In this case it used for data-types and interfaces/components implementation relation.

If you are confused by something, don't worry. Type-system will be explained in detail on related section.

## Implicit Parallelism

Nevalang supports implicit parallelism. It means that you don't have to (and you can't) access parallelism/concurrency primitives such as mutexes, threads, coroutines, channels, etc. You just create dataflow and everything that can happen in parallel (better word is _concurrently_) - will happen this way. It's good because of 2 things: performance and maintainance. You basically get parallelism for free.

If you want to know how exactly Nevalang executes your program then don't worry, there will be a special section about that. This is just a high level overview of a specific feature.

## Hybrid (Textual and Visual Programming)

Textual and visual programming in Nevalang are combined into single workflow. You can use only text if you e.g. work in environment that doesn't support visuals or you just don't like visual programming. However, Nevalang is and always will be hybrid langauge, which means visual programming is a first class citizen. If some feature cannot be supported in visual programming it might not be added or can be removed. Even language abstractions are done in a way so it's possible to visually represent them.

## Pure Declarative

Nevalang doesn't have a single imperative bit except go-interop (including both writing extensions and maintaining stdlib).

Declarative Programming means you write your program by describing what do you want to get as a result, instead of explaining how to actually do it under the hood. E.g. explicit controlflow instructions like `for`, `break` or `goto` are imperative while things like `[1,2,3].map(double)` are declarative. You are not explaining _how_ to actually `map` stuff, you just say that this is _what_ result should be.

In Nevalang declarative programming is expressed in a way you write your program - you define entities. Computation is expressed in terms of graph that is also declarative. Network show what should happen (e.g. message from X should be moved to Y) and not how (send message to channel, block coroutine, etc).

Of course there are different problems and imperative programming is better for some of them. However, if we have imperative languages, why not have declarative? Besides, there are tons of declarative languages, they are just (mostly) not dataflow. Let's take functional programming paradigm for instance. If we are talking about pure FP then we are automatically talking about pure declarative. Same goes for dataflow - pure dataflow is always pure declarative.

## Go Interop

As being said Nevalang uses Go as low-level IR which opens the door for two-way interop with existing Go codebases. That is, Nevalang has potential to call Go code from neva code and vice versa - call Nevalang from Go.

This is very important thing about Nevalang - it's intended for gradual adaptation among Go developers. Otherwise it would be close to impossible to adapt the language to such a highly competitive market.

However, if you are not Go developer, don't worry. Nevalang won't have interop with your language so you will have to use network for communication. However, it's completely fine to use Nevalang without knowing anything about Go. Nothing requires you Go knowledge to write Nevalang programs. Maybe couple of things might not seem obvious without Go background or as a non-Go developer you couldn't appreciate some decisions. That doesn't matter. Nevalang is separate language. Nevalang to Go is more what C to assembler is, rather than TypeScript to JavaScript.
63 changes: 63 additions & 0 deletions docs/component_entity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Component Entity

Component always has signature (basically embedded _interface_) and optional _compiler directives_, _nodes_ and _network_. There are two kinds of components: _normal_ and _native_ ones.

## Main Component

Executable package must have _component_ called `Main`. This component must follow specific set of rules:

- Must be _normal_
- Must be _private_
- Must have exactly 1 inport `start`
- Must have exactly one outport `stop`
- Both ports must have type `any`
- Must have no _abstract nodes_

Main component must have both node and network.jkl

## Native Components

Component without implementation (without nodes and network) must use `#extern` directive to refer to _runtime function_. Such component called _native component_. Native components normally only exist inside `std` module, but there should be no forced restriction for that.

## Normal Component

Normal component is implemented in source code i.e. it has not only interface but also nodes and network, or at least just network. Normal components must never use `#extern` directive.

## Nodes

Nodes are things that have inports and outports that can be connected in network. There's two kinds of nodes:

1. IO Nodes
2. Computational nodes

IO nodes are created implicitly. Every component have one `in` and one `out` node. Node `in` has outports corresponding to component's interface's inports. And vice versa - `out` node has inports corresponding to component interface's inports.

Computational nodes are nodes that are instantiated from entities - components or interfaces. There's 2 types of computational nodes: concrete and abstract. Nodes that are instantiated from components are _concrete nodes_ and those that instantiated from interfaces are _abstract nodes_.

Interfaces and component's interfaces can have type parameters. In this case node must specify type arguments in instantiation expression.

## Dependency Injection (DI)

Normal component can have _abstract node_ that is instantiated from an interface instead of a component. Such components with abstract nodes needs what's called dependency injection.

I.e. if a component has dependency node `n` instantiated with interface `I` one must provide concrete component that _implements_ this interface.

Dependency Injection can be infinitely nested. Component `Main` cannot use dependency injection.

## Component and Interface Compatability (Implementation)

Component _implements_ interface (is _compatible_ with it) if type paremeters, inports and outports are compatible.

Type parameters are compatible if their count, order and names are equal. Constraints of component's type parameters must be compatible with the constraints of the corresponding interface's type parameter's constraints.

Component's inports are compatible with the interface's if:

1. Amount is exactly equal
2. They have exactly the same names and _kind_ (array or single)
3. Their types are _compatible_ (are _subtypes_ of) with the corresponding interface's inports

Outports of a component are compatible with the interface's if:

1. Amount is equal or more (this is only difference with inports)
2. Exactly the same names and _kind_
3. Their types are _compatible_
133 changes: 0 additions & 133 deletions docs/connections.md

This file was deleted.

Loading

0 comments on commit 4a86048

Please sign in to comment.