A series of small test programs to figure out which of the languages I like best. I don't plan on dropping C++ but I'd be willing to replace Go with Zig or Odin if I like them.
I think C# has earned its place for me as a primary tool. I still love Go due to its simplicity so I'll still find use for it, especially for Wasm graphics demos since .NET wasm is too large currently, though if a future version of .NET wasm (or a js compiler) can produce small enough code I'll reconsider C# for browser graphic demos. Here are the pros and cons for me of Go vs C#:
+
Simple+
super easy C/ffi interop+
Easy and small distributable executables+
Feels like C plus+
Wails for UI is cool+
Fyne for UI is simple-
Wails for UI uses 4 langauges, html, css, javascript & Go-
Wails (and most webview based uis) on mac has startup flash/flicker-
Fyne doesn't support topmost/always on top windows
+
flexible distribution with .NET reliant programs which are very light and also self contained executables+
LINQ is cool+
Blazor is cool+
MAUI is cool+
Avalonia is for me, the best cross platform gui framework in any language+
Unity+
Godot+
ASP.NET-
Non Native self contained .NET exe's are about 5 times the size of go-
wasm is currently very large at around 4 Mb?
always adding new often useful features, maybe the most rapidly developing language and platform. This could be good or bad but so far I like the direction its headed so mostly positive for me, but it does make the language/platform harder and harder to learn.
I have use cases for both languages. If C# is too slow/large as in the case of wasm I'd probably use Go and if that is even larger/slower than I want I can always use C++. I think between Go, C#, C++ and Javascript, I've got most of my interests covered
C# enters the competition. I've been keeping an eye on .NET and C# for a while and the last few versions of .NET have been focusing on some very cool features that I've been waiting to test, namely: Wasm, Top Level Statements (C# 9) and new Native Aot with .NET 8.
The Native AOT is very cool allowing C# to be compiled into small self contained executables and brings C# closer to Go in terms of distribution. The coolest feature for me personally is that Native AOT exes can not only call into foreign libraries but actually statically link native libraries as well. This makes C# a compelling choice especially for me, due to the .NET MAUI and Avalonia libraries for GUI programming.
I love C++. Despite its ugly bits, its still my favorite. And programming successfully in it makes me feel like a superhero even with trivial programs
Even though it was eliminated early on, I will probably still use it for small tools using Webview because its networking support is just too good.
Go is un-eliminated after rewriting the opengl example directly against C myself and I think that after using it for a few projects out side of this where in one case it actually replaced a previously C++ version, it might be dethroning C++ as my favorite.
Zig is super interesting and I definitely want to explore it further. There is an imporant note on the compile times I recorded. Zig was consistently significantly slower than the others, however I actuallys poke with Andrew Kelly the creator of Zig on discord about this and He mentioned that the compile times will dramatically improve when the self hosted incremental compiler is finished which is about 66% of the way there (as of 02/13/2022). Despite its verbosity I really love the language's approach to memory management (via allocators) and error handling. Also Zig can be used as a cross compiling C and C++ (and I think also Objective C ) compiler. I've even read a couple Go articles in which Zig is used to cross compile Go applications using Cgo. It has a cool build system using the language itself as the build language. I will absolutely be playing with it more in the future.
Odin is also awesome. It feels like the most high level out of these due to its syntax. It isn't as far along with some things like networking but it has great bindings for many game and media libraries out of the box (though on mac I had to do some manual building). Its pretty cool and I would even consider replacing Go with it since superficially they are similar. In fact once Odin gets standard networking I will revisit and consider replacing Go with it. Also I complained a lot about Odin's strong typing and strict casting, however of course this was greatly exaggerated by my misunderstanding. You can mitigate the casting by precasting things that you will use a lot, and also by using constants wherever possible as these are "untyped" and coerce to other types easily
I added D during the platformer task and found it is interesting. In many ways it does improve on C++. But its kind of a pain to use in wasm which is important for me, so I won't be using it further.
I added a weird language called Janet recently. Janet is a lisp like language which can be embedded in and extended with C, similar to Mruby. I had come acrossed it many times before but just wrote it off as another weird Lisp, but recently (Late Feb 2022) I've been revisiting Lisps thanks to Bagger's awesome video of recompiling a 3d demo as it runs. I began learning Common Lisp with SBCL and had intended to use Emacs and Slime/Swank for development but... I detest Emacs. Ha ha, the bindings are just too unnatural for me coming from Vim. I know there is the Spacemacs plugin to make it more like vim but I couldn't get it to work on Mac Catalina. And so I decided to give up on Lisps for good as my thinking was that Emacs it the best Lisp environment and if I can't use the best environment I'm just not going to use it at all. Well I came to Alive in VSCode and a project called Conjure for Neovim and these worked well and were easy to set up.
Janet is much simpler and smaller than Common Lisp and this was attractive to me. And so I decided that if I could get live reloading for Janet working like Baggers showed in his Lisp video then I'd use it. Well Janet has a package called spork which contains, among other things, a SWANK like server for Janet. And so spork along with Conjure and Neovim allowed me to have my live reloading development, and so I decided to learn Janet.
Janet is awesome. Once you get passed the strange S expression syntax is it very fun. Of course complex expressions are still hard to read but to me its other merits are worth it. It comes with networking, coroutines/fibers (green threads) and real native (OS)threading, with (to my knowledge) no GIL. To me the final feature that made Janet really worth using is the fact that using its easy to install package manager jpm you can compile your project to standalone binaries. And the size is quite reasonable for a non systems language: the Pong example using Raylib is about 2.4MB on Mac OS Catalina. Additionally it has a great set of bindings to Raylib called Jaylib which map very closely to the C api, much closer than the Commmon Lisp bindings I found. Since Janet is a small virtual machine that can be embedded in C, it should be trivial to compile it for the browser via Wasm and Emscripten, and the official documentation even mentions this. Also in the gitter channel a user mentioned that he has successfully used the Jaylib bindings with emscripten. It has std library socket wrappers and an "official" community http server. It even has Webview bindings, however I may write my own since these are very old and because I maintain my own fork of Webview.
Overall I really liked Janet and I think it will be dethroning Go as my secondary language.
I learned more about and worked with Janet last month. Toward the end I ended up writing OpenGl bindings which were quite capable though not complete. However I found compiling to wasm much more difficult than I originally thought. Compiling Janet to wasm via emscripten is quite easy, however my OpenGL bindings are a large project with many submodules and the Janet C embedding api doesn't have support for preserving this module structure when exposing bindings, that is to say that all my gl functions would be exposed in the global scope, meaning my module imports wouldn't work. The workaround is to create a dummy project structure that imports all the functions from the global scope in the proper modules, however this was much more than I wanted to do. I really liked Janet, but this event led me to abandon the gl bindings and go back to Go which I'm quite happy with now.
Added swift on 6/1/22. I forgot about swift during the project. Swift has made a lot of progress towards cross compatibility and as of 2022, Swift runs on Mac, Linux, Windows and Wasm. On Windows a lot of effort has been invested to allow Swift to interop with the win32 api and so completely native applications can be written in pure swift for both Mac and Windows using their native bindings. Swift is not garbage collected, although technically it is since reference counting is a form of garbage collection, but it wont' suffer from GC pauses. It is a proven language, it is obviously used extensively (almost exclusively) on Mac OS for everything from Applications to games. I imagine that these days virtually all games are written in Swift for IOS and so it is a proven games programming language even though I've never seen it mentioned in discussions about games languages. I assume that Swift can't run on any consoles currently and this might be the reason it is not mentioned in game language discussions, but then again Python is quite popular amongs hobbyist and no one is even thinking of running Python on consoles. It can't cross compile like Go or Zig unfortunately. It is really nice and looks and feels like a GC'd scripting language but compiles natively. It feels a lot like Rust without the borrow checker, and it predates Rust as far as I know.
Swift is fun and it was really nice to use. However I think the thing that might hold it back for me is that even though it is supported on Windows and Linux, I don't know of any cross platform networking api that would allow for tcp or udp servers. Though its great C interop does mean that something like SDL_Net, Enet or Mongoose could be used
Nim is pretty cool. Its similar to Go in that it has a lot of libraries included. I'd say that interop with C is easier in Nim because the types map more closely to C. In Nim an array is the same as a C array so there is no funny business with wrapping unsafe slices like in Go. I'd say that Nim is in general better than Go for graphics, however nothing I've tested so far can beat Go's easy concurrency. So for everything except highly concurrent networking I'd say that I actaully prefer Nim over Go. Also Nim's module system is in my opinion better than Go's. Go's system requires specific file layouts and when testing a repo on github and pushing and pulling I found that I had to make sure Go wasn't caching the changes and used the latest commits which I forget how to do often, however with Nim its like Python, you just import the file and its the file that is in the import path. Nim is more complex language than Go in terms of what you can do, but in practice I found that the complexity of say macros don't really seem to show. If you're learning macros they are quite complex but they exist in many places and can be used without necessarily knowing how they are implemented. Back to Nim's C interop, you do have to write bindings, however the c2nim tool which can be installed via the nimble package manager is excellent. I was able to generate nim bindings to the emoon's minifb library very easily and the only thing I needed to do manually was insert importing code for a single struct, and then embed the linking flags. I'd say this is better than Go because even though Go can just include C with cgo, interacting with that C from Go often involves trickery due to Go's view of C as unsafe and because Go has strict rules about passing memory back and forth with C code.
First class compilation to Javascript is cool.
The funny thing is, I'd say that overall Nim is a better language than Go when it comes to interop with C, maybe syntax aesthetics if you care about that (I don't really), and definitely its module system, however Nim also feels really generic. It is definitely a general purpose language and doesn't really shine in any area. Go shines in networking, concurrency, compile times and simplicity and even though it brings some irritations and annoyances with things like its C interop, at least it has a reason for being. Nim is a cool language dont get me wrong, but it doesn't really feel like it has a reason to exist. Nim makes most everything easier (at least easier than C and C++) but it doesn't necessarily make anything awesome. I'd say that Nim is good for people who want a compiled language and are very concerned about how code looks. I mean I have to say I do think Nim is the best looking language of the lot.
Place | Language | Notes |
---|---|---|
1 | Odin | Odin has built in libraries for Opengl, Metal, DirectX 11 & 12, Vulkan and WebgL 1 & 2. The Language also has builtin in array component wise operations which can be used for basic vector math as builtin in Matrix and Quaternion types and libraries containing all the game math you'd have to install as a separate library in every other language. It has builtin bindings to SDL net and Enet for networking. I don't think wasm with Odin and OpenGL isn't too difficult, you should just need to use the WebGL libraries instead of the OpenGL ones which should just be a matter of conditional imports. I haven't tried this specifically but I'm confident it would work for the most part |
2 | C++ | Obviously pretty much all the libraries are written in C or C++. The only reason it isn't number 1 is because with Odin link flags are embedded in the libraries so you don't have to worry about build flags like in C++ and because Odin has built in support for Vectors, Matrices and Quaternions. But if you don't mind writing simple makefiles, and either writing or getting libraries for things like vectors and matrices C++ is of course still great. Also C++'s wasm support works with OpenGL and SDL with pretty much just a change of compilers, using emscripten instead of the normal compiler. Emscripten's downside for me personally is that you get a large bundle. When I say the bundle is large, its not so much about size but complexity, emscripten does things a specific way. Its still very easy to use and in fact is the best for cross platform graphics from desktop and web |
3 | Nim | Not a whole lot ot say except that Nim's C interop is very good so working with graphics apis is also simple. It does have a very comprehensive library NimGL which provides not only OpenGL bindings but also: GLFW, Vulkan and ImGui. For the web, Nim works with Emscripten since it compiles to C but Nim also has first class compilation to JavaScript and the front page of the Nim site actually has a snake game written in JS by the language creator |
4 | Go | Go is pretty good once you get past some of the setup for its C interop. Also you can make talking to C easier by using C types instead of Go types. For example if you want to pass some floats to openGL its super easy if you use C.float instead of Go's float. Go also works well with Wasm and even its concurrency works with both GopherJS and TinyGo |
5 | D | D could arguably be #2 either tied with or displacing C++ if we're just talking about Desktop without web support. D's dstep tool and importc language feature make interop with C pretty easy. It has componet wise array operations similar to Odin which is nice. For me the big problem with D is that its wasm support is terrible. You can really only use it with betterC which is D's limited mode that restricts it to an enhanced C subset. When I was asking questions about using D's betterC with wasm one discord user kept refering to it as worse D, which might sound negative but is 100% accurate. Many of the things that make D good don't work in better C. Overall using D on desktop is great, but its terrible for the web and wasm |
6 | Zig | Zig's C interop is great, actually for the most part its almost as easy as C++ and it also works very well with wasm. I think the main draw back for Zig with graphics is that its syntax for casting is very verbose and it becomes a bit of a chore |
Place | Language | Notes |
---|---|---|
1 | Go | No surprise here. Go's networking is the best, not just because it has so many built in libraries specifically built for networking, but also because its concurrency is so simple and efficient |
2 | Nim | Nim's async and multithreading are not as polished as Go's coroutine concurrency but they are simple and work. Its networking is also pretty simple. It doesn't really stand out as being particularly interesting but it is straightforward and straightforward is good |
3 | D | D has built in sockets and the higher level vibe D library for http servers is well documented. D also has decent parallelism constructs. Not a lot to say as I haven't actually used D for networking, I just looked at some of the documentation and tutorials. Though I haven't actually done any D networkign myself, just from the examples on the Dlang site and Rosetta code I can see that it is very simple to use |
4 | Odin | Despite not having standard networking, Odin has built in support for SDL_Net and Enet. I haven't used Enet but I did use SDL net to write Odin's echo server example |
5 | C++ | Of course you could use Unix or Windows sockets if you don't care about cross platform support, but Asio is really cool. Its more verbose than the options above, but it does give C++ cross platform networking. It also serves as a general purpose async runtime for C++. It was pretty simple to use and the rewrite of the echo server with Asio was easy to put together |
6 | Zig | Zig's networking is actually very nice, but its not documented and will probably change until 1.0. Andrew Kelley, Zig's creator says that proper documentation will come at 1.0, which is a reasonable plan. I think once Zig's networking is documented it will easily move up this list. Zig's async stuff is very cool but not complete and more complex than Go so I don't think I could place it higher than #2 |
lower score is better. No scores for the trival programs like Battle and Guess Number
lang | ppm & png | http & json | opengl | echo server | build system | platformer | pong | total |
---|---|---|---|---|---|---|---|---|
c++ | 1 | 2 | 2 | 1 | 3 | 1 | 1 | 11 |
go | 1 | 1 | 4 | 1 | 2 | todo | todo | 9 |
zig | 2 | 3 | 3 | 2 | 1 | 4 | n/a | 15 |
odin | 1 | 6 | 1 | 2 | 2 | 3 | 1 | 16 |
d | n/a | n/a | n/a | 1 | n/a | n/a | 1 | tbd |
janet | n/a | n/a | n/a | 1 | n/a | n/a | 1 | tbd |
nim | n/a | 1 | n/a | 1 | n/a | n/a | n/a | n/a |
c# | n/a | 4 | n/a | n/a | n/a | n/a | n/a | n/a |
- Guess Number
- Http and Json
- Langton's Ant
- PPM and PNG
- Text Battle Simulation
- Build System
- Echo Server
- OpenGL Triangle
- Mini Platformer - One Lone Coder port
- Pong