The two extremes are the bulkiness & rigidity of C/C++ modules and the unpredictability & granularity of JavaScript libraries:
- In C/C++ compiler-coupled header files evolved from serving as an interface with an OS to unreadable, impossible to refactor and often redundant high-level precompiled libraries;
- In JavaScript the dependencies are updated at runtime with all the new versions of its minuscule libraries. As a result, a year-old code will certainly be crashing down on you.
The solution is having a modules and a build subfolder in your app folder preserving the compatibility and putting the control in the hands of a programmer instead of a PL or an OS.
The meta-programming options for module handling that Jai offers is a compromise between updating system headers and using your modules subfolder.
After the Beta Release, different options for module handling will, in part, make it easier for people to exchange module code and also write meta-programs to download modules from a server.
It would be helpful to compile only the modules that are being used from both the internal and external libraries. This would require parsing everything, which can be fast, as opposed to type checking and code generation, which take longer.
While compiling a meta-program runs to check the program with minimal compiler warnings. The warnings are implemented as a user-level library.
As of now, some basic things are not checked for, though, such as implicit casts:
// Declare & assign a variable with type inference
zork: 69105;
// Assign a variable
zork = 69105;
// Common mistake: declaring & assigning a new variable,
// instead of assigning an outer-scope variable
zork: string = "in-joke";
if {
zork := "69105";
}
Allocators are functions under the Context Base struct (program context) which is defined in one of the compiler modules — Preload.jai. The default allocator has enough arguments to do free malloc, realloc, free the entire arena, add tracking to the allocation you've just made, etc. The modes supported now are ALLOCATE, RESIZE and FREE, but you can extend them with values of your own and write custom allocators.
Unlike custom allocators in C/C++ which imply changing all the source code, Jai allows to create wrappers of the default allocator and specify the necessary allocator while making the same alloc function call.
These are on the list, but not officially supported right now. However, someone on the pilot team at one point managed to target the ARM64 Platform and it might still work if you provide the right LLVM target triple and build options.
It definitely shouldn't go open until the core features are implemented and until certain compiler bugs stop being reported, especially because of compile-time function execution, object packaging, etc. Before the Open Beta, there will probably be invite codes distributed by Private Beta users.
Send them one at a time to Jon's e-mail.
For instance, if you have a bunch of C files in a folder, they don't make a C program, because you also need a definition of what these files are to compile them. So, now you need a makefile and for that, you need the Make language, which often isn't enough, so you need a couple of other programming languages just to do your C programming, not counting transferring to other OSs. This is exactly what downloading all the modules from the Internet before compiling is, and if they go missing or suffer an unlucky update — there appears a question mark in your program.
In an ideally specified language, though, all you need to compile is a program and a compiler.
Just like an object, a closure is a certain instantiated thing with certain data that has its own separate lifetime. And, honestly, with these costs, it's not worth doing if you can help it. However, it is still on the roadmap to provide closures in the future and avoiding what C++ did.
In C++ a closure is essentially a functor struct with an overloaded parenthesis operator, which means it's not actually a function type, so any time you need a negative function or a closure, you need that special thing. And, of course, in C++ a member function is also a different type, so you have C-style functions, member functions and closures which are totally different things.
The way to avoid it is by having the standard function type in a language be a fat pointer so that every function has a data pointer that it can refer to. For example, talking about allocators, you always pass an allocator and allocator data, because you might have several different heaps, the same allocator as a heap-handling function and allocator data as a pointer to the base of a heap, which is different for all those heaps. So, even without closures very often you want to have a data pointer be a function pointer type, which is also true with thread safety in general.
Historically, in C if you wanted to do Q-Sort and you needed more data, you would make your Q-Sort function read global variables with extra values, which is bad for thread safety. So you put the extra data into thread-local storage, which is a hit on performance and not without its own error tendencies.
- Autocast;
- Dereferencing pointers;
- Implicit Casting: adding an implicit pointer dereference, which isn't scary with a compile-time check for it, just like an array bounds check;
- Later on, the plans are to do a "Question Everything" approach to syntax, especially in places where most mistakes occur.
The structure is to be figured out. Right now, the compiler distribution includes the Basic module with the compiler essentials and some side modules like Vulkan.
Here you start getting into the OS or workflow details that can go wrong. Someone could do it as a meta-program, the goal is to make such things as easy as possible.
Good code should be as general as possible and handle as many situations as possible, which also means that the code has to be as complex as possible. This reflects in libraries having too much redundant code. What should be done is combining the universality of simple code with the ad hoc complex code.
The idea is to make a macro or a function for that. First, the macro system has to have more powerful features, though.
The two options are having universal struct literal syntax for either the ones with static or with dynamic values. The current idea is to leave literals as is and later see whether to add dynamic initialization through a macro or build it into the compiler.
Not available yet as Thekla, Inc., apart from a Jai compiler, is currently developing a single-player Sokoban-type game, but it would definitely be great to have sockets together with a networking module some time later.
Single underscore is currently employed as a variable like in C, but could possibly be changed to serve as a blank identifier for junk values like in Golang, if enough people want it.
Typo fixes and decentralized content additions are very welcome.
Launching a Wiki is an option.
Procedure Declaration Notes could be upgraded to a strongly typed version of notes and not only be string notes, but also struct literals. With such an upgrade namespace collisions would be handled correctly.
// Procedure declaration notes can be put on declarations and structs
private_beta = true; @b1 // String note
open_beta = false; @@Type{Field: Variable} // Potential struct literal
Unannounced, but brewing feature.
Thread functionality is currently based on function calls and there are no real plans to change that.
Different languages have different threading primitives and most languages optimize for solving simple problems, not for helping with hard ones.
The hard problem is how to actually use all of the processors' cores. If you do something like co-routines or the like — these are not high-performance constructs.
Concurrency can be a rigorous computer science term or it can be a way to avoid saying parallelism when parallelism is what you actually care about in all practical cases. This reflects in many languages having all of the concurrency primitives, but really no parallelism help at all.
Presently, no intention to add any of these. Scalars is really the point to stop, for all the rest you have structs. The 16-bit half-precision float is becoming popular on some hardware, however it doesn't matter too much right this second.
Jon uses Remedy to debug the compiler. Long-term goal is to provide some default visualization tools that could augment debuggers.
The custom graphics engine that has evolved through the production of The Witness and is currently employed for the development of the Sokoban game will be given out for free with the stripped down version of the latest game, enough to play around, but not to duplicate the game.
People complain about the type conversion compilation errors when porting games from C++ with the Autocast, which are caused by the syntax of " xx ".
There are several reasons for that default choice:
- Big enough not to worry about overflow;
- Enables to load 2+ GB files;
- Signed to avoid underflowing surprises.
That being said it is better to use smaller numbers in a data structure that gets frequently instanced.
One of the big mistakes of C is not being able to identify the size of an integer, for example.
LLVM Backend will remain a supporting part of the Jai compiler for the foreseeable future, even though its a giant, expensive cargo aeroplane of modules. In addition, as of right now, the LLVM does Auto Vectorization, but Hand Vectorization is still to be added.
The better idea is to avoid writing intrinsics and instead implement AVX-512 instructions by injecting some Inline Assembly into your Jai code.
Rust language is so intrusive because it requires all of the information to ensure memory safety. The solution for a less demanding safety checking at runtime is to replace your heap allocator with the one that puts every allocation on its own pages, and on FREE it turns off read permission from the pages so that there's no way of dereferencing it after freeing. You can do similar things for buffer overruns, the drawbacks are that these are often slow and paid-for.
There are module parameters already, they remain undocumented, though.
The default meta-program could be taking some options and there wouldn't be extra things built into the compiler making it much lighter. That could really solve some things.
You can already build libraries statically or DLL, same as C binaries. You would lose the power of the language, though. If a function is compiled, you can't polymorph it or bake the arguments and the meta program can't inspect it either.
Most modern languages think they are using all your cores to compile by compiling a bunch of separate object files and linking them at the end, but they are not. Most of the time what most cores are doing is waste.
To all the game industry, if you want your game to run fast, the last thing that you would do is take all your scene data from different parts of a scene, write it out to disk, have a separate process read it in from disk, put it together, draw it on a screen and try to do that at 240 times a second. Like, nobody would do that. But for historical reasons, we have decided that's the way to compile things and convinced ourselves that it's efficient as it provides parallelism.
For example, compile time of C++ is largely dominated by including massively redundant header files and then deduplicating that massively redundant information at link time. But even without that it wouldn't be too fast because it's writing all those files out to disk and then linking them.
Jai could be made faster still by parallelizing the compiler, which isn't easy.
You need a slightly more sophisticated tracking within the compiler for good error reporting.
C/C++ reports multiple errors, but most of them are garbage, not revealing the real problem.
To report multiple error messages correctly you need to know what you did as a stop gap in order to continue compilation in the face of the first error and just not report further errors when that happens.
Right now there are parsing report errors if they occur in different files.
Like if there's a hashbang at the top of a file, then the default is no output. A better solution is to set a variable that we started with a hashbang and the default meta-program can, then, set the build type to no output.
Yes, there are plans. Console vendors generally don't like someone talking about these things. The currently developed game under Jai will probably ship on multiple consoles.
Yes!
C++ sure suffers from a lot of bad operator overloading. However, there's also good overloading when operating on vectors and matrices.
Memory ownership on data structures was removed because you shouldn't have that many independently managed pieces of memory. It's bad to malloc a ton of nodes to remember membership or ownership about. It's much better to have temporary storage, where we don't worry about ownership, rather we track it.
Same as in C, it's a struct with a slot and an enum in it, which you happen to know. People who worry about discriminated unions tend to also worry about non-nullable references, which is probably the one to worry not having in the language right now. But actually that reference is going to be null at some point anyway.
If meaning to interpret x64 machine code, then — no. But if meaning to JIT compile and run that — it's feasible.
There are no right now, but they will be added.