Repository files navigation

The Void Programming Language

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler.

Disclaimer

Void has been recently changed paradigm, and now is willing to enter the world of native code. This opens up a new era of being able to write blazingly fast applications. Void aims to offer an easy and reliable syntax, that ensures that you can still write safe and simple code, without having to use a heavy runtime. The old compiler has been deprecated, and a new one is being developed at.

What is Void?

Void is an LLVM-based native programming language that is designed for developers to be able to create clean, fast and stable multi-platform applications with the power that native provides.

It has a relatively easy syntax, that follows most of the already existing code conventions.

Void features many utilities that enchant the experience of coding, such as object destruction, variable redeclaration, multi-returning, and much more.

Why Void?

Void compiles to LLVM IR, therefore it allows the application with the same exact code, to be compiled to all platforms, whilst forward and backward compatibility is guaranteed. This also makes it easy for Void to communicate with libraries written in other programming languages. It has many built-in designs, which makes it much easier to develop enterprise-grade applications.

How to use Void?

Void is designed to satisfy the needs of multiple development paradigms. Unlike many production-oriented languages, Void does not force object-oriented programming on you, however it is recommended to use in most cases.

Want to know more?

Join the discord server

Want to help the project?

Donate to the Void Project

Examples

Below are few examples showcasing what Void provides to enchant programming experience. Warning: This documentation was made way before, that Void had finally given a complete design, therefore expect some functionalities to be somewhat different when trying Void. Besides, some features may be not yet present in the following lines.

A Hello World application

voidmain() {
println("Hello, World")
}

Variable declaration

You can declare variables using the "let" keyword. It automatically detects the type of the variable, but type can be also declared explicitly.
let value = 100
In case of explicit type definition:
floatvalue = 3.5
As you may notice, semicolons are not required. The compiler automatically places them after the end of lines, when it is required. This allows the chaining syntax, unlike many in case of many languages.
database
.fetchUser("username")
.transform(User::from)
.getOrThrow(err)
In case of putting multiple statements in one line, you can separate them with putting a semicolon between them.
intf = 0; floatf = 10.0
Although it is possible in Void, it is considered a bad practice of coding, as it can possibly overcomplicate a line of code. You may put only one statement in a line.

Variable redeclaration

Normally languages doesn't let you declare variables twice, but as for some other languages such as Void, lets you to do that.
This is becuase in many cases you need to swap between variable types for a value, and having too much variables for a single value, or having long lines of type conversion is both considered a bad practice.
// bad codeintbalance = database.getUser("username").balancestringbalanceStr = $"{balance} USD"// also bad codestringbalance = string.from(database.getUser("username").balance) + " USD"
// simplified code with variable redeclarationlet balance = database.getUser("username").balancelet balance = $"{balance}USD"

Multi-returning

Void lets you have methods return multiple variables at once.
This allows you to have more simple codes, without the need of making structs holding multiple values for return.
// overcomplicated codestructHttpResponse{publicintcodepublicstringmessage}HttpResponsefetchURL(stringurl){returnnewHttpResponse{code:404,message:"Not found."}}// simplified code(int,string)fetchURL(stringurl){return(404,"Not found.")}
In the last example, a tuple is returned. A tuple can hold values of any length and any type. To access these values you can use a feature called tuple destruction.
let(code,message)=fetchURL("google.com")println($"Webserver responded with {code} status code")
You can also access these values without destructing the tuple. You need to specify the index of the tuple you want to retrieve.
let response = fetchURL("google.com")let code = response.0let message = response.1
Void allows you to name tuple members. Member names are specified inside the method return type declaration.
(booleansuccess,stringtoken)authenticate(Stringusername,stringpassword){return(true,"Authenticated.")}
In this case, you can access these values by their names.
letresult=authenticate("admin","12345")if(result.success){println("Authenticated.")println($"Token: {result.token}")}else{println("Invalid credentials.")}

Primitive types

Void features a wide variety of primitive types.
byte, short, int, long, float, double
And their unsigned version.
ubyte, ushort, uint, ulong, ufloat, udouble

Simplified number constants

You can specify the types of numbers in the number constant.
let byteVal = 30Blet shortVal = 150Slet intVal = 540// you don't need to put 'I' prefix, as non-decimal numbers are integers by default
let longVal = 120000Llet floatVal = 3.5Flet doubleVal = 40D

Class types

The most high-level one of types is the class type.
It has the capatibility of holding methods, implementations, fileds, and much more.
classCar {
stringtypeintspeedvoidmove() {
println("Moving...")
}
}
In case of the need of classes, which are only for the purpose of holding values, you can use structs.
Structs are simplified classes with the purpose of holding values.
structPoint{
int x
int y
}let point = new Point{x:2,y:3}
// you can use the simplified initializator, if Void knows the type of the structPointpoint = { x: -7, y: 0}
voiddrawPoint(Pointpoint) { /* do something */ }
drawPoint({ x: 30, y: 40 })
You can create classes which only show you what methods they have, but they don't have an actual implementations.
interfaceCar {
voidmove()
} 
The methods are implemented by a class.
classFerrariimplementsCar {
@Overridevoidmove() {
println("Vrooooom...")
}
}
You can have classes with constant members.
enumMimeType {
PLAIN_TEXT("text/plain"),
IMAGE("image/png"),
VIDEO("video/mp4")
stringdata;
MimeType(stringdata) {
this.data = data;
}
}
You can decorate classes, fields, methods or code blocks with annotations.
@interface Subscribe {
stringevent
}
@Subscribe(event = "playerJoin")
voidonPlayerJoin() {
println("A player has joined the game.");
}

Access modifiers

Void features two types of access modifier declarations.
You can specify access modifiers separately for each methods and fields.
However this is not a good practise, as you are making method declarations much longer by having to put these manually everywhere.
publicstaticvoidfoo(){println("bar")}structPoint{publicintx,y}
Alternatively, you can use "modifier blocks", which allows you to set the visibility modifiers of a section of code.
classEntity {
public:int posX, posY
int entityId
private:float health
float stamina
}
Visibilitymodifiersarepublic, protectedandprivate.

Object destruction

You can destruct object members in order to make them more accessible in the code.
structPoint{
int x, y
}
letpoint=newPoint{x: 10,y: -2}let{ x, y }=pointprintln($"Point {x}, {y}")// you don't have to deconstruct all the variables of an objectlet{ x }=point

Lambda objects

You can create anonymus functions inside the code. These are also known as lambdas.
let foo = |int x| println(x)
foo(123)
interfaceCallback {
voidhandle(intx, inty)
}
voidbar(Callbackc) {
c.handle(2, 3)
}
bar(|x, y| println(x * y))

Default values

Using the default(Type) function, you can retrieve the default value of the given type.
letnumber=default(int)// 0letstate=default(bool)// falseletcar=default(Car)// null
Void features a system, which allows to create custom default values for your own class types.
You should put a "default" modifier before the class declaration, and a default() method must be declared as well.
defaultclassCredentials {
stringusername, passworddefault() {
return {
username: "admin",
password: "12345"
}
}
}
// initialize credentials to the default value let credentials = default(Credentials)// { username: "admin", password: "12345" }// you can use the default function without an explicit type, if the variable's type is defined explicitly
Credentials credentials = default// a simplfied syntax is also available
default Credentials credentials

Null-conditional operators

Null-conditional operators provide null-safe access to members.
A '?' mark is placed before the instruction to make it null-conditional.
let profile = database.find("user")// the profile's balance is retrieved if the profile is not null, otherwise a default value is required
println(profile?.balance ?? 0)let person = getSomePerson()// might be null// the "walk" method is not invoked if "person" is null
person?.walk()// you can use null-conditional operators on nested members as well
person?.entity?.motion?.move()

Static variables

By default, variables live only in the scope of a method. Static variables however provide a way to statically store data in the method,
that is accessible for further method calls.
intgetIncrementId(){staticletcounter=0returncounter++}getIncrementId()// returns 0getIncrementId()// returns 1getIncrementId()// returns 2

Enchanted switch

In Void you can use the old switch syntax.
switch (status) {
case200:
println("ok")
breakcase400:
println("error")
breakdefault:
println("unrecognized")
}
However in most cases, this old design forces us to write a lot of unnecessary code, such as always having to write "break" after each cases.
Void's echanted switch allows you to simplify switch blocks and use them as direct expressions.
enumStatus {
SUCCESS,
FAILED,
UNKNOWN
}
switch (code) {
200 -> println("success")
400 -> println("failed")
else -> println("unknown")
}
// you can merge cases as wellswitch (status) {
FAILED|UNKNOWN -> println("Unable to authenticate")
SUCCESS -> println("Authenticated")
}

Blocks as expressions

You can use code blocks as expressions, including loops and switches.
letstatus=switch(code){200->SUCCESS400->FAILEDelse->UNKNOWN}
StatusgetStatus(intcode) = switch (code) {
caseSUCCESS -> 200caseFAILED -> 400else -> 0
}
intcode = getStatus(SUCCESS)
// code is now 200

Method pre-processing

Void allows you to merge an instruction with the return keyword. Therefore you don't need two extra lines to do a negated method guard.
// previously you had to use two lines of code to returnvoidhandleCommand(Playerp,stringcommand){if(!p.hasPermission("use")){p.sendMessage("no perms")
return
}// handle the command}
// Void's syntax allows you to merge these two lines togetherif (!p.hasPermission("use"))
returnp.sendMessage("no perms")
// note that handleCommand returns void, so there are no conflicts returning something.// the return value of p.sendMessage (assuming it has one) is ignored

Method post-processing

In Void, you can queue post tasks for the method, that are going to be executed, when the method returns.
This can be used to prevent duplicating instructions whenever returning.
// prevously you had to do something like this// as you can see you need to call guard.unlock() 3 timesvoidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()if(myObject.someError){guard.unlock()return}if(myObject.someOtherError){guard.unlock()return}myObject.doSomething()guard.unlock()}
In Void, you can replace these duplicates using the "defer" keyword. Deferred instructions will be executed whenever the method returns.
voidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()deferguard.unlock()if(myObject.someError)returnif(myObject.someOtherError)returnmyObject.doSomething()}

Conditional variables

Conditional variables are local variables that only exist in the scope of the condition. This prevents keeping unused object in the memory, and unnecessary variable name reservation.
// previously you had to code something like thisletcreated=createFolder("myFolder")if(!created)returnerror
// Void allows you to simplify this code the following wayif(letcreated=createFolder("folder");!created)returnerror
When checking the isntance of objects, in older code practices, you had to first check the instance, then manually cast the value to the new type.
if(animalisParrot){letparrot=(Parrot)animalparrot.fly()}
However this code requires us to write an extra line of code and manually cast the value.
if(animal is Parrot parrot){
parrot.fly()}

Labels

You can name certain parts of code in order to allow jumps in scopes and nested loops.
// you may label your outer loop in order to allow "break" or "continue" on it.
myLoop:for(let i in1..10){for(let j in1..10){println($"{i}{j}")if(j == 5){break myLoop
}}}
// you can put labels to non-blocks as well// this example shows a loop jump to a section
hello:
doSomething()
if (condition)
goto hello

Multithreading

Void features an async value retrieval system. In other programming languages, you might have seen these ad promises, tasks or futures.
Future<User>getUser(){returnFuture.completed(myUser)}letuser=getUser().get()
Although Void lets you to explicitly declare future types, you should rather use the async/await syntax.
This automatically wraps the return type to be a Future, and calls get().
asyncUsergetUser(){returnmyUser}letuser=awaitgetUser()
You can create threads as well and have full control over them.
letthread=newThread(||println("hello")).setName("my-thread").setPriority(0)thread.start()

JSON in code

Void has a built-in JSON serializer, which allows you to directly map Void objects to JSON and vice versa.
// object to stringlet entity = @Json{entityId:100,meta:{
health:20,food:10,stamina:3},position:{x:200,y:4
z: -55}}let json = Json.serialize(entity)
println($"Data:{json}")// string to object
let data = "{ \"name\": \"admin\", \"userId\": 12345 }"
struct User{
string name
int userId
}
let user = Json.deserialize<User>(data)println($"Welcome,{user.name}")

HTML in code

Void lets you have HTML code inside source code. By default, tags are mapped with Void's built-in tag system, however this can be extended.
letpage= @Html{<divclass="container"><h1>Hello, World</h1></div>}

Bytecode in code

Void features direct bytecode instructions to be placed inside the source code.
int getSomeMagicValue(){let magic = 30
@Bytecodeunsafe{
ipush 100// push 100 to the stack
istore magic // store the value on the stack in the "magic" variable }return magic
}// the value of "test" will be 100let test = getSomeMagicValue()

Interaction with native code

Void allows you to call native library methods. It also has a framework that makes it possible to interace with Void from native context.
// get the implementation of the method from native code@Link("library.dll")
publicnativeintmultiply(inta, intb)
// call the native implementation from Void contextletresult = multiply(2, 6)
println($"Result: {result}")
// you can also link native methods of different signature@Link("library.dll")
@NativeTarget("bar")
nativevoidfoo(@NativeParam("int") inta)
// in case of having multiple native methods in a class, you should annotate the class instead@Link("library.dll")
classMyNativeImplementation {
nativeintfoo()
nativevoidbar(floatf)
@NativeTarget("baz")
nativevoidsomething()
}

Dynamic native implementation loading

letlibrary=newNativeLibrary().target(typeof(object))library.load()

Creating an HTTP server

Void's built-in http module allows you to create efficient web servers with the express-js design.
voidmain(){// create a new server instance which will take care of handling the request routesletserver=newHttpServer()// create a GET request handler for the "/" routeserver.get("/",|req,res|// respond to the request with a plain text messageres.send("Hello, World"))// start the web server and listen on port 80server.listen(80)}

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

The Void Programming Language

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler.

Disclaimer

Void has been recently changed paradigm, and now is willing to enter the world of native code. This opens up a new era of being able to write blazingly fast applications. Void aims to offer an easy and reliable syntax, that ensures that you can still write safe and simple code, without having to use a heavy runtime. The old compiler has been deprecated, and a new one is being developed at.

What is Void?

Void is an LLVM-based native programming language that is designed for developers to be able to create clean, fast and stable multi-platform applications with the power that native provides.

It has a relatively easy syntax, that follows most of the already existing code conventions.

Void features many utilities that enchant the experience of coding, such as object destruction, variable redeclaration, multi-returning, and much more.

Why Void?

Void compiles to LLVM IR, therefore it allows the application with the same exact code, to be compiled to all platforms, whilst forward and backward compatibility is guaranteed. This also makes it easy for Void to communicate with libraries written in other programming languages. It has many built-in designs, which makes it much easier to develop enterprise-grade applications.

How to use Void?

Void is designed to satisfy the needs of multiple development paradigms. Unlike many production-oriented languages, Void does not force object-oriented programming on you, however it is recommended to use in most cases.

Want to know more?

Join the discord server

Want to help the project?

Donate to the Void Project

Examples

Below are few examples showcasing what Void provides to enchant programming experience. Warning: This documentation was made way before, that Void had finally given a complete design, therefore expect some functionalities to be somewhat different when trying Void. Besides, some features may be not yet present in the following lines.

A Hello World application

voidmain() {
println("Hello, World")
}

Variable declaration

You can declare variables using the "let" keyword. It automatically detects the type of the variable, but type can be also declared explicitly.
let value = 100
In case of explicit type definition:
floatvalue = 3.5
As you may notice, semicolons are not required. The compiler automatically places them after the end of lines, when it is required. This allows the chaining syntax, unlike many in case of many languages.
database
.fetchUser("username")
.transform(User::from)
.getOrThrow(err)
In case of putting multiple statements in one line, you can separate them with putting a semicolon between them.
intf = 0; floatf = 10.0
Although it is possible in Void, it is considered a bad practice of coding, as it can possibly overcomplicate a line of code. You may put only one statement in a line.

Variable redeclaration

Normally languages doesn't let you declare variables twice, but as for some other languages such as Void, lets you to do that.
This is becuase in many cases you need to swap between variable types for a value, and having too much variables for a single value, or having long lines of type conversion is both considered a bad practice.
// bad codeintbalance = database.getUser("username").balancestringbalanceStr = $"{balance} USD"// also bad codestringbalance = string.from(database.getUser("username").balance) + " USD"
// simplified code with variable redeclarationlet balance = database.getUser("username").balancelet balance = $"{balance}USD"

Multi-returning

Void lets you have methods return multiple variables at once.
This allows you to have more simple codes, without the need of making structs holding multiple values for return.
// overcomplicated codestructHttpResponse{publicintcodepublicstringmessage}HttpResponsefetchURL(stringurl){returnnewHttpResponse{code:404,message:"Not found."}}// simplified code(int,string)fetchURL(stringurl){return(404,"Not found.")}
In the last example, a tuple is returned. A tuple can hold values of any length and any type. To access these values you can use a feature called tuple destruction.
let(code,message)=fetchURL("google.com")println($"Webserver responded with {code} status code")
You can also access these values without destructing the tuple. You need to specify the index of the tuple you want to retrieve.
let response = fetchURL("google.com")let code = response.0let message = response.1
Void allows you to name tuple members. Member names are specified inside the method return type declaration.
(booleansuccess,stringtoken)authenticate(Stringusername,stringpassword){return(true,"Authenticated.")}
In this case, you can access these values by their names.
letresult=authenticate("admin","12345")if(result.success){println("Authenticated.")println($"Token: {result.token}")}else{println("Invalid credentials.")}

Primitive types

Void features a wide variety of primitive types.
byte, short, int, long, float, double
And their unsigned version.
ubyte, ushort, uint, ulong, ufloat, udouble

Simplified number constants

You can specify the types of numbers in the number constant.
let byteVal = 30Blet shortVal = 150Slet intVal = 540// you don't need to put 'I' prefix, as non-decimal numbers are integers by default
let longVal = 120000Llet floatVal = 3.5Flet doubleVal = 40D

Class types

The most high-level one of types is the class type.
It has the capatibility of holding methods, implementations, fileds, and much more.
classCar {
stringtypeintspeedvoidmove() {
println("Moving...")
}
}
In case of the need of classes, which are only for the purpose of holding values, you can use structs.
Structs are simplified classes with the purpose of holding values.
structPoint{
int x
int y
}let point = new Point{x:2,y:3}
// you can use the simplified initializator, if Void knows the type of the structPointpoint = { x: -7, y: 0}
voiddrawPoint(Pointpoint) { /* do something */ }
drawPoint({ x: 30, y: 40 })
You can create classes which only show you what methods they have, but they don't have an actual implementations.
interfaceCar {
voidmove()
} 
The methods are implemented by a class.
classFerrariimplementsCar {
@Overridevoidmove() {
println("Vrooooom...")
}
}
You can have classes with constant members.
enumMimeType {
PLAIN_TEXT("text/plain"),
IMAGE("image/png"),
VIDEO("video/mp4")
stringdata;
MimeType(stringdata) {
this.data = data;
}
}
You can decorate classes, fields, methods or code blocks with annotations.
@interface Subscribe {
stringevent
}
@Subscribe(event = "playerJoin")
voidonPlayerJoin() {
println("A player has joined the game.");
}

Access modifiers

Void features two types of access modifier declarations.
You can specify access modifiers separately for each methods and fields.
However this is not a good practise, as you are making method declarations much longer by having to put these manually everywhere.
publicstaticvoidfoo(){println("bar")}structPoint{publicintx,y}
Alternatively, you can use "modifier blocks", which allows you to set the visibility modifiers of a section of code.
classEntity {
public:int posX, posY
int entityId
private:float health
float stamina
}
Visibilitymodifiersarepublic, protectedandprivate.

Object destruction

You can destruct object members in order to make them more accessible in the code.
structPoint{
int x, y
}
letpoint=newPoint{x: 10,y: -2}let{ x, y }=pointprintln($"Point {x}, {y}")// you don't have to deconstruct all the variables of an objectlet{ x }=point

Lambda objects

You can create anonymus functions inside the code. These are also known as lambdas.
let foo = |int x| println(x)
foo(123)
interfaceCallback {
voidhandle(intx, inty)
}
voidbar(Callbackc) {
c.handle(2, 3)
}
bar(|x, y| println(x * y))

Default values

Using the default(Type) function, you can retrieve the default value of the given type.
letnumber=default(int)// 0letstate=default(bool)// falseletcar=default(Car)// null
Void features a system, which allows to create custom default values for your own class types.
You should put a "default" modifier before the class declaration, and a default() method must be declared as well.
defaultclassCredentials {
stringusername, passworddefault() {
return {
username: "admin",
password: "12345"
}
}
}
// initialize credentials to the default value let credentials = default(Credentials)// { username: "admin", password: "12345" }// you can use the default function without an explicit type, if the variable's type is defined explicitly
Credentials credentials = default// a simplfied syntax is also available
default Credentials credentials

Null-conditional operators

Null-conditional operators provide null-safe access to members.
A '?' mark is placed before the instruction to make it null-conditional.
let profile = database.find("user")// the profile's balance is retrieved if the profile is not null, otherwise a default value is required
println(profile?.balance ?? 0)let person = getSomePerson()// might be null// the "walk" method is not invoked if "person" is null
person?.walk()// you can use null-conditional operators on nested members as well
person?.entity?.motion?.move()

Static variables

By default, variables live only in the scope of a method. Static variables however provide a way to statically store data in the method,
that is accessible for further method calls.
intgetIncrementId(){staticletcounter=0returncounter++}getIncrementId()// returns 0getIncrementId()// returns 1getIncrementId()// returns 2

Enchanted switch

In Void you can use the old switch syntax.
switch (status) {
case200:
println("ok")
breakcase400:
println("error")
breakdefault:
println("unrecognized")
}
However in most cases, this old design forces us to write a lot of unnecessary code, such as always having to write "break" after each cases.
Void's echanted switch allows you to simplify switch blocks and use them as direct expressions.
enumStatus {
SUCCESS,
FAILED,
UNKNOWN
}
switch (code) {
200 -> println("success")
400 -> println("failed")
else -> println("unknown")
}
// you can merge cases as wellswitch (status) {
FAILED|UNKNOWN -> println("Unable to authenticate")
SUCCESS -> println("Authenticated")
}

Blocks as expressions

You can use code blocks as expressions, including loops and switches.
letstatus=switch(code){200->SUCCESS400->FAILEDelse->UNKNOWN}
StatusgetStatus(intcode) = switch (code) {
caseSUCCESS -> 200caseFAILED -> 400else -> 0
}
intcode = getStatus(SUCCESS)
// code is now 200

Method pre-processing

Void allows you to merge an instruction with the return keyword. Therefore you don't need two extra lines to do a negated method guard.
// previously you had to use two lines of code to returnvoidhandleCommand(Playerp,stringcommand){if(!p.hasPermission("use")){p.sendMessage("no perms")
return
}// handle the command}
// Void's syntax allows you to merge these two lines togetherif (!p.hasPermission("use"))
returnp.sendMessage("no perms")
// note that handleCommand returns void, so there are no conflicts returning something.// the return value of p.sendMessage (assuming it has one) is ignored

Method post-processing

In Void, you can queue post tasks for the method, that are going to be executed, when the method returns.
This can be used to prevent duplicating instructions whenever returning.
// prevously you had to do something like this// as you can see you need to call guard.unlock() 3 timesvoidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()if(myObject.someError){guard.unlock()return}if(myObject.someOtherError){guard.unlock()return}myObject.doSomething()guard.unlock()}
In Void, you can replace these duplicates using the "defer" keyword. Deferred instructions will be executed whenever the method returns.
voidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()deferguard.unlock()if(myObject.someError)returnif(myObject.someOtherError)returnmyObject.doSomething()}

Conditional variables

Conditional variables are local variables that only exist in the scope of the condition. This prevents keeping unused object in the memory, and unnecessary variable name reservation.
// previously you had to code something like thisletcreated=createFolder("myFolder")if(!created)returnerror
// Void allows you to simplify this code the following wayif(letcreated=createFolder("folder");!created)returnerror
When checking the isntance of objects, in older code practices, you had to first check the instance, then manually cast the value to the new type.
if(animalisParrot){letparrot=(Parrot)animalparrot.fly()}
However this code requires us to write an extra line of code and manually cast the value.
if(animal is Parrot parrot){
parrot.fly()}

Labels

You can name certain parts of code in order to allow jumps in scopes and nested loops.
// you may label your outer loop in order to allow "break" or "continue" on it.
myLoop:for(let i in1..10){for(let j in1..10){println($"{i}{j}")if(j == 5){break myLoop
}}}
// you can put labels to non-blocks as well// this example shows a loop jump to a section
hello:
doSomething()
if (condition)
goto hello

Multithreading

Void features an async value retrieval system. In other programming languages, you might have seen these ad promises, tasks or futures.
Future<User>getUser(){returnFuture.completed(myUser)}letuser=getUser().get()
Although Void lets you to explicitly declare future types, you should rather use the async/await syntax.
This automatically wraps the return type to be a Future, and calls get().
asyncUsergetUser(){returnmyUser}letuser=awaitgetUser()
You can create threads as well and have full control over them.
letthread=newThread(||println("hello")).setName("my-thread").setPriority(0)thread.start()

JSON in code

Void has a built-in JSON serializer, which allows you to directly map Void objects to JSON and vice versa.
// object to stringlet entity = @Json{entityId:100,meta:{
health:20,food:10,stamina:3},position:{x:200,y:4
z: -55}}let json = Json.serialize(entity)
println($"Data:{json}")// string to object
let data = "{ \"name\": \"admin\", \"userId\": 12345 }"
struct User{
string name
int userId
}
let user = Json.deserialize<User>(data)println($"Welcome,{user.name}")

HTML in code

Void lets you have HTML code inside source code. By default, tags are mapped with Void's built-in tag system, however this can be extended.
letpage= @Html{<divclass="container"><h1>Hello, World</h1></div>}

Bytecode in code

Void features direct bytecode instructions to be placed inside the source code.
int getSomeMagicValue(){let magic = 30
@Bytecodeunsafe{
ipush 100// push 100 to the stack
istore magic // store the value on the stack in the "magic" variable }return magic
}// the value of "test" will be 100let test = getSomeMagicValue()

Interaction with native code

Void allows you to call native library methods. It also has a framework that makes it possible to interace with Void from native context.
// get the implementation of the method from native code@Link("library.dll")
publicnativeintmultiply(inta, intb)
// call the native implementation from Void contextletresult = multiply(2, 6)
println($"Result: {result}")
// you can also link native methods of different signature@Link("library.dll")
@NativeTarget("bar")
nativevoidfoo(@NativeParam("int") inta)
// in case of having multiple native methods in a class, you should annotate the class instead@Link("library.dll")
classMyNativeImplementation {
nativeintfoo()
nativevoidbar(floatf)
@NativeTarget("baz")
nativevoidsomething()
}

Dynamic native implementation loading

letlibrary=newNativeLibrary().target(typeof(object))library.load()

Creating an HTTP server

Void's built-in http module allows you to create efficient web servers with the express-js design.
voidmain(){// create a new server instance which will take care of handling the request routesletserver=newHttpServer()// create a GET request handler for the "/" routeserver.get("/",|req,res|// respond to the request with a plain text messageres.send("Hello, World"))// start the web server and listen on port 80server.listen(80)}

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

The Void Programming Language

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler.

Disclaimer

Void has been recently changed paradigm, and now is willing to enter the world of native code. This opens up a new era of being able to write blazingly fast applications. Void aims to offer an easy and reliable syntax, that ensures that you can still write safe and simple code, without having to use a heavy runtime. The old compiler has been deprecated, and a new one is being developed at.

What is Void?

Void is an LLVM-based native programming language that is designed for developers to be able to create clean, fast and stable multi-platform applications with the power that native provides.

It has a relatively easy syntax, that follows most of the already existing code conventions.

Void features many utilities that enchant the experience of coding, such as object destruction, variable redeclaration, multi-returning, and much more.

Why Void?

Void compiles to LLVM IR, therefore it allows the application with the same exact code, to be compiled to all platforms, whilst forward and backward compatibility is guaranteed. This also makes it easy for Void to communicate with libraries written in other programming languages. It has many built-in designs, which makes it much easier to develop enterprise-grade applications.

How to use Void?

Void is designed to satisfy the needs of multiple development paradigms. Unlike many production-oriented languages, Void does not force object-oriented programming on you, however it is recommended to use in most cases.

Want to know more?

Join the discord server

Want to help the project?

Donate to the Void Project

Examples

Below are few examples showcasing what Void provides to enchant programming experience. Warning: This documentation was made way before, that Void had finally given a complete design, therefore expect some functionalities to be somewhat different when trying Void. Besides, some features may be not yet present in the following lines.

A Hello World application

voidmain() {
println("Hello, World")
}

Variable declaration

You can declare variables using the "let" keyword. It automatically detects the type of the variable, but type can be also declared explicitly.
let value = 100
In case of explicit type definition:
floatvalue = 3.5
As you may notice, semicolons are not required. The compiler automatically places them after the end of lines, when it is required. This allows the chaining syntax, unlike many in case of many languages.
database
.fetchUser("username")
.transform(User::from)
.getOrThrow(err)
In case of putting multiple statements in one line, you can separate them with putting a semicolon between them.
intf = 0; floatf = 10.0
Although it is possible in Void, it is considered a bad practice of coding, as it can possibly overcomplicate a line of code. You may put only one statement in a line.

Variable redeclaration

Normally languages doesn't let you declare variables twice, but as for some other languages such as Void, lets you to do that.
This is becuase in many cases you need to swap between variable types for a value, and having too much variables for a single value, or having long lines of type conversion is both considered a bad practice.
// bad codeintbalance = database.getUser("username").balancestringbalanceStr = $"{balance} USD"// also bad codestringbalance = string.from(database.getUser("username").balance) + " USD"
// simplified code with variable redeclarationlet balance = database.getUser("username").balancelet balance = $"{balance}USD"

Multi-returning

Void lets you have methods return multiple variables at once.
This allows you to have more simple codes, without the need of making structs holding multiple values for return.
// overcomplicated codestructHttpResponse{publicintcodepublicstringmessage}HttpResponsefetchURL(stringurl){returnnewHttpResponse{code:404,message:"Not found."}}// simplified code(int,string)fetchURL(stringurl){return(404,"Not found.")}
In the last example, a tuple is returned. A tuple can hold values of any length and any type. To access these values you can use a feature called tuple destruction.
let(code,message)=fetchURL("google.com")println($"Webserver responded with {code} status code")
You can also access these values without destructing the tuple. You need to specify the index of the tuple you want to retrieve.
let response = fetchURL("google.com")let code = response.0let message = response.1
Void allows you to name tuple members. Member names are specified inside the method return type declaration.
(booleansuccess,stringtoken)authenticate(Stringusername,stringpassword){return(true,"Authenticated.")}
In this case, you can access these values by their names.
letresult=authenticate("admin","12345")if(result.success){println("Authenticated.")println($"Token: {result.token}")}else{println("Invalid credentials.")}

Primitive types

Void features a wide variety of primitive types.
byte, short, int, long, float, double
And their unsigned version.
ubyte, ushort, uint, ulong, ufloat, udouble

Simplified number constants

You can specify the types of numbers in the number constant.
let byteVal = 30Blet shortVal = 150Slet intVal = 540// you don't need to put 'I' prefix, as non-decimal numbers are integers by default
let longVal = 120000Llet floatVal = 3.5Flet doubleVal = 40D

Class types

The most high-level one of types is the class type.
It has the capatibility of holding methods, implementations, fileds, and much more.
classCar {
stringtypeintspeedvoidmove() {
println("Moving...")
}
}
In case of the need of classes, which are only for the purpose of holding values, you can use structs.
Structs are simplified classes with the purpose of holding values.
structPoint{
int x
int y
}let point = new Point{x:2,y:3}
// you can use the simplified initializator, if Void knows the type of the structPointpoint = { x: -7, y: 0}
voiddrawPoint(Pointpoint) { /* do something */ }
drawPoint({ x: 30, y: 40 })
You can create classes which only show you what methods they have, but they don't have an actual implementations.
interfaceCar {
voidmove()
} 
The methods are implemented by a class.
classFerrariimplementsCar {
@Overridevoidmove() {
println("Vrooooom...")
}
}
You can have classes with constant members.
enumMimeType {
PLAIN_TEXT("text/plain"),
IMAGE("image/png"),
VIDEO("video/mp4")
stringdata;
MimeType(stringdata) {
this.data = data;
}
}
You can decorate classes, fields, methods or code blocks with annotations.
@interface Subscribe {
stringevent
}
@Subscribe(event = "playerJoin")
voidonPlayerJoin() {
println("A player has joined the game.");
}

Access modifiers

Void features two types of access modifier declarations.
You can specify access modifiers separately for each methods and fields.
However this is not a good practise, as you are making method declarations much longer by having to put these manually everywhere.
publicstaticvoidfoo(){println("bar")}structPoint{publicintx,y}
Alternatively, you can use "modifier blocks", which allows you to set the visibility modifiers of a section of code.
classEntity {
public:int posX, posY
int entityId
private:float health
float stamina
}
Visibilitymodifiersarepublic, protectedandprivate.

Object destruction

You can destruct object members in order to make them more accessible in the code.
structPoint{
int x, y
}
letpoint=newPoint{x: 10,y: -2}let{ x, y }=pointprintln($"Point {x}, {y}")// you don't have to deconstruct all the variables of an objectlet{ x }=point

Lambda objects

You can create anonymus functions inside the code. These are also known as lambdas.
let foo = |int x| println(x)
foo(123)
interfaceCallback {
voidhandle(intx, inty)
}
voidbar(Callbackc) {
c.handle(2, 3)
}
bar(|x, y| println(x * y))

Default values

Using the default(Type) function, you can retrieve the default value of the given type.
letnumber=default(int)// 0letstate=default(bool)// falseletcar=default(Car)// null
Void features a system, which allows to create custom default values for your own class types.
You should put a "default" modifier before the class declaration, and a default() method must be declared as well.
defaultclassCredentials {
stringusername, passworddefault() {
return {
username: "admin",
password: "12345"
}
}
}
// initialize credentials to the default value let credentials = default(Credentials)// { username: "admin", password: "12345" }// you can use the default function without an explicit type, if the variable's type is defined explicitly
Credentials credentials = default// a simplfied syntax is also available
default Credentials credentials

Null-conditional operators

Null-conditional operators provide null-safe access to members.
A '?' mark is placed before the instruction to make it null-conditional.
let profile = database.find("user")// the profile's balance is retrieved if the profile is not null, otherwise a default value is required
println(profile?.balance ?? 0)let person = getSomePerson()// might be null// the "walk" method is not invoked if "person" is null
person?.walk()// you can use null-conditional operators on nested members as well
person?.entity?.motion?.move()

Static variables

By default, variables live only in the scope of a method. Static variables however provide a way to statically store data in the method,
that is accessible for further method calls.
intgetIncrementId(){staticletcounter=0returncounter++}getIncrementId()// returns 0getIncrementId()// returns 1getIncrementId()// returns 2

Enchanted switch

In Void you can use the old switch syntax.
switch (status) {
case200:
println("ok")
breakcase400:
println("error")
breakdefault:
println("unrecognized")
}
However in most cases, this old design forces us to write a lot of unnecessary code, such as always having to write "break" after each cases.
Void's echanted switch allows you to simplify switch blocks and use them as direct expressions.
enumStatus {
SUCCESS,
FAILED,
UNKNOWN
}
switch (code) {
200 -> println("success")
400 -> println("failed")
else -> println("unknown")
}
// you can merge cases as wellswitch (status) {
FAILED|UNKNOWN -> println("Unable to authenticate")
SUCCESS -> println("Authenticated")
}

Blocks as expressions

You can use code blocks as expressions, including loops and switches.
letstatus=switch(code){200->SUCCESS400->FAILEDelse->UNKNOWN}
StatusgetStatus(intcode) = switch (code) {
caseSUCCESS -> 200caseFAILED -> 400else -> 0
}
intcode = getStatus(SUCCESS)
// code is now 200

Method pre-processing

Void allows you to merge an instruction with the return keyword. Therefore you don't need two extra lines to do a negated method guard.
// previously you had to use two lines of code to returnvoidhandleCommand(Playerp,stringcommand){if(!p.hasPermission("use")){p.sendMessage("no perms")
return
}// handle the command}
// Void's syntax allows you to merge these two lines togetherif (!p.hasPermission("use"))
returnp.sendMessage("no perms")
// note that handleCommand returns void, so there are no conflicts returning something.// the return value of p.sendMessage (assuming it has one) is ignored

Method post-processing

In Void, you can queue post tasks for the method, that are going to be executed, when the method returns.
This can be used to prevent duplicating instructions whenever returning.
// prevously you had to do something like this// as you can see you need to call guard.unlock() 3 timesvoidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()if(myObject.someError){guard.unlock()return}if(myObject.someOtherError){guard.unlock()return}myObject.doSomething()guard.unlock()}
In Void, you can replace these duplicates using the "defer" keyword. Deferred instructions will be executed whenever the method returns.
voidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()deferguard.unlock()if(myObject.someError)returnif(myObject.someOtherError)returnmyObject.doSomething()}

Conditional variables

Conditional variables are local variables that only exist in the scope of the condition. This prevents keeping unused object in the memory, and unnecessary variable name reservation.
// previously you had to code something like thisletcreated=createFolder("myFolder")if(!created)returnerror
// Void allows you to simplify this code the following wayif(letcreated=createFolder("folder");!created)returnerror
When checking the isntance of objects, in older code practices, you had to first check the instance, then manually cast the value to the new type.
if(animalisParrot){letparrot=(Parrot)animalparrot.fly()}
However this code requires us to write an extra line of code and manually cast the value.
if(animal is Parrot parrot){
parrot.fly()}

Labels

You can name certain parts of code in order to allow jumps in scopes and nested loops.
// you may label your outer loop in order to allow "break" or "continue" on it.
myLoop:for(let i in1..10){for(let j in1..10){println($"{i}{j}")if(j == 5){break myLoop
}}}
// you can put labels to non-blocks as well// this example shows a loop jump to a section
hello:
doSomething()
if (condition)
goto hello

Multithreading

Void features an async value retrieval system. In other programming languages, you might have seen these ad promises, tasks or futures.
Future<User>getUser(){returnFuture.completed(myUser)}letuser=getUser().get()
Although Void lets you to explicitly declare future types, you should rather use the async/await syntax.
This automatically wraps the return type to be a Future, and calls get().
asyncUsergetUser(){returnmyUser}letuser=awaitgetUser()
You can create threads as well and have full control over them.
letthread=newThread(||println("hello")).setName("my-thread").setPriority(0)thread.start()

JSON in code

Void has a built-in JSON serializer, which allows you to directly map Void objects to JSON and vice versa.
// object to stringlet entity = @Json{entityId:100,meta:{
health:20,food:10,stamina:3},position:{x:200,y:4
z: -55}}let json = Json.serialize(entity)
println($"Data:{json}")// string to object
let data = "{ \"name\": \"admin\", \"userId\": 12345 }"
struct User{
string name
int userId
}
let user = Json.deserialize<User>(data)println($"Welcome,{user.name}")

HTML in code

Void lets you have HTML code inside source code. By default, tags are mapped with Void's built-in tag system, however this can be extended.
letpage= @Html{<divclass="container"><h1>Hello, World</h1></div>}

Bytecode in code

Void features direct bytecode instructions to be placed inside the source code.
int getSomeMagicValue(){let magic = 30
@Bytecodeunsafe{
ipush 100// push 100 to the stack
istore magic // store the value on the stack in the "magic" variable }return magic
}// the value of "test" will be 100let test = getSomeMagicValue()

Interaction with native code

Void allows you to call native library methods. It also has a framework that makes it possible to interace with Void from native context.
// get the implementation of the method from native code@Link("library.dll")
publicnativeintmultiply(inta, intb)
// call the native implementation from Void contextletresult = multiply(2, 6)
println($"Result: {result}")
// you can also link native methods of different signature@Link("library.dll")
@NativeTarget("bar")
nativevoidfoo(@NativeParam("int") inta)
// in case of having multiple native methods in a class, you should annotate the class instead@Link("library.dll")
classMyNativeImplementation {
nativeintfoo()
nativevoidbar(floatf)
@NativeTarget("baz")
nativevoidsomething()
}

Dynamic native implementation loading

letlibrary=newNativeLibrary().target(typeof(object))library.load()

Creating an HTTP server

Void's built-in http module allows you to create efficient web servers with the express-js design.
voidmain(){// create a new server instance which will take care of handling the request routesletserver=newHttpServer()// create a GET request handler for the "/" routeserver.get("/",|req,res|// respond to the request with a plain text messageres.send("Hello, World"))// start the web server and listen on port 80server.listen(80)}

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

The Void Programming Language

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler.

Disclaimer

Void has been recently changed paradigm, and now is willing to enter the world of native code. This opens up a new era of being able to write blazingly fast applications. Void aims to offer an easy and reliable syntax, that ensures that you can still write safe and simple code, without having to use a heavy runtime. The old compiler has been deprecated, and a new one is being developed at.

What is Void?

Void is an LLVM-based native programming language that is designed for developers to be able to create clean, fast and stable multi-platform applications with the power that native provides.

It has a relatively easy syntax, that follows most of the already existing code conventions.

Void features many utilities that enchant the experience of coding, such as object destruction, variable redeclaration, multi-returning, and much more.

Why Void?

Void compiles to LLVM IR, therefore it allows the application with the same exact code, to be compiled to all platforms, whilst forward and backward compatibility is guaranteed. This also makes it easy for Void to communicate with libraries written in other programming languages. It has many built-in designs, which makes it much easier to develop enterprise-grade applications.

How to use Void?

Void is designed to satisfy the needs of multiple development paradigms. Unlike many production-oriented languages, Void does not force object-oriented programming on you, however it is recommended to use in most cases.

Want to know more?

Join the discord server

Want to help the project?

Donate to the Void Project

Examples

Below are few examples showcasing what Void provides to enchant programming experience. Warning: This documentation was made way before, that Void had finally given a complete design, therefore expect some functionalities to be somewhat different when trying Void. Besides, some features may be not yet present in the following lines.

A Hello World application

voidmain() {
println("Hello, World")
}

Variable declaration

You can declare variables using the "let" keyword. It automatically detects the type of the variable, but type can be also declared explicitly.
let value = 100
In case of explicit type definition:
floatvalue = 3.5
As you may notice, semicolons are not required. The compiler automatically places them after the end of lines, when it is required. This allows the chaining syntax, unlike many in case of many languages.
database
.fetchUser("username")
.transform(User::from)
.getOrThrow(err)
In case of putting multiple statements in one line, you can separate them with putting a semicolon between them.
intf = 0; floatf = 10.0
Although it is possible in Void, it is considered a bad practice of coding, as it can possibly overcomplicate a line of code. You may put only one statement in a line.

Variable redeclaration

Normally languages doesn't let you declare variables twice, but as for some other languages such as Void, lets you to do that.
This is becuase in many cases you need to swap between variable types for a value, and having too much variables for a single value, or having long lines of type conversion is both considered a bad practice.
// bad codeintbalance = database.getUser("username").balancestringbalanceStr = $"{balance} USD"// also bad codestringbalance = string.from(database.getUser("username").balance) + " USD"
// simplified code with variable redeclarationlet balance = database.getUser("username").balancelet balance = $"{balance}USD"

Multi-returning

Void lets you have methods return multiple variables at once.
This allows you to have more simple codes, without the need of making structs holding multiple values for return.
// overcomplicated codestructHttpResponse{publicintcodepublicstringmessage}HttpResponsefetchURL(stringurl){returnnewHttpResponse{code:404,message:"Not found."}}// simplified code(int,string)fetchURL(stringurl){return(404,"Not found.")}
In the last example, a tuple is returned. A tuple can hold values of any length and any type. To access these values you can use a feature called tuple destruction.
let(code,message)=fetchURL("google.com")println($"Webserver responded with {code} status code")
You can also access these values without destructing the tuple. You need to specify the index of the tuple you want to retrieve.
let response = fetchURL("google.com")let code = response.0let message = response.1
Void allows you to name tuple members. Member names are specified inside the method return type declaration.
(booleansuccess,stringtoken)authenticate(Stringusername,stringpassword){return(true,"Authenticated.")}
In this case, you can access these values by their names.
letresult=authenticate("admin","12345")if(result.success){println("Authenticated.")println($"Token: {result.token}")}else{println("Invalid credentials.")}

Primitive types

Void features a wide variety of primitive types.
byte, short, int, long, float, double
And their unsigned version.
ubyte, ushort, uint, ulong, ufloat, udouble

Simplified number constants

You can specify the types of numbers in the number constant.
let byteVal = 30Blet shortVal = 150Slet intVal = 540// you don't need to put 'I' prefix, as non-decimal numbers are integers by default
let longVal = 120000Llet floatVal = 3.5Flet doubleVal = 40D

Class types

The most high-level one of types is the class type.
It has the capatibility of holding methods, implementations, fileds, and much more.
classCar {
stringtypeintspeedvoidmove() {
println("Moving...")
}
}
In case of the need of classes, which are only for the purpose of holding values, you can use structs.
Structs are simplified classes with the purpose of holding values.
structPoint{
int x
int y
}let point = new Point{x:2,y:3}
// you can use the simplified initializator, if Void knows the type of the structPointpoint = { x: -7, y: 0}
voiddrawPoint(Pointpoint) { /* do something */ }
drawPoint({ x: 30, y: 40 })
You can create classes which only show you what methods they have, but they don't have an actual implementations.
interfaceCar {
voidmove()
} 
The methods are implemented by a class.
classFerrariimplementsCar {
@Overridevoidmove() {
println("Vrooooom...")
}
}
You can have classes with constant members.
enumMimeType {
PLAIN_TEXT("text/plain"),
IMAGE("image/png"),
VIDEO("video/mp4")
stringdata;
MimeType(stringdata) {
this.data = data;
}
}
You can decorate classes, fields, methods or code blocks with annotations.
@interface Subscribe {
stringevent
}
@Subscribe(event = "playerJoin")
voidonPlayerJoin() {
println("A player has joined the game.");
}

Access modifiers

Void features two types of access modifier declarations.
You can specify access modifiers separately for each methods and fields.
However this is not a good practise, as you are making method declarations much longer by having to put these manually everywhere.
publicstaticvoidfoo(){println("bar")}structPoint{publicintx,y}
Alternatively, you can use "modifier blocks", which allows you to set the visibility modifiers of a section of code.
classEntity {
public:int posX, posY
int entityId
private:float health
float stamina
}
Visibilitymodifiersarepublic, protectedandprivate.

Object destruction

You can destruct object members in order to make them more accessible in the code.
structPoint{
int x, y
}
letpoint=newPoint{x: 10,y: -2}let{ x, y }=pointprintln($"Point {x}, {y}")// you don't have to deconstruct all the variables of an objectlet{ x }=point

Lambda objects

You can create anonymus functions inside the code. These are also known as lambdas.
let foo = |int x| println(x)
foo(123)
interfaceCallback {
voidhandle(intx, inty)
}
voidbar(Callbackc) {
c.handle(2, 3)
}
bar(|x, y| println(x * y))

Default values

Using the default(Type) function, you can retrieve the default value of the given type.
letnumber=default(int)// 0letstate=default(bool)// falseletcar=default(Car)// null
Void features a system, which allows to create custom default values for your own class types.
You should put a "default" modifier before the class declaration, and a default() method must be declared as well.
defaultclassCredentials {
stringusername, passworddefault() {
return {
username: "admin",
password: "12345"
}
}
}
// initialize credentials to the default value let credentials = default(Credentials)// { username: "admin", password: "12345" }// you can use the default function without an explicit type, if the variable's type is defined explicitly
Credentials credentials = default// a simplfied syntax is also available
default Credentials credentials

Null-conditional operators

Null-conditional operators provide null-safe access to members.
A '?' mark is placed before the instruction to make it null-conditional.
let profile = database.find("user")// the profile's balance is retrieved if the profile is not null, otherwise a default value is required
println(profile?.balance ?? 0)let person = getSomePerson()// might be null// the "walk" method is not invoked if "person" is null
person?.walk()// you can use null-conditional operators on nested members as well
person?.entity?.motion?.move()

Static variables

By default, variables live only in the scope of a method. Static variables however provide a way to statically store data in the method,
that is accessible for further method calls.
intgetIncrementId(){staticletcounter=0returncounter++}getIncrementId()// returns 0getIncrementId()// returns 1getIncrementId()// returns 2

Enchanted switch

In Void you can use the old switch syntax.
switch (status) {
case200:
println("ok")
breakcase400:
println("error")
breakdefault:
println("unrecognized")
}
However in most cases, this old design forces us to write a lot of unnecessary code, such as always having to write "break" after each cases.
Void's echanted switch allows you to simplify switch blocks and use them as direct expressions.
enumStatus {
SUCCESS,
FAILED,
UNKNOWN
}
switch (code) {
200 -> println("success")
400 -> println("failed")
else -> println("unknown")
}
// you can merge cases as wellswitch (status) {
FAILED|UNKNOWN -> println("Unable to authenticate")
SUCCESS -> println("Authenticated")
}

Blocks as expressions

You can use code blocks as expressions, including loops and switches.
letstatus=switch(code){200->SUCCESS400->FAILEDelse->UNKNOWN}
StatusgetStatus(intcode) = switch (code) {
caseSUCCESS -> 200caseFAILED -> 400else -> 0
}
intcode = getStatus(SUCCESS)
// code is now 200

Method pre-processing

Void allows you to merge an instruction with the return keyword. Therefore you don't need two extra lines to do a negated method guard.
// previously you had to use two lines of code to returnvoidhandleCommand(Playerp,stringcommand){if(!p.hasPermission("use")){p.sendMessage("no perms")
return
}// handle the command}
// Void's syntax allows you to merge these two lines togetherif (!p.hasPermission("use"))
returnp.sendMessage("no perms")
// note that handleCommand returns void, so there are no conflicts returning something.// the return value of p.sendMessage (assuming it has one) is ignored

Method post-processing

In Void, you can queue post tasks for the method, that are going to be executed, when the method returns.
This can be used to prevent duplicating instructions whenever returning.
// prevously you had to do something like this// as you can see you need to call guard.unlock() 3 timesvoidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()if(myObject.someError){guard.unlock()return}if(myObject.someOtherError){guard.unlock()return}myObject.doSomething()guard.unlock()}
In Void, you can replace these duplicates using the "defer" keyword. Deferred instructions will be executed whenever the method returns.
voidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()deferguard.unlock()if(myObject.someError)returnif(myObject.someOtherError)returnmyObject.doSomething()}

Conditional variables

Conditional variables are local variables that only exist in the scope of the condition. This prevents keeping unused object in the memory, and unnecessary variable name reservation.
// previously you had to code something like thisletcreated=createFolder("myFolder")if(!created)returnerror
// Void allows you to simplify this code the following wayif(letcreated=createFolder("folder");!created)returnerror
When checking the isntance of objects, in older code practices, you had to first check the instance, then manually cast the value to the new type.
if(animalisParrot){letparrot=(Parrot)animalparrot.fly()}
However this code requires us to write an extra line of code and manually cast the value.
if(animal is Parrot parrot){
parrot.fly()}

Labels

You can name certain parts of code in order to allow jumps in scopes and nested loops.
// you may label your outer loop in order to allow "break" or "continue" on it.
myLoop:for(let i in1..10){for(let j in1..10){println($"{i}{j}")if(j == 5){break myLoop
}}}
// you can put labels to non-blocks as well// this example shows a loop jump to a section
hello:
doSomething()
if (condition)
goto hello

Multithreading

Void features an async value retrieval system. In other programming languages, you might have seen these ad promises, tasks or futures.
Future<User>getUser(){returnFuture.completed(myUser)}letuser=getUser().get()
Although Void lets you to explicitly declare future types, you should rather use the async/await syntax.
This automatically wraps the return type to be a Future, and calls get().
asyncUsergetUser(){returnmyUser}letuser=awaitgetUser()
You can create threads as well and have full control over them.
letthread=newThread(||println("hello")).setName("my-thread").setPriority(0)thread.start()

JSON in code

Void has a built-in JSON serializer, which allows you to directly map Void objects to JSON and vice versa.
// object to stringlet entity = @Json{entityId:100,meta:{
health:20,food:10,stamina:3},position:{x:200,y:4
z: -55}}let json = Json.serialize(entity)
println($"Data:{json}")// string to object
let data = "{ \"name\": \"admin\", \"userId\": 12345 }"
struct User{
string name
int userId
}
let user = Json.deserialize<User>(data)println($"Welcome,{user.name}")

HTML in code

Void lets you have HTML code inside source code. By default, tags are mapped with Void's built-in tag system, however this can be extended.
letpage= @Html{<divclass="container"><h1>Hello, World</h1></div>}

Bytecode in code

Void features direct bytecode instructions to be placed inside the source code.
int getSomeMagicValue(){let magic = 30
@Bytecodeunsafe{
ipush 100// push 100 to the stack
istore magic // store the value on the stack in the "magic" variable }return magic
}// the value of "test" will be 100let test = getSomeMagicValue()

Interaction with native code

Void allows you to call native library methods. It also has a framework that makes it possible to interace with Void from native context.
// get the implementation of the method from native code@Link("library.dll")
publicnativeintmultiply(inta, intb)
// call the native implementation from Void contextletresult = multiply(2, 6)
println($"Result: {result}")
// you can also link native methods of different signature@Link("library.dll")
@NativeTarget("bar")
nativevoidfoo(@NativeParam("int") inta)
// in case of having multiple native methods in a class, you should annotate the class instead@Link("library.dll")
classMyNativeImplementation {
nativeintfoo()
nativevoidbar(floatf)
@NativeTarget("baz")
nativevoidsomething()
}

Dynamic native implementation loading

letlibrary=newNativeLibrary().target(typeof(object))library.load()

Creating an HTTP server

Void's built-in http module allows you to create efficient web servers with the express-js design.
voidmain(){// create a new server instance which will take care of handling the request routesletserver=newHttpServer()// create a GET request handler for the "/" routeserver.get("/",|req,res|// respond to the request with a plain text messageres.send("Hello, World"))// start the web server and listen on port 80server.listen(80)}

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

The Void Programming Language

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler.

Disclaimer

Void has been recently changed paradigm, and now is willing to enter the world of native code. This opens up a new era of being able to write blazingly fast applications. Void aims to offer an easy and reliable syntax, that ensures that you can still write safe and simple code, without having to use a heavy runtime. The old compiler has been deprecated, and a new one is being developed at.

What is Void?

Void is an LLVM-based native programming language that is designed for developers to be able to create clean, fast and stable multi-platform applications with the power that native provides.

It has a relatively easy syntax, that follows most of the already existing code conventions.

Void features many utilities that enchant the experience of coding, such as object destruction, variable redeclaration, multi-returning, and much more.

Why Void?

Void compiles to LLVM IR, therefore it allows the application with the same exact code, to be compiled to all platforms, whilst forward and backward compatibility is guaranteed. This also makes it easy for Void to communicate with libraries written in other programming languages. It has many built-in designs, which makes it much easier to develop enterprise-grade applications.

How to use Void?

Void is designed to satisfy the needs of multiple development paradigms. Unlike many production-oriented languages, Void does not force object-oriented programming on you, however it is recommended to use in most cases.

Want to know more?

Join the discord server

Want to help the project?

Donate to the Void Project

Examples

Below are few examples showcasing what Void provides to enchant programming experience. Warning: This documentation was made way before, that Void had finally given a complete design, therefore expect some functionalities to be somewhat different when trying Void. Besides, some features may be not yet present in the following lines.

A Hello World application

voidmain() {
println("Hello, World")
}

Variable declaration

You can declare variables using the "let" keyword. It automatically detects the type of the variable, but type can be also declared explicitly.
let value = 100
In case of explicit type definition:
floatvalue = 3.5
As you may notice, semicolons are not required. The compiler automatically places them after the end of lines, when it is required. This allows the chaining syntax, unlike many in case of many languages.
database
.fetchUser("username")
.transform(User::from)
.getOrThrow(err)
In case of putting multiple statements in one line, you can separate them with putting a semicolon between them.
intf = 0; floatf = 10.0
Although it is possible in Void, it is considered a bad practice of coding, as it can possibly overcomplicate a line of code. You may put only one statement in a line.

Variable redeclaration

Normally languages doesn't let you declare variables twice, but as for some other languages such as Void, lets you to do that.
This is becuase in many cases you need to swap between variable types for a value, and having too much variables for a single value, or having long lines of type conversion is both considered a bad practice.
// bad codeintbalance = database.getUser("username").balancestringbalanceStr = $"{balance} USD"// also bad codestringbalance = string.from(database.getUser("username").balance) + " USD"
// simplified code with variable redeclarationlet balance = database.getUser("username").balancelet balance = $"{balance}USD"

Multi-returning

Void lets you have methods return multiple variables at once.
This allows you to have more simple codes, without the need of making structs holding multiple values for return.
// overcomplicated codestructHttpResponse{publicintcodepublicstringmessage}HttpResponsefetchURL(stringurl){returnnewHttpResponse{code:404,message:"Not found."}}// simplified code(int,string)fetchURL(stringurl){return(404,"Not found.")}
In the last example, a tuple is returned. A tuple can hold values of any length and any type. To access these values you can use a feature called tuple destruction.
let(code,message)=fetchURL("google.com")println($"Webserver responded with {code} status code")
You can also access these values without destructing the tuple. You need to specify the index of the tuple you want to retrieve.
let response = fetchURL("google.com")let code = response.0let message = response.1
Void allows you to name tuple members. Member names are specified inside the method return type declaration.
(booleansuccess,stringtoken)authenticate(Stringusername,stringpassword){return(true,"Authenticated.")}
In this case, you can access these values by their names.
letresult=authenticate("admin","12345")if(result.success){println("Authenticated.")println($"Token: {result.token}")}else{println("Invalid credentials.")}

Primitive types

Void features a wide variety of primitive types.
byte, short, int, long, float, double
And their unsigned version.
ubyte, ushort, uint, ulong, ufloat, udouble

Simplified number constants

You can specify the types of numbers in the number constant.
let byteVal = 30Blet shortVal = 150Slet intVal = 540// you don't need to put 'I' prefix, as non-decimal numbers are integers by default
let longVal = 120000Llet floatVal = 3.5Flet doubleVal = 40D

Class types

The most high-level one of types is the class type.
It has the capatibility of holding methods, implementations, fileds, and much more.
classCar {
stringtypeintspeedvoidmove() {
println("Moving...")
}
}
In case of the need of classes, which are only for the purpose of holding values, you can use structs.
Structs are simplified classes with the purpose of holding values.
structPoint{
int x
int y
}let point = new Point{x:2,y:3}
// you can use the simplified initializator, if Void knows the type of the structPointpoint = { x: -7, y: 0}
voiddrawPoint(Pointpoint) { /* do something */ }
drawPoint({ x: 30, y: 40 })
You can create classes which only show you what methods they have, but they don't have an actual implementations.
interfaceCar {
voidmove()
} 
The methods are implemented by a class.
classFerrariimplementsCar {
@Overridevoidmove() {
println("Vrooooom...")
}
}
You can have classes with constant members.
enumMimeType {
PLAIN_TEXT("text/plain"),
IMAGE("image/png"),
VIDEO("video/mp4")
stringdata;
MimeType(stringdata) {
this.data = data;
}
}
You can decorate classes, fields, methods or code blocks with annotations.
@interface Subscribe {
stringevent
}
@Subscribe(event = "playerJoin")
voidonPlayerJoin() {
println("A player has joined the game.");
}

Access modifiers

Void features two types of access modifier declarations.
You can specify access modifiers separately for each methods and fields.
However this is not a good practise, as you are making method declarations much longer by having to put these manually everywhere.
publicstaticvoidfoo(){println("bar")}structPoint{publicintx,y}
Alternatively, you can use "modifier blocks", which allows you to set the visibility modifiers of a section of code.
classEntity {
public:int posX, posY
int entityId
private:float health
float stamina
}
Visibilitymodifiersarepublic, protectedandprivate.

Object destruction

You can destruct object members in order to make them more accessible in the code.
structPoint{
int x, y
}
letpoint=newPoint{x: 10,y: -2}let{ x, y }=pointprintln($"Point {x}, {y}")// you don't have to deconstruct all the variables of an objectlet{ x }=point

Lambda objects

You can create anonymus functions inside the code. These are also known as lambdas.
let foo = |int x| println(x)
foo(123)
interfaceCallback {
voidhandle(intx, inty)
}
voidbar(Callbackc) {
c.handle(2, 3)
}
bar(|x, y| println(x * y))

Default values

Using the default(Type) function, you can retrieve the default value of the given type.
letnumber=default(int)// 0letstate=default(bool)// falseletcar=default(Car)// null
Void features a system, which allows to create custom default values for your own class types.
You should put a "default" modifier before the class declaration, and a default() method must be declared as well.
defaultclassCredentials {
stringusername, passworddefault() {
return {
username: "admin",
password: "12345"
}
}
}
// initialize credentials to the default value let credentials = default(Credentials)// { username: "admin", password: "12345" }// you can use the default function without an explicit type, if the variable's type is defined explicitly
Credentials credentials = default// a simplfied syntax is also available
default Credentials credentials

Null-conditional operators

Null-conditional operators provide null-safe access to members.
A '?' mark is placed before the instruction to make it null-conditional.
let profile = database.find("user")// the profile's balance is retrieved if the profile is not null, otherwise a default value is required
println(profile?.balance ?? 0)let person = getSomePerson()// might be null// the "walk" method is not invoked if "person" is null
person?.walk()// you can use null-conditional operators on nested members as well
person?.entity?.motion?.move()

Static variables

By default, variables live only in the scope of a method. Static variables however provide a way to statically store data in the method,
that is accessible for further method calls.
intgetIncrementId(){staticletcounter=0returncounter++}getIncrementId()// returns 0getIncrementId()// returns 1getIncrementId()// returns 2

Enchanted switch

In Void you can use the old switch syntax.
switch (status) {
case200:
println("ok")
breakcase400:
println("error")
breakdefault:
println("unrecognized")
}
However in most cases, this old design forces us to write a lot of unnecessary code, such as always having to write "break" after each cases.
Void's echanted switch allows you to simplify switch blocks and use them as direct expressions.
enumStatus {
SUCCESS,
FAILED,
UNKNOWN
}
switch (code) {
200 -> println("success")
400 -> println("failed")
else -> println("unknown")
}
// you can merge cases as wellswitch (status) {
FAILED|UNKNOWN -> println("Unable to authenticate")
SUCCESS -> println("Authenticated")
}

Blocks as expressions

You can use code blocks as expressions, including loops and switches.
letstatus=switch(code){200->SUCCESS400->FAILEDelse->UNKNOWN}
StatusgetStatus(intcode) = switch (code) {
caseSUCCESS -> 200caseFAILED -> 400else -> 0
}
intcode = getStatus(SUCCESS)
// code is now 200

Method pre-processing

Void allows you to merge an instruction with the return keyword. Therefore you don't need two extra lines to do a negated method guard.
// previously you had to use two lines of code to returnvoidhandleCommand(Playerp,stringcommand){if(!p.hasPermission("use")){p.sendMessage("no perms")
return
}// handle the command}
// Void's syntax allows you to merge these two lines togetherif (!p.hasPermission("use"))
returnp.sendMessage("no perms")
// note that handleCommand returns void, so there are no conflicts returning something.// the return value of p.sendMessage (assuming it has one) is ignored

Method post-processing

In Void, you can queue post tasks for the method, that are going to be executed, when the method returns.
This can be used to prevent duplicating instructions whenever returning.
// prevously you had to do something like this// as you can see you need to call guard.unlock() 3 timesvoidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()if(myObject.someError){guard.unlock()return}if(myObject.someOtherError){guard.unlock()return}myObject.doSomething()guard.unlock()}
In Void, you can replace these duplicates using the "defer" keyword. Deferred instructions will be executed whenever the method returns.
voidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()deferguard.unlock()if(myObject.someError)returnif(myObject.someOtherError)returnmyObject.doSomething()}

Conditional variables

Conditional variables are local variables that only exist in the scope of the condition. This prevents keeping unused object in the memory, and unnecessary variable name reservation.
// previously you had to code something like thisletcreated=createFolder("myFolder")if(!created)returnerror
// Void allows you to simplify this code the following wayif(letcreated=createFolder("folder");!created)returnerror
When checking the isntance of objects, in older code practices, you had to first check the instance, then manually cast the value to the new type.
if(animalisParrot){letparrot=(Parrot)animalparrot.fly()}
However this code requires us to write an extra line of code and manually cast the value.
if(animal is Parrot parrot){
parrot.fly()}

Labels

You can name certain parts of code in order to allow jumps in scopes and nested loops.
// you may label your outer loop in order to allow "break" or "continue" on it.
myLoop:for(let i in1..10){for(let j in1..10){println($"{i}{j}")if(j == 5){break myLoop
}}}
// you can put labels to non-blocks as well// this example shows a loop jump to a section
hello:
doSomething()
if (condition)
goto hello

Multithreading

Void features an async value retrieval system. In other programming languages, you might have seen these ad promises, tasks or futures.
Future<User>getUser(){returnFuture.completed(myUser)}letuser=getUser().get()
Although Void lets you to explicitly declare future types, you should rather use the async/await syntax.
This automatically wraps the return type to be a Future, and calls get().
asyncUsergetUser(){returnmyUser}letuser=awaitgetUser()
You can create threads as well and have full control over them.
letthread=newThread(||println("hello")).setName("my-thread").setPriority(0)thread.start()

JSON in code

Void has a built-in JSON serializer, which allows you to directly map Void objects to JSON and vice versa.
// object to stringlet entity = @Json{entityId:100,meta:{
health:20,food:10,stamina:3},position:{x:200,y:4
z: -55}}let json = Json.serialize(entity)
println($"Data:{json}")// string to object
let data = "{ \"name\": \"admin\", \"userId\": 12345 }"
struct User{
string name
int userId
}
let user = Json.deserialize<User>(data)println($"Welcome,{user.name}")

HTML in code

Void lets you have HTML code inside source code. By default, tags are mapped with Void's built-in tag system, however this can be extended.
letpage= @Html{<divclass="container"><h1>Hello, World</h1></div>}

Bytecode in code

Void features direct bytecode instructions to be placed inside the source code.
int getSomeMagicValue(){let magic = 30
@Bytecodeunsafe{
ipush 100// push 100 to the stack
istore magic // store the value on the stack in the "magic" variable }return magic
}// the value of "test" will be 100let test = getSomeMagicValue()

Interaction with native code

Void allows you to call native library methods. It also has a framework that makes it possible to interace with Void from native context.
// get the implementation of the method from native code@Link("library.dll")
publicnativeintmultiply(inta, intb)
// call the native implementation from Void contextletresult = multiply(2, 6)
println($"Result: {result}")
// you can also link native methods of different signature@Link("library.dll")
@NativeTarget("bar")
nativevoidfoo(@NativeParam("int") inta)
// in case of having multiple native methods in a class, you should annotate the class instead@Link("library.dll")
classMyNativeImplementation {
nativeintfoo()
nativevoidbar(floatf)
@NativeTarget("baz")
nativevoidsomething()
}

Dynamic native implementation loading

letlibrary=newNativeLibrary().target(typeof(object))library.load()

Creating an HTTP server

Void's built-in http module allows you to create efficient web servers with the express-js design.
voidmain(){// create a new server instance which will take care of handling the request routesletserver=newHttpServer()// create a GET request handler for the "/" routeserver.get("/",|req,res|// respond to the request with a plain text messageres.send("Hello, World"))// start the web server and listen on port 80server.listen(80)}

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

The Void Programming Language

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler.

Disclaimer

Void has been recently changed paradigm, and now is willing to enter the world of native code. This opens up a new era of being able to write blazingly fast applications. Void aims to offer an easy and reliable syntax, that ensures that you can still write safe and simple code, without having to use a heavy runtime. The old compiler has been deprecated, and a new one is being developed at.

What is Void?

Void is an LLVM-based native programming language that is designed for developers to be able to create clean, fast and stable multi-platform applications with the power that native provides.

It has a relatively easy syntax, that follows most of the already existing code conventions.

Void features many utilities that enchant the experience of coding, such as object destruction, variable redeclaration, multi-returning, and much more.

Why Void?

Void compiles to LLVM IR, therefore it allows the application with the same exact code, to be compiled to all platforms, whilst forward and backward compatibility is guaranteed. This also makes it easy for Void to communicate with libraries written in other programming languages. It has many built-in designs, which makes it much easier to develop enterprise-grade applications.

How to use Void?

Void is designed to satisfy the needs of multiple development paradigms. Unlike many production-oriented languages, Void does not force object-oriented programming on you, however it is recommended to use in most cases.

Want to know more?

Join the discord server

Want to help the project?

Donate to the Void Project

Examples

Below are few examples showcasing what Void provides to enchant programming experience. Warning: This documentation was made way before, that Void had finally given a complete design, therefore expect some functionalities to be somewhat different when trying Void. Besides, some features may be not yet present in the following lines.

A Hello World application

voidmain() {
println("Hello, World")
}

Variable declaration

You can declare variables using the "let" keyword. It automatically detects the type of the variable, but type can be also declared explicitly.
let value = 100
In case of explicit type definition:
floatvalue = 3.5
As you may notice, semicolons are not required. The compiler automatically places them after the end of lines, when it is required. This allows the chaining syntax, unlike many in case of many languages.
database
.fetchUser("username")
.transform(User::from)
.getOrThrow(err)
In case of putting multiple statements in one line, you can separate them with putting a semicolon between them.
intf = 0; floatf = 10.0
Although it is possible in Void, it is considered a bad practice of coding, as it can possibly overcomplicate a line of code. You may put only one statement in a line.

Variable redeclaration

Normally languages doesn't let you declare variables twice, but as for some other languages such as Void, lets you to do that.
This is becuase in many cases you need to swap between variable types for a value, and having too much variables for a single value, or having long lines of type conversion is both considered a bad practice.
// bad codeintbalance = database.getUser("username").balancestringbalanceStr = $"{balance} USD"// also bad codestringbalance = string.from(database.getUser("username").balance) + " USD"
// simplified code with variable redeclarationlet balance = database.getUser("username").balancelet balance = $"{balance}USD"

Multi-returning

Void lets you have methods return multiple variables at once.
This allows you to have more simple codes, without the need of making structs holding multiple values for return.
// overcomplicated codestructHttpResponse{publicintcodepublicstringmessage}HttpResponsefetchURL(stringurl){returnnewHttpResponse{code:404,message:"Not found."}}// simplified code(int,string)fetchURL(stringurl){return(404,"Not found.")}
In the last example, a tuple is returned. A tuple can hold values of any length and any type. To access these values you can use a feature called tuple destruction.
let(code,message)=fetchURL("google.com")println($"Webserver responded with {code} status code")
You can also access these values without destructing the tuple. You need to specify the index of the tuple you want to retrieve.
let response = fetchURL("google.com")let code = response.0let message = response.1
Void allows you to name tuple members. Member names are specified inside the method return type declaration.
(booleansuccess,stringtoken)authenticate(Stringusername,stringpassword){return(true,"Authenticated.")}
In this case, you can access these values by their names.
letresult=authenticate("admin","12345")if(result.success){println("Authenticated.")println($"Token: {result.token}")}else{println("Invalid credentials.")}

Primitive types

Void features a wide variety of primitive types.
byte, short, int, long, float, double
And their unsigned version.
ubyte, ushort, uint, ulong, ufloat, udouble

Simplified number constants

You can specify the types of numbers in the number constant.
let byteVal = 30Blet shortVal = 150Slet intVal = 540// you don't need to put 'I' prefix, as non-decimal numbers are integers by default
let longVal = 120000Llet floatVal = 3.5Flet doubleVal = 40D

Class types

The most high-level one of types is the class type.
It has the capatibility of holding methods, implementations, fileds, and much more.
classCar {
stringtypeintspeedvoidmove() {
println("Moving...")
}
}
In case of the need of classes, which are only for the purpose of holding values, you can use structs.
Structs are simplified classes with the purpose of holding values.
structPoint{
int x
int y
}let point = new Point{x:2,y:3}
// you can use the simplified initializator, if Void knows the type of the structPointpoint = { x: -7, y: 0}
voiddrawPoint(Pointpoint) { /* do something */ }
drawPoint({ x: 30, y: 40 })
You can create classes which only show you what methods they have, but they don't have an actual implementations.
interfaceCar {
voidmove()
} 
The methods are implemented by a class.
classFerrariimplementsCar {
@Overridevoidmove() {
println("Vrooooom...")
}
}
You can have classes with constant members.
enumMimeType {
PLAIN_TEXT("text/plain"),
IMAGE("image/png"),
VIDEO("video/mp4")
stringdata;
MimeType(stringdata) {
this.data = data;
}
}
You can decorate classes, fields, methods or code blocks with annotations.
@interface Subscribe {
stringevent
}
@Subscribe(event = "playerJoin")
voidonPlayerJoin() {
println("A player has joined the game.");
}

Access modifiers

Void features two types of access modifier declarations.
You can specify access modifiers separately for each methods and fields.
However this is not a good practise, as you are making method declarations much longer by having to put these manually everywhere.
publicstaticvoidfoo(){println("bar")}structPoint{publicintx,y}
Alternatively, you can use "modifier blocks", which allows you to set the visibility modifiers of a section of code.
classEntity {
public:int posX, posY
int entityId
private:float health
float stamina
}
Visibilitymodifiersarepublic, protectedandprivate.

Object destruction

You can destruct object members in order to make them more accessible in the code.
structPoint{
int x, y
}
letpoint=newPoint{x: 10,y: -2}let{ x, y }=pointprintln($"Point {x}, {y}")// you don't have to deconstruct all the variables of an objectlet{ x }=point

Lambda objects

You can create anonymus functions inside the code. These are also known as lambdas.
let foo = |int x| println(x)
foo(123)
interfaceCallback {
voidhandle(intx, inty)
}
voidbar(Callbackc) {
c.handle(2, 3)
}
bar(|x, y| println(x * y))

Default values

Using the default(Type) function, you can retrieve the default value of the given type.
letnumber=default(int)// 0letstate=default(bool)// falseletcar=default(Car)// null
Void features a system, which allows to create custom default values for your own class types.
You should put a "default" modifier before the class declaration, and a default() method must be declared as well.
defaultclassCredentials {
stringusername, passworddefault() {
return {
username: "admin",
password: "12345"
}
}
}
// initialize credentials to the default value let credentials = default(Credentials)// { username: "admin", password: "12345" }// you can use the default function without an explicit type, if the variable's type is defined explicitly
Credentials credentials = default// a simplfied syntax is also available
default Credentials credentials

Null-conditional operators

Null-conditional operators provide null-safe access to members.
A '?' mark is placed before the instruction to make it null-conditional.
let profile = database.find("user")// the profile's balance is retrieved if the profile is not null, otherwise a default value is required
println(profile?.balance ?? 0)let person = getSomePerson()// might be null// the "walk" method is not invoked if "person" is null
person?.walk()// you can use null-conditional operators on nested members as well
person?.entity?.motion?.move()

Static variables

By default, variables live only in the scope of a method. Static variables however provide a way to statically store data in the method,
that is accessible for further method calls.
intgetIncrementId(){staticletcounter=0returncounter++}getIncrementId()// returns 0getIncrementId()// returns 1getIncrementId()// returns 2

Enchanted switch

In Void you can use the old switch syntax.
switch (status) {
case200:
println("ok")
breakcase400:
println("error")
breakdefault:
println("unrecognized")
}
However in most cases, this old design forces us to write a lot of unnecessary code, such as always having to write "break" after each cases.
Void's echanted switch allows you to simplify switch blocks and use them as direct expressions.
enumStatus {
SUCCESS,
FAILED,
UNKNOWN
}
switch (code) {
200 -> println("success")
400 -> println("failed")
else -> println("unknown")
}
// you can merge cases as wellswitch (status) {
FAILED|UNKNOWN -> println("Unable to authenticate")
SUCCESS -> println("Authenticated")
}

Blocks as expressions

You can use code blocks as expressions, including loops and switches.
letstatus=switch(code){200->SUCCESS400->FAILEDelse->UNKNOWN}
StatusgetStatus(intcode) = switch (code) {
caseSUCCESS -> 200caseFAILED -> 400else -> 0
}
intcode = getStatus(SUCCESS)
// code is now 200

Method pre-processing

Void allows you to merge an instruction with the return keyword. Therefore you don't need two extra lines to do a negated method guard.
// previously you had to use two lines of code to returnvoidhandleCommand(Playerp,stringcommand){if(!p.hasPermission("use")){p.sendMessage("no perms")
return
}// handle the command}
// Void's syntax allows you to merge these two lines togetherif (!p.hasPermission("use"))
returnp.sendMessage("no perms")
// note that handleCommand returns void, so there are no conflicts returning something.// the return value of p.sendMessage (assuming it has one) is ignored

Method post-processing

In Void, you can queue post tasks for the method, that are going to be executed, when the method returns.
This can be used to prevent duplicating instructions whenever returning.
// prevously you had to do something like this// as you can see you need to call guard.unlock() 3 timesvoidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()if(myObject.someError){guard.unlock()return}if(myObject.someOtherError){guard.unlock()return}myObject.doSomething()guard.unlock()}
In Void, you can replace these duplicates using the "defer" keyword. Deferred instructions will be executed whenever the method returns.
voidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()deferguard.unlock()if(myObject.someError)returnif(myObject.someOtherError)returnmyObject.doSomething()}

Conditional variables

Conditional variables are local variables that only exist in the scope of the condition. This prevents keeping unused object in the memory, and unnecessary variable name reservation.
// previously you had to code something like thisletcreated=createFolder("myFolder")if(!created)returnerror
// Void allows you to simplify this code the following wayif(letcreated=createFolder("folder");!created)returnerror
When checking the isntance of objects, in older code practices, you had to first check the instance, then manually cast the value to the new type.
if(animalisParrot){letparrot=(Parrot)animalparrot.fly()}
However this code requires us to write an extra line of code and manually cast the value.
if(animal is Parrot parrot){
parrot.fly()}

Labels

You can name certain parts of code in order to allow jumps in scopes and nested loops.
// you may label your outer loop in order to allow "break" or "continue" on it.
myLoop:for(let i in1..10){for(let j in1..10){println($"{i}{j}")if(j == 5){break myLoop
}}}
// you can put labels to non-blocks as well// this example shows a loop jump to a section
hello:
doSomething()
if (condition)
goto hello

Multithreading

Void features an async value retrieval system. In other programming languages, you might have seen these ad promises, tasks or futures.
Future<User>getUser(){returnFuture.completed(myUser)}letuser=getUser().get()
Although Void lets you to explicitly declare future types, you should rather use the async/await syntax.
This automatically wraps the return type to be a Future, and calls get().
asyncUsergetUser(){returnmyUser}letuser=awaitgetUser()
You can create threads as well and have full control over them.
letthread=newThread(||println("hello")).setName("my-thread").setPriority(0)thread.start()

JSON in code

Void has a built-in JSON serializer, which allows you to directly map Void objects to JSON and vice versa.
// object to stringlet entity = @Json{entityId:100,meta:{
health:20,food:10,stamina:3},position:{x:200,y:4
z: -55}}let json = Json.serialize(entity)
println($"Data:{json}")// string to object
let data = "{ \"name\": \"admin\", \"userId\": 12345 }"
struct User{
string name
int userId
}
let user = Json.deserialize<User>(data)println($"Welcome,{user.name}")

HTML in code

Void lets you have HTML code inside source code. By default, tags are mapped with Void's built-in tag system, however this can be extended.
letpage= @Html{<divclass="container"><h1>Hello, World</h1></div>}

Bytecode in code

Void features direct bytecode instructions to be placed inside the source code.
int getSomeMagicValue(){let magic = 30
@Bytecodeunsafe{
ipush 100// push 100 to the stack
istore magic // store the value on the stack in the "magic" variable }return magic
}// the value of "test" will be 100let test = getSomeMagicValue()

Interaction with native code

Void allows you to call native library methods. It also has a framework that makes it possible to interace with Void from native context.
// get the implementation of the method from native code@Link("library.dll")
publicnativeintmultiply(inta, intb)
// call the native implementation from Void contextletresult = multiply(2, 6)
println($"Result: {result}")
// you can also link native methods of different signature@Link("library.dll")
@NativeTarget("bar")
nativevoidfoo(@NativeParam("int") inta)
// in case of having multiple native methods in a class, you should annotate the class instead@Link("library.dll")
classMyNativeImplementation {
nativeintfoo()
nativevoidbar(floatf)
@NativeTarget("baz")
nativevoidsomething()
}

Dynamic native implementation loading

letlibrary=newNativeLibrary().target(typeof(object))library.load()

Creating an HTTP server

Void's built-in http module allows you to create efficient web servers with the express-js design.
voidmain(){// create a new server instance which will take care of handling the request routesletserver=newHttpServer()// create a GET request handler for the "/" routeserver.get("/",|req,res|// respond to the request with a plain text messageres.send("Hello, World"))// start the web server and listen on port 80server.listen(80)}

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

The Void Programming Language

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler.

Disclaimer

Void has been recently changed paradigm, and now is willing to enter the world of native code. This opens up a new era of being able to write blazingly fast applications. Void aims to offer an easy and reliable syntax, that ensures that you can still write safe and simple code, without having to use a heavy runtime. The old compiler has been deprecated, and a new one is being developed at.

What is Void?

Void is an LLVM-based native programming language that is designed for developers to be able to create clean, fast and stable multi-platform applications with the power that native provides.

It has a relatively easy syntax, that follows most of the already existing code conventions.

Void features many utilities that enchant the experience of coding, such as object destruction, variable redeclaration, multi-returning, and much more.

Why Void?

Void compiles to LLVM IR, therefore it allows the application with the same exact code, to be compiled to all platforms, whilst forward and backward compatibility is guaranteed. This also makes it easy for Void to communicate with libraries written in other programming languages. It has many built-in designs, which makes it much easier to develop enterprise-grade applications.

How to use Void?

Void is designed to satisfy the needs of multiple development paradigms. Unlike many production-oriented languages, Void does not force object-oriented programming on you, however it is recommended to use in most cases.

Want to know more?

Join the discord server

Want to help the project?

Donate to the Void Project

Examples

Below are few examples showcasing what Void provides to enchant programming experience. Warning: This documentation was made way before, that Void had finally given a complete design, therefore expect some functionalities to be somewhat different when trying Void. Besides, some features may be not yet present in the following lines.

A Hello World application

voidmain() {
println("Hello, World")
}

Variable declaration

You can declare variables using the "let" keyword. It automatically detects the type of the variable, but type can be also declared explicitly.
let value = 100
In case of explicit type definition:
floatvalue = 3.5
As you may notice, semicolons are not required. The compiler automatically places them after the end of lines, when it is required. This allows the chaining syntax, unlike many in case of many languages.
database
.fetchUser("username")
.transform(User::from)
.getOrThrow(err)
In case of putting multiple statements in one line, you can separate them with putting a semicolon between them.
intf = 0; floatf = 10.0
Although it is possible in Void, it is considered a bad practice of coding, as it can possibly overcomplicate a line of code. You may put only one statement in a line.

Variable redeclaration

Normally languages doesn't let you declare variables twice, but as for some other languages such as Void, lets you to do that.
This is becuase in many cases you need to swap between variable types for a value, and having too much variables for a single value, or having long lines of type conversion is both considered a bad practice.
// bad codeintbalance = database.getUser("username").balancestringbalanceStr = $"{balance} USD"// also bad codestringbalance = string.from(database.getUser("username").balance) + " USD"
// simplified code with variable redeclarationlet balance = database.getUser("username").balancelet balance = $"{balance}USD"

Multi-returning

Void lets you have methods return multiple variables at once.
This allows you to have more simple codes, without the need of making structs holding multiple values for return.
// overcomplicated codestructHttpResponse{publicintcodepublicstringmessage}HttpResponsefetchURL(stringurl){returnnewHttpResponse{code:404,message:"Not found."}}// simplified code(int,string)fetchURL(stringurl){return(404,"Not found.")}
In the last example, a tuple is returned. A tuple can hold values of any length and any type. To access these values you can use a feature called tuple destruction.
let(code,message)=fetchURL("google.com")println($"Webserver responded with {code} status code")
You can also access these values without destructing the tuple. You need to specify the index of the tuple you want to retrieve.
let response = fetchURL("google.com")let code = response.0let message = response.1
Void allows you to name tuple members. Member names are specified inside the method return type declaration.
(booleansuccess,stringtoken)authenticate(Stringusername,stringpassword){return(true,"Authenticated.")}
In this case, you can access these values by their names.
letresult=authenticate("admin","12345")if(result.success){println("Authenticated.")println($"Token: {result.token}")}else{println("Invalid credentials.")}

Primitive types

Void features a wide variety of primitive types.
byte, short, int, long, float, double
And their unsigned version.
ubyte, ushort, uint, ulong, ufloat, udouble

Simplified number constants

You can specify the types of numbers in the number constant.
let byteVal = 30Blet shortVal = 150Slet intVal = 540// you don't need to put 'I' prefix, as non-decimal numbers are integers by default
let longVal = 120000Llet floatVal = 3.5Flet doubleVal = 40D

Class types

The most high-level one of types is the class type.
It has the capatibility of holding methods, implementations, fileds, and much more.
classCar {
stringtypeintspeedvoidmove() {
println("Moving...")
}
}
In case of the need of classes, which are only for the purpose of holding values, you can use structs.
Structs are simplified classes with the purpose of holding values.
structPoint{
int x
int y
}let point = new Point{x:2,y:3}
// you can use the simplified initializator, if Void knows the type of the structPointpoint = { x: -7, y: 0}
voiddrawPoint(Pointpoint) { /* do something */ }
drawPoint({ x: 30, y: 40 })
You can create classes which only show you what methods they have, but they don't have an actual implementations.
interfaceCar {
voidmove()
} 
The methods are implemented by a class.
classFerrariimplementsCar {
@Overridevoidmove() {
println("Vrooooom...")
}
}
You can have classes with constant members.
enumMimeType {
PLAIN_TEXT("text/plain"),
IMAGE("image/png"),
VIDEO("video/mp4")
stringdata;
MimeType(stringdata) {
this.data = data;
}
}
You can decorate classes, fields, methods or code blocks with annotations.
@interface Subscribe {
stringevent
}
@Subscribe(event = "playerJoin")
voidonPlayerJoin() {
println("A player has joined the game.");
}

Access modifiers

Void features two types of access modifier declarations.
You can specify access modifiers separately for each methods and fields.
However this is not a good practise, as you are making method declarations much longer by having to put these manually everywhere.
publicstaticvoidfoo(){println("bar")}structPoint{publicintx,y}
Alternatively, you can use "modifier blocks", which allows you to set the visibility modifiers of a section of code.
classEntity {
public:int posX, posY
int entityId
private:float health
float stamina
}
Visibilitymodifiersarepublic, protectedandprivate.

Object destruction

You can destruct object members in order to make them more accessible in the code.
structPoint{
int x, y
}
letpoint=newPoint{x: 10,y: -2}let{ x, y }=pointprintln($"Point {x}, {y}")// you don't have to deconstruct all the variables of an objectlet{ x }=point

Lambda objects

You can create anonymus functions inside the code. These are also known as lambdas.
let foo = |int x| println(x)
foo(123)
interfaceCallback {
voidhandle(intx, inty)
}
voidbar(Callbackc) {
c.handle(2, 3)
}
bar(|x, y| println(x * y))

Default values

Using the default(Type) function, you can retrieve the default value of the given type.
letnumber=default(int)// 0letstate=default(bool)// falseletcar=default(Car)// null
Void features a system, which allows to create custom default values for your own class types.
You should put a "default" modifier before the class declaration, and a default() method must be declared as well.
defaultclassCredentials {
stringusername, passworddefault() {
return {
username: "admin",
password: "12345"
}
}
}
// initialize credentials to the default value let credentials = default(Credentials)// { username: "admin", password: "12345" }// you can use the default function without an explicit type, if the variable's type is defined explicitly
Credentials credentials = default// a simplfied syntax is also available
default Credentials credentials

Null-conditional operators

Null-conditional operators provide null-safe access to members.
A '?' mark is placed before the instruction to make it null-conditional.
let profile = database.find("user")// the profile's balance is retrieved if the profile is not null, otherwise a default value is required
println(profile?.balance ?? 0)let person = getSomePerson()// might be null// the "walk" method is not invoked if "person" is null
person?.walk()// you can use null-conditional operators on nested members as well
person?.entity?.motion?.move()

Static variables

By default, variables live only in the scope of a method. Static variables however provide a way to statically store data in the method,
that is accessible for further method calls.
intgetIncrementId(){staticletcounter=0returncounter++}getIncrementId()// returns 0getIncrementId()// returns 1getIncrementId()// returns 2

Enchanted switch

In Void you can use the old switch syntax.
switch (status) {
case200:
println("ok")
breakcase400:
println("error")
breakdefault:
println("unrecognized")
}
However in most cases, this old design forces us to write a lot of unnecessary code, such as always having to write "break" after each cases.
Void's echanted switch allows you to simplify switch blocks and use them as direct expressions.
enumStatus {
SUCCESS,
FAILED,
UNKNOWN
}
switch (code) {
200 -> println("success")
400 -> println("failed")
else -> println("unknown")
}
// you can merge cases as wellswitch (status) {
FAILED|UNKNOWN -> println("Unable to authenticate")
SUCCESS -> println("Authenticated")
}

Blocks as expressions

You can use code blocks as expressions, including loops and switches.
letstatus=switch(code){200->SUCCESS400->FAILEDelse->UNKNOWN}
StatusgetStatus(intcode) = switch (code) {
caseSUCCESS -> 200caseFAILED -> 400else -> 0
}
intcode = getStatus(SUCCESS)
// code is now 200

Method pre-processing

Void allows you to merge an instruction with the return keyword. Therefore you don't need two extra lines to do a negated method guard.
// previously you had to use two lines of code to returnvoidhandleCommand(Playerp,stringcommand){if(!p.hasPermission("use")){p.sendMessage("no perms")
return
}// handle the command}
// Void's syntax allows you to merge these two lines togetherif (!p.hasPermission("use"))
returnp.sendMessage("no perms")
// note that handleCommand returns void, so there are no conflicts returning something.// the return value of p.sendMessage (assuming it has one) is ignored

Method post-processing

In Void, you can queue post tasks for the method, that are going to be executed, when the method returns.
This can be used to prevent duplicating instructions whenever returning.
// prevously you had to do something like this// as you can see you need to call guard.unlock() 3 timesvoidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()if(myObject.someError){guard.unlock()return}if(myObject.someOtherError){guard.unlock()return}myObject.doSomething()guard.unlock()}
In Void, you can replace these duplicates using the "defer" keyword. Deferred instructions will be executed whenever the method returns.
voidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()deferguard.unlock()if(myObject.someError)returnif(myObject.someOtherError)returnmyObject.doSomething()}

Conditional variables

Conditional variables are local variables that only exist in the scope of the condition. This prevents keeping unused object in the memory, and unnecessary variable name reservation.
// previously you had to code something like thisletcreated=createFolder("myFolder")if(!created)returnerror
// Void allows you to simplify this code the following wayif(letcreated=createFolder("folder");!created)returnerror
When checking the isntance of objects, in older code practices, you had to first check the instance, then manually cast the value to the new type.
if(animalisParrot){letparrot=(Parrot)animalparrot.fly()}
However this code requires us to write an extra line of code and manually cast the value.
if(animal is Parrot parrot){
parrot.fly()}

Labels

You can name certain parts of code in order to allow jumps in scopes and nested loops.
// you may label your outer loop in order to allow "break" or "continue" on it.
myLoop:for(let i in1..10){for(let j in1..10){println($"{i}{j}")if(j == 5){break myLoop
}}}
// you can put labels to non-blocks as well// this example shows a loop jump to a section
hello:
doSomething()
if (condition)
goto hello

Multithreading

Void features an async value retrieval system. In other programming languages, you might have seen these ad promises, tasks or futures.
Future<User>getUser(){returnFuture.completed(myUser)}letuser=getUser().get()
Although Void lets you to explicitly declare future types, you should rather use the async/await syntax.
This automatically wraps the return type to be a Future, and calls get().
asyncUsergetUser(){returnmyUser}letuser=awaitgetUser()
You can create threads as well and have full control over them.
letthread=newThread(||println("hello")).setName("my-thread").setPriority(0)thread.start()

JSON in code

Void has a built-in JSON serializer, which allows you to directly map Void objects to JSON and vice versa.
// object to stringlet entity = @Json{entityId:100,meta:{
health:20,food:10,stamina:3},position:{x:200,y:4
z: -55}}let json = Json.serialize(entity)
println($"Data:{json}")// string to object
let data = "{ \"name\": \"admin\", \"userId\": 12345 }"
struct User{
string name
int userId
}
let user = Json.deserialize<User>(data)println($"Welcome,{user.name}")

HTML in code

Void lets you have HTML code inside source code. By default, tags are mapped with Void's built-in tag system, however this can be extended.
letpage= @Html{<divclass="container"><h1>Hello, World</h1></div>}

Bytecode in code

Void features direct bytecode instructions to be placed inside the source code.
int getSomeMagicValue(){let magic = 30
@Bytecodeunsafe{
ipush 100// push 100 to the stack
istore magic // store the value on the stack in the "magic" variable }return magic
}// the value of "test" will be 100let test = getSomeMagicValue()

Interaction with native code

Void allows you to call native library methods. It also has a framework that makes it possible to interace with Void from native context.
// get the implementation of the method from native code@Link("library.dll")
publicnativeintmultiply(inta, intb)
// call the native implementation from Void contextletresult = multiply(2, 6)
println($"Result: {result}")
// you can also link native methods of different signature@Link("library.dll")
@NativeTarget("bar")
nativevoidfoo(@NativeParam("int") inta)
// in case of having multiple native methods in a class, you should annotate the class instead@Link("library.dll")
classMyNativeImplementation {
nativeintfoo()
nativevoidbar(floatf)
@NativeTarget("baz")
nativevoidsomething()
}

Dynamic native implementation loading

letlibrary=newNativeLibrary().target(typeof(object))library.load()

Creating an HTTP server

Void's built-in http module allows you to create efficient web servers with the express-js design.
voidmain(){// create a new server instance which will take care of handling the request routesletserver=newHttpServer()// create a GET request handler for the "/" routeserver.get("/",|req,res|// respond to the request with a plain text messageres.send("Hello, World"))// start the web server and listen on port 80server.listen(80)}

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

The Void Programming Language

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. – Martin Fowler.

Disclaimer

Void has been recently changed paradigm, and now is willing to enter the world of native code. This opens up a new era of being able to write blazingly fast applications. Void aims to offer an easy and reliable syntax, that ensures that you can still write safe and simple code, without having to use a heavy runtime. The old compiler has been deprecated, and a new one is being developed at.

What is Void?

Void is an LLVM-based native programming language that is designed for developers to be able to create clean, fast and stable multi-platform applications with the power that native provides.

It has a relatively easy syntax, that follows most of the already existing code conventions.

Void features many utilities that enchant the experience of coding, such as object destruction, variable redeclaration, multi-returning, and much more.

Why Void?

Void compiles to LLVM IR, therefore it allows the application with the same exact code, to be compiled to all platforms, whilst forward and backward compatibility is guaranteed. This also makes it easy for Void to communicate with libraries written in other programming languages. It has many built-in designs, which makes it much easier to develop enterprise-grade applications.

How to use Void?

Void is designed to satisfy the needs of multiple development paradigms. Unlike many production-oriented languages, Void does not force object-oriented programming on you, however it is recommended to use in most cases.

Want to know more?

Join the discord server

Want to help the project?

Donate to the Void Project

Examples

Below are few examples showcasing what Void provides to enchant programming experience. Warning: This documentation was made way before, that Void had finally given a complete design, therefore expect some functionalities to be somewhat different when trying Void. Besides, some features may be not yet present in the following lines.

A Hello World application

voidmain() {
println("Hello, World")
}

Variable declaration

You can declare variables using the "let" keyword. It automatically detects the type of the variable, but type can be also declared explicitly.
let value = 100
In case of explicit type definition:
floatvalue = 3.5
As you may notice, semicolons are not required. The compiler automatically places them after the end of lines, when it is required. This allows the chaining syntax, unlike many in case of many languages.
database
.fetchUser("username")
.transform(User::from)
.getOrThrow(err)
In case of putting multiple statements in one line, you can separate them with putting a semicolon between them.
intf = 0; floatf = 10.0
Although it is possible in Void, it is considered a bad practice of coding, as it can possibly overcomplicate a line of code. You may put only one statement in a line.

Variable redeclaration

Normally languages doesn't let you declare variables twice, but as for some other languages such as Void, lets you to do that.
This is becuase in many cases you need to swap between variable types for a value, and having too much variables for a single value, or having long lines of type conversion is both considered a bad practice.
// bad codeintbalance = database.getUser("username").balancestringbalanceStr = $"{balance} USD"// also bad codestringbalance = string.from(database.getUser("username").balance) + " USD"
// simplified code with variable redeclarationlet balance = database.getUser("username").balancelet balance = $"{balance}USD"

Multi-returning

Void lets you have methods return multiple variables at once.
This allows you to have more simple codes, without the need of making structs holding multiple values for return.
// overcomplicated codestructHttpResponse{publicintcodepublicstringmessage}HttpResponsefetchURL(stringurl){returnnewHttpResponse{code:404,message:"Not found."}}// simplified code(int,string)fetchURL(stringurl){return(404,"Not found.")}
In the last example, a tuple is returned. A tuple can hold values of any length and any type. To access these values you can use a feature called tuple destruction.
let(code,message)=fetchURL("google.com")println($"Webserver responded with {code} status code")
You can also access these values without destructing the tuple. You need to specify the index of the tuple you want to retrieve.
let response = fetchURL("google.com")let code = response.0let message = response.1
Void allows you to name tuple members. Member names are specified inside the method return type declaration.
(booleansuccess,stringtoken)authenticate(Stringusername,stringpassword){return(true,"Authenticated.")}
In this case, you can access these values by their names.
letresult=authenticate("admin","12345")if(result.success){println("Authenticated.")println($"Token: {result.token}")}else{println("Invalid credentials.")}

Primitive types

Void features a wide variety of primitive types.
byte, short, int, long, float, double
And their unsigned version.
ubyte, ushort, uint, ulong, ufloat, udouble

Simplified number constants

You can specify the types of numbers in the number constant.
let byteVal = 30Blet shortVal = 150Slet intVal = 540// you don't need to put 'I' prefix, as non-decimal numbers are integers by default
let longVal = 120000Llet floatVal = 3.5Flet doubleVal = 40D

Class types

The most high-level one of types is the class type.
It has the capatibility of holding methods, implementations, fileds, and much more.
classCar {
stringtypeintspeedvoidmove() {
println("Moving...")
}
}
In case of the need of classes, which are only for the purpose of holding values, you can use structs.
Structs are simplified classes with the purpose of holding values.
structPoint{
int x
int y
}let point = new Point{x:2,y:3}
// you can use the simplified initializator, if Void knows the type of the structPointpoint = { x: -7, y: 0}
voiddrawPoint(Pointpoint) { /* do something */ }
drawPoint({ x: 30, y: 40 })
You can create classes which only show you what methods they have, but they don't have an actual implementations.
interfaceCar {
voidmove()
} 
The methods are implemented by a class.
classFerrariimplementsCar {
@Overridevoidmove() {
println("Vrooooom...")
}
}
You can have classes with constant members.
enumMimeType {
PLAIN_TEXT("text/plain"),
IMAGE("image/png"),
VIDEO("video/mp4")
stringdata;
MimeType(stringdata) {
this.data = data;
}
}
You can decorate classes, fields, methods or code blocks with annotations.
@interface Subscribe {
stringevent
}
@Subscribe(event = "playerJoin")
voidonPlayerJoin() {
println("A player has joined the game.");
}

Access modifiers

Void features two types of access modifier declarations.
You can specify access modifiers separately for each methods and fields.
However this is not a good practise, as you are making method declarations much longer by having to put these manually everywhere.
publicstaticvoidfoo(){println("bar")}structPoint{publicintx,y}
Alternatively, you can use "modifier blocks", which allows you to set the visibility modifiers of a section of code.
classEntity {
public:int posX, posY
int entityId
private:float health
float stamina
}
Visibilitymodifiersarepublic, protectedandprivate.

Object destruction

You can destruct object members in order to make them more accessible in the code.
structPoint{
int x, y
}
letpoint=newPoint{x: 10,y: -2}let{ x, y }=pointprintln($"Point {x}, {y}")// you don't have to deconstruct all the variables of an objectlet{ x }=point

Lambda objects

You can create anonymus functions inside the code. These are also known as lambdas.
let foo = |int x| println(x)
foo(123)
interfaceCallback {
voidhandle(intx, inty)
}
voidbar(Callbackc) {
c.handle(2, 3)
}
bar(|x, y| println(x * y))

Default values

Using the default(Type) function, you can retrieve the default value of the given type.
letnumber=default(int)// 0letstate=default(bool)// falseletcar=default(Car)// null
Void features a system, which allows to create custom default values for your own class types.
You should put a "default" modifier before the class declaration, and a default() method must be declared as well.
defaultclassCredentials {
stringusername, passworddefault() {
return {
username: "admin",
password: "12345"
}
}
}
// initialize credentials to the default value let credentials = default(Credentials)// { username: "admin", password: "12345" }// you can use the default function without an explicit type, if the variable's type is defined explicitly
Credentials credentials = default// a simplfied syntax is also available
default Credentials credentials

Null-conditional operators

Null-conditional operators provide null-safe access to members.
A '?' mark is placed before the instruction to make it null-conditional.
let profile = database.find("user")// the profile's balance is retrieved if the profile is not null, otherwise a default value is required
println(profile?.balance ?? 0)let person = getSomePerson()// might be null// the "walk" method is not invoked if "person" is null
person?.walk()// you can use null-conditional operators on nested members as well
person?.entity?.motion?.move()

Static variables

By default, variables live only in the scope of a method. Static variables however provide a way to statically store data in the method,
that is accessible for further method calls.
intgetIncrementId(){staticletcounter=0returncounter++}getIncrementId()// returns 0getIncrementId()// returns 1getIncrementId()// returns 2

Enchanted switch

In Void you can use the old switch syntax.
switch (status) {
case200:
println("ok")
breakcase400:
println("error")
breakdefault:
println("unrecognized")
}
However in most cases, this old design forces us to write a lot of unnecessary code, such as always having to write "break" after each cases.
Void's echanted switch allows you to simplify switch blocks and use them as direct expressions.
enumStatus {
SUCCESS,
FAILED,
UNKNOWN
}
switch (code) {
200 -> println("success")
400 -> println("failed")
else -> println("unknown")
}
// you can merge cases as wellswitch (status) {
FAILED|UNKNOWN -> println("Unable to authenticate")
SUCCESS -> println("Authenticated")
}

Blocks as expressions

You can use code blocks as expressions, including loops and switches.
letstatus=switch(code){200->SUCCESS400->FAILEDelse->UNKNOWN}
StatusgetStatus(intcode) = switch (code) {
caseSUCCESS -> 200caseFAILED -> 400else -> 0
}
intcode = getStatus(SUCCESS)
// code is now 200

Method pre-processing

Void allows you to merge an instruction with the return keyword. Therefore you don't need two extra lines to do a negated method guard.
// previously you had to use two lines of code to returnvoidhandleCommand(Playerp,stringcommand){if(!p.hasPermission("use")){p.sendMessage("no perms")
return
}// handle the command}
// Void's syntax allows you to merge these two lines togetherif (!p.hasPermission("use"))
returnp.sendMessage("no perms")
// note that handleCommand returns void, so there are no conflicts returning something.// the return value of p.sendMessage (assuming it has one) is ignored

Method post-processing

In Void, you can queue post tasks for the method, that are going to be executed, when the method returns.
This can be used to prevent duplicating instructions whenever returning.
// prevously you had to do something like this// as you can see you need to call guard.unlock() 3 timesvoidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()if(myObject.someError){guard.unlock()return}if(myObject.someOtherError){guard.unlock()return}myObject.doSomething()guard.unlock()}
In Void, you can replace these duplicates using the "defer" keyword. Deferred instructions will be executed whenever the method returns.
voidmyThreadSafeMethod(){letguard=// get some lock for concurrencyguard.lock()deferguard.unlock()if(myObject.someError)returnif(myObject.someOtherError)returnmyObject.doSomething()}

Conditional variables

Conditional variables are local variables that only exist in the scope of the condition. This prevents keeping unused object in the memory, and unnecessary variable name reservation.
// previously you had to code something like thisletcreated=createFolder("myFolder")if(!created)returnerror
// Void allows you to simplify this code the following wayif(letcreated=createFolder("folder");!created)returnerror
When checking the isntance of objects, in older code practices, you had to first check the instance, then manually cast the value to the new type.
if(animalisParrot){letparrot=(Parrot)animalparrot.fly()}
However this code requires us to write an extra line of code and manually cast the value.
if(animal is Parrot parrot){
parrot.fly()}

Labels

You can name certain parts of code in order to allow jumps in scopes and nested loops.
// you may label your outer loop in order to allow "break" or "continue" on it.
myLoop:for(let i in1..10){for(let j in1..10){println($"{i}{j}")if(j == 5){break myLoop
}}}
// you can put labels to non-blocks as well// this example shows a loop jump to a section
hello:
doSomething()
if (condition)
goto hello

Multithreading

Void features an async value retrieval system. In other programming languages, you might have seen these ad promises, tasks or futures.
Future<User>getUser(){returnFuture.completed(myUser)}letuser=getUser().get()
Although Void lets you to explicitly declare future types, you should rather use the async/await syntax.
This automatically wraps the return type to be a Future, and calls get().
asyncUsergetUser(){returnmyUser}letuser=awaitgetUser()
You can create threads as well and have full control over them.
letthread=newThread(||println("hello")).setName("my-thread").setPriority(0)thread.start()

JSON in code

Void has a built-in JSON serializer, which allows you to directly map Void objects to JSON and vice versa.
// object to stringlet entity = @Json{entityId:100,meta:{
health:20,food:10,stamina:3},position:{x:200,y:4
z: -55}}let json = Json.serialize(entity)
println($"Data:{json}")// string to object
let data = "{ \"name\": \"admin\", \"userId\": 12345 }"
struct User{
string name
int userId
}
let user = Json.deserialize<User>(data)println($"Welcome,{user.name}")

HTML in code

Void lets you have HTML code inside source code. By default, tags are mapped with Void's built-in tag system, however this can be extended.
letpage= @Html{<divclass="container"><h1>Hello, World</h1></div>}

Bytecode in code

Void features direct bytecode instructions to be placed inside the source code.
int getSomeMagicValue(){let magic = 30
@Bytecodeunsafe{
ipush 100// push 100 to the stack
istore magic // store the value on the stack in the "magic" variable }return magic
}// the value of "test" will be 100let test = getSomeMagicValue()

Interaction with native code

Void allows you to call native library methods. It also has a framework that makes it possible to interace with Void from native context.
// get the implementation of the method from native code@Link("library.dll")
publicnativeintmultiply(inta, intb)
// call the native implementation from Void contextletresult = multiply(2, 6)
println($"Result: {result}")
// you can also link native methods of different signature@Link("library.dll")
@NativeTarget("bar")
nativevoidfoo(@NativeParam("int") inta)
// in case of having multiple native methods in a class, you should annotate the class instead@Link("library.dll")
classMyNativeImplementation {
nativeintfoo()
nativevoidbar(floatf)
@NativeTarget("baz")
nativevoidsomething()
}

Dynamic native implementation loading

letlibrary=newNativeLibrary().target(typeof(object))library.load()

Creating an HTTP server

Void's built-in http module allows you to create efficient web servers with the express-js design.
voidmain(){// create a new server instance which will take care of handling the request routesletserver=newHttpServer()// create a GET request handler for the "/" routeserver.get("/",|req,res|// respond to the request with a plain text messageres.send("Hello, World"))// start the web server and listen on port 80server.listen(80)}

Releases

Packages

Used by

Contributors

Languages