I like that the compiler can distinguish those two things too, so that I can refer to things within modules even if I happen to also have a local variable with the same name. Go sometimes annoys me because I need to rename a module or a variable just because the syntax doesn't distinguish between the two things.
zig: what's the difference?
In C3 it goes the other way despite having methods, and says "let's not allow static variables or methods".
This also goes hand in hand in the language approaches between open/close. Zig is very strongly "closed" and C3 is very much "open" (adding methods to types anywhere, appending to modules anywhere etc)
It's an interesting contrast that leads to very different code layouts.
unions and enums also create namespaces. any @import creates a namespace that is a struct.
If a function's first argument is the type of the function's namespace or a pointer to the type of the namespace, you may (and by convention are encouraged to) use dot dereferencing (like python firso parameter self) as a lexical sugar to call the function with the "implicitly rearranged" first parameter ("oop style caling, but not really oop")
modules are somewhat different, in zig these are collections of code you can map to a non-filepath import string using the command line or build tool (in particular you may map something out of code root path), but these too ultimately become a namespace struct
modules (might not mean the same as module in any other given lang) ⊂ imports ⊂ structs ⊂ namespaces ⊂ types
MyModule.zig
somefield: u32,
fn get(self: @This()) u32 {
return this.somefield;
}
main.zig const Module = @import("MyModule"); // note there are build parameters necessary to make this a module
fn main() void {
var x: Module = .{.somefield = 42 };
_ = x.get() // => 42
...
A module is an import external to your project. Whether or not the import has top level fields or declarations, the mechanism is the same for structs, imports, and modules. For all imports (including modules) you should imagine an implicit `struct{...}` expression surrounding the code in your file.Do you mean object like object file (to be linked)? Those don't have member functions as far as I know.
I'm using "object" as it is commonly used; essentially a value. In e.g Java or C or C++, the struct/class is a type, and an instance of the struct/class is an object.
Accessing an object's members is a different operation from looking up a symbol in a namespace. Even in Zig.
In C3 there is something called "path shortening", allowing you to use `foo::bar()` in place of something like `std::baz::foo::bar()`. To do something similar with `.` is problematic, because you don't know where the path ends. Is `foo.baz.bar()` referring to `foo::baz::bar()` or `foo::baz.bar()` or `foo.baz.bar()`?
It feels more lightweight and consistent, and collisions aren’t super common once you adopt some conventions.
It’s a tradeoff for sure, but this preference comes from having lived in both worlds.
Yeah, but in practice I try not to produce write-only code.
Code is for reading more than for writing, hence make it easier to read. If it becomes easier to write as a side-effect, then so be it.
The identifier on the right is looked up in the scope of the identifier on the left. If it resolves to a module, then it's a module. If it resolves to a function, then it's a function. If the left side is a pointer (not a symbol with a scope) then the right side resolves to a member.
It also makes refactoring much easier - changing a pointer to a reference does not require a global search/replace of -> with .
Also, "io", "file", "random" etc are commonly used variables, so the issue with shadowing is real.
`File file = file::open(...)` is completely unambiguous and fine. `File file = file.open(...)` on the other hand would be bad.
If the language had flat modules, or no path shortening, then it would be possible.
D uses a spell checker for undefined identifiers, and the dictionary is all the identifiers in scope. It has about a 50% success rate in guessing which identifier was meant, which is quite good.
> Also, "io", "file", "random" etc are commonly used variables, so the issue with shadowing is real.
If the same identifier is accessible through multiple lookup paths, an error is issued. If a local variable shadows a variable in an outer scope, and error is issued.
We've developed this over several years, and it works quite well.
Path shortening can be done with:
alias open = file.open;
or: import io: open;
In Rust where modules share a namespace with other identifiers, I just pick different variable names, or write my imports so they don't conflict. It's not that big a deal.
When Rust was in its infancy, I maintained a local fork for a while where I modified the parser to use the Ada syntax («'», a single apostrophe) for the same purpose. So
std::collections::hash_map::HashMap::entry
became std'collections'hash_map'HashMap'entry
But I am not convinced that using «.» for two distinct purposes – 1) as a name qualifier, and 2) as a method/data element accessor – is a good idea, for maintaining the semantic clarity is important, i.e. is «e» in «a.b.c.d.e» 1) A method call; or
2) An accessor for a deeply nested data structure.
It is not easy to say whilst just glancing upon the code.C3 has a lot in common with Odin, but very little in common with Zig.
C3 has a slightly different feature set than Odin (e.g. more compile time execution, has methods, contracts, but doesn't have Odin's matrix programming and more extensive runtime reflection), but the goals aligns strongly with Odin's.
If you prefer C syntax and semantics you might like C3 better, but Odin is a fine language too.
A more concrete example that might explain it better is looking at Advent of Code solutions.
One thing that struck me was that doing typical tasks for parsing would be 2-3 functions stringed together in a smart way in the Zig solutions, whereas in Odin and C3 it was achieved by having a single standard library function that did these steps.
From what I understand, there is a pushback against creating convenience functions in the Zig standard library, if the same thing can be achieved by stacking together a few functions.
My understanding is that doing these smart things with the Zig library with the existing functionality of considered a cool way to leverage existing code.
In C3, and I feel Odin as well, the lack of such a convenience function would be considered an omission to patch, and that having to stack things together should be reserved for specialized solutions, rather than having to stack things together for everyday tasks.
Thus in C3 and Odin, it is okay to trade detailed explicitness for convenience, whereas this is a no-no in Zig.
But what this means is that Zig users tend to celebrate and focus on smart and clever code, whereas this is a complete non-goal in C3 and Odin.
I could probably have formulated this better before.
But at the time there was little need for high level abstractions, and it was fine to just allocate an array with N number of entries and that was the maximum the program allowed.
Today we're dynamically allocating memory with intricate relationships, especially in object oriented programming.
This makes C look really hard to use – but it can actually be much improved with just an improved string library and a good dynamic array.
But there are also cross platform concerns for things like networking and here C offers little to help.
Regardless whether you're using Zig, C3 or Odin you're going to have a much easier time than C with straight up libc.
So I think a better comparison is that Zig is a bit like using the C++ containers. If you've ever struggled to mutate a std::vector while iterating over it, you know it's a bit complicated to figure out exactly what functions fit where.
The final solution might be performant, but it's a lot more to remember than say `iterator.remove_element()` that you might encounter in some other language. However, it does offer more ways to tweak it, and it's very explicit in what happens.
This is very much not productive and you’re now part of spreding this narrative. There’s plenty of people out there who has «figured out» and appreciate both Zig and Rust without becoming attached to it.
I’m interested in communities which looks towards other languages for inspiration and admiration, not judgements and alienation.
I don't find this an unfair judgment but rather an observation.
I think this naturally arises from the language claiming to be "a programming language designed for robustness, optimality, and clarity" (See for instance https://www.recurse.com/events/localhost-andrew-kelley)
If you feel that this is an optimal programming language that gives more robustness and clarity than other languages, then it's natural to be preachy about it.
This is similar to Rust being sold as safe language, where similarly the proponents of Rust feel that the advantages of Rust need to be spread.
As a contrast, Odin focuses on "joy of programming" as its main goal, and the author does not make any claims of the language having killer features to choose it over something else.
However, it seems to be successful in that new users tend to remark how pleasant and fun it is to program in the language.
> Also, there is a lot of "I discovered Zig and it's finally showing me how to program" echoed as well.
So, did you try Zig? How did you find it? Did it show you a new way to program? Or were you already aware of this way? Or do you think it's not a good way? What did you find interesting? What features did you steal because they were good? What do you think is overrated? These are the questions I'm interested in from other programming language designers!
> As a contrast, Odin focuses on "joy of programming" as its main goal, and the author does not make any claims of the language having killer features to choose it over something else.
And that's a fair thing to say! You can say that C3 is just a slightly better C and doesn't have any other killer feature. I'm just not sure why you need to talk negatively about other languages.
However, my impression was obviously coloured by being around 45 at the time and I was used to program in many different programming languages. Plus I grew up with BASIC, Pascal and C.
There's going to be quite a different experience for someone coming from Go/JS/Java and venturing into low level programming for the first time!
That is not to say that all of the people being enthusiastic about Zig is coming from those particular languages, but I think that C is considered a scary language for many people, so C alternatives tend to attract people from higher level languages to a higher degree than C itself.
When I eventually started on C3, I incorporated some features from Zig. I ended up removing all of them as I found them to be problematic: untyped literals combined with implicit widening, unsigned error on overflow, special arithmetic operators for wrap, saturation.
However, I am very grateful that Zig explored these designs.
From Odin I ended up including its array programming, but for vectors only. I also adopted Odin's use of distinct types.
But most of the C3 features are GCC C extensions plus ASTEC inspired macros.
Indeed, Zig has interesting features that make you think in ways you won't make when using C, like an ability to offload large amount of computation to comptime code, or using different allocators at different times (super simple arena allocation per a game frame, for instance).
"A language that's not changing the way you think about programming is not worth knowing."
Edit: Someone already asked: https://news.ycombinator.com/item?id=43572190
As for both C3 and Odin, they've been around for many years, yet don't even have a Wikipedia page and have relatively low numbers on GitHub. That comes across as more time spent pushing or hyping on HN, than those languages being considered a truly viable alternative by the general public. Just weird, because you would think it should be the other way around.
Talking about GitHub numbers, we can look at VLang, which had an astronomical trajectory initially due to overpromising and selling a language that would solve long standing issues such as no manual memory management but no GC needed etc.
Such viral popularity creates a different trajectory from organically growing word of mouth such as in the Odin case.
Vlang also has a Wikipedia page.
Is this then proof that it is a viable alternative to the general public? This is what you argue.
The use of negative catch phrases and envious put downs by competitors of Vlang has no bearing on the Wikipedia process. They will not care about any competition or politics among programming languages. The language either meets their standard and proves its case, that it should have a page, or not. Just like Zig, Nim, Rust, etc... have done.
This idea is what I criticize.
Not to mention that Wikipedia’s notability criteria is increasingly harder to live up to as tech news gets more and more decentralized.
It is not enough for notability that the Odin author is interviewed in various podcasts. It’s not enough for the language to be used in a leading visual effects tool and so on. These are not valid references for Wikipedia.
So how did Vlang achieve it? By commissioning books on the language(!). Once there was a book on V (nevermind no one bought it) it fulfilled the Wikipedia criteria. There are discussions about this on various V forums.
So let go of the idea that Wikipedia is proving anything.
The first book written about V (5 years ago), looks to have been a total surprise to the community and its creator, because it was written in Japanese[1]. The 2nd book written about V has clearly sold well and received very good reviews[2]. It's author had no connection with V development and there are interviews about him. A 3rd book[3], which was for academic circles, is not primarily about V. It, however, uses the programming language for random number generation and explanations on the subject.
The point I'm getting at, is marketing one's self on HN or podcasts as a viable alternative versus being an actual viable alternative used by the general public. It's one thing to act like or say your popular, it's another thing to be popular. There is a qualitative difference, that is even picked up on by Wikipedia, which is arguably why Odin or C3 don't have a page or the numbers on GitHub.
Despite any misdirected anger or envy, that problem has nothing to do with other languages like V or even Zig. It's up to fans and interested third parties of that language to show and report widespread usage.
[1] https://www.amazon.com/dp/B08BKJDRFR (Book about V (Vlang) in 2020 and in Japanese)
[2] https://www.amazon.com/dp/B09FKK3JL7 (Book about V (Vlang) in 2021, highly rated, and sold well)
[3] https://doi.org/10.52305%2FCVCN5241 (Book is part of an academic series, 2023, and uses V (Vlang))
First, "future prospect", is a claim almost any language can try to make. Unless it is a language created by a well known corporation (Carbon for example) or famous programmer (Jai or Mojo), such claims lack a foundation. A new language can really only make the argument of truly being a future prospect, if it comes from something already successful or famous.
Thus, for most newer languages, GitHub is a valid metric. Not just stars, but the number of contributors and activity associated with the repo. Other things like books on Amazon by third parties or articles about the language in well known magazines, would clearly count too. These things are measurables, beyond just hype.
Every language designer takes things they like about some languages and leaves things they don't like.
https://news.ycombinator.com/item?id=27441848
https://news.ycombinator.com/item?id=39503446
Many links paint a picture of constant false advertising, even deception.
That's why I said "communities" and not "languages". Every programming language has a wide set of people who use it. You can always find some people who constantly say bad things about other languages. You can also find people who are interested in the different trade offs of the language. I use languages which are technically interesting, and then I engage with the parts of the community which are interested in finding the best solutions to actual problems.
And guess what? Most of the Zig and Rust community are, in my experience, way more focused on solving real problems than to push their language at all cost. Both /r/rust and /r/zig will often recommend different languages. I mean, this was the most upvoted comment around how to convince someone's boss to use Rust over Python: https://old.reddit.com/r/rust/comments/14a7vgo/how_to_convin....
I think there's a difference between a critical generalization of a community and the mindset behind it and how that relates to the language (without weighing in on how legitimate that criticism is), and a direct accusation that one individual did a specific bad thing.
That was truly foul. On top of that, begged readers to give their money to Zig. Clearly some have no limits on what to say and do against other languages or to sell their language.
That's why whatever bad things a creator or evangelist says about another language, people shouldn't just swallow, and instead take with a grain of salt and some skepticism.
Is it because, as the leader of a language, he shouldn't be making "attacks" against other languages? Because, as far as V being a fraud, he was 100% correct.
Actually, yes. Not only from the angle of common decency or adhering to a code of conduct, but as a matter of professionalism and setting the example for followers.
> as far as V being a fraud...
That is a provably false claim from competitors, who should not be engaging in such activity.
Paying supporters[1][2][3] (ylluminate, gompertz, etc...) of the V language have even gone on record at HN, to clearly state such competitor or evangelist claims are false, and that they are happy with the language.
Not only can such competitor generated claims be seen as false, through direct V supporter refutation, but by the visible progress of the project as a whole. Over the years, the V language repo continually amasses thousands of stars and hundreds of contributors, that can be plainly seen on GitHub. It is a significantly large and successful project. To pretend or argue otherwise, is very disingenuous. People are there, because they like using Vlang[4].
[1] https://news.ycombinator.com/item?id=31801287
[2] https://news.ycombinator.com/item?id=31801262
> Focus on debugging your application rather than debugging your programming language knowledge.
Clearly, you think the language fails at this criteria (your subjective opinion). Please be honest and say that, rather than implying that it's not explicitly one of the core design principles of the language (objectively false).
it’s not at all “clear” from the comment that the author thinks what you say he does. what was said in the original comment aligns well with zig’s “only one (obvious) way to do things” and its explicitness. other languages offer a much broader vocabularies, higher-level overlapping toolsets, while zig is much more constrained and requires the user to do the work herself, hence “Zig fans like to wrestle with the features of Zig to figure out how to fit their solutions within the constraints of the language”, which is an objective fact.
I didn't mean to give the impression that I'm putting down Zig. It's more that I've noticed that people tend to frame problems differently with Zig than with Odin.
To explain what I mean by framing, consider OO vs procedural and the way OO will frame the problem as objects with behaviour that interact, and procedural will frame the problem as functions being invoked mutating data.
The difference isn't at all that stark between Odin and Zig, but it's present nonetheless. And clearly Zig is doing something which a lot of people like enjoy. It's just that the person using Zig seems to enjoy different aspects of programming (and it seems to me be in the spirit of "the challenge of finding an optimal solution") than the person using Odin.
[1] https://www.1a-insec.net/blog/25-zig-reference-semantics/ [2] https://github.com/odin-lang/Odin/issues/2971
Zig has SIMD vectors, but I frequently need 3D vectors, and refuse to use things like vec3_add(vec3_mul(a, 2), b) etc since I mainly develop 3D graphics software.
const meta = @import("std").meta;
test "vector add" {
const x: @Vector(4, f32) = .{ 1, -10, 20, -1 };
const y: @Vector(4, f32) = .{ 2, 10, 0, 1 };
const z = x + y;
try expect(meta.eql(z, @Vector(4, f32){ 3, 0, 20, 0 }));
}
Everything is element-wise, which matches what the shading languages do (mostly).But, yes, you won't get overloading allowing things like dot, cross, or scalar products. And I do miss the swizzle notation, but I don't think the language actually prevents that so it might appear at some point.
Given Zig's preference for closed modules, I don't expect this to be on the roadmap, but rather would need to be implemented as functions.
``` int[<3>] a = { 11, 22, 33 }; int[<4>] b = a.xxzx; ```
I assume that the `xxzx` is translated directly by the compiler. not seen that in any other language though ruby can fake it pretty easily via `method_missing`
I seem to recall that Jon just asked beta testers that they actually have a project that they will use it for?
I heard he only gives access to people who more out less guarantee they will use it relatively heavily, but how can I know of I will before I can scarily try it?
Basically I heard he only gives access if he thinks you will use it — not just so you can try it. That might work fine for people with enough time on their hands that they can say “I’ll do this full project in it, regardless of how it goes”, but I prefer to be able to tinker first to see if I find it suitable. For example, I’ve had projects where I though “ok I’m going to do this in Rust|Zig” and after trying for a few days realising that it’s not the route I want to take.
In any case, anyone can just download C3 and try it. You can’t just download Jai to try it. So my statement is still true.
I do all my coding in Python, but if I ever find myself needing to reach for C again, I'll certainly consider this.
EDIT: Though is there a reason why "fn" is needed? I would think the AST builder would still be able to identify the beginning of a function definition without it, and as a programmer, I can identify a function definition easily.
For ex. parens-less calls (myfunc 42 "hello") are elegant but don't stand out and - for me - take more time to identify.
Also `fun foo(i:int)` is easier on the parser than C-style `void foo(int i)`
That said, I will still try C3.
For example, let's say that for some reason macros were removed (this is very unlikely to happen, but as a thought experiment), then the symmetry between macro/fn definitions wouldn't be an argument anymore, and the question could be revisited.
Similar things have happened before: the optional type syntax changed from `int!` to the more mainstream `int?`. So why did I stick with `int!` for so long? Because initially it was called a "failable" and had different semantics. Revisiting this syntax after other changes to the syntax in 0.6.0, made it clear that `int?` was now fine to use.
So that's why I don't say never. But it would need for the situation to change in some way.
There is a C (single header-only) library[1] that determines CPU features at runtime (similar to that of what libsodium does), so I might try to use that with C3 and implement BLAKE2. That might be a good starting point, or perhaps even TOTP, my friend told me implementing TOTP might give me some insight into the language, but for that I will need base32 (I checked, it exists[2]) and SHA{1,256,512}-HMAC, which may not be available in C3, although there may be an OpenSSL binding already, or perhaps I could directly use OpenSSL from C3? The latter would be pretty cool if so.
Regarding the mentioned C library, it might not work, because it is header-only, and it generates the C functions using macros (see line 219). What do you think? Would this work from C3?
[1] https://zolk3ri.name/cgit/cpudetect/tree/cpudetect.h
[2] https://github.com/c3lang/c3c/blob/master/lib/std/encoding/b...
---
I checked base32.c3. I am comparing it to Odin's, which can be found at https://github.com/odin-lang/Odin/blob/master/core/encoding/.... Apparently they added a way to gracefully handle error cases. Is it possible to do so with the current C3 implementation?
Edit: I noticed "@require padding < 0xFF : "Invalid padding character"", and there is "encoding::INVALID_CHARACTER", so I presume we can handle some errors or invalid input gracefully. Although I prefer Odin's current implementation because you can handle specific errors, e.g. not just "invalid character", but invalid length (and others, see base32.odin for more). Any thoughts on this?
Additionally, what are the differences between
@require padding < 0xFF : "Invalid padding character"
and @return? encoding::INVALID_PADDING, encoding::INVALID_CHARACTER
exactly? If I want to handle invalid padding character, I would have to get rid of "@require"?Additionally, what if there are many more error cases? It would be a "long line" of available error cases? Could I use an enum of errors or something instead or something similar to https://github.com/odin-lang/Odin/blob/master/core/encoding/...?
BTW I like programming by contract, but I am not sure that all error cases that Odin has could have a "@require", and I am not sure if I would like to mix them either, because what if I have a program where I want to handle even "padding < 0xFF" specifically (in terms of base32.c3), or let's say, I don't want it to fail when "padding < 0xFF". Would I really need to implement my own base32 encoding in that case, then, or what?
---
Thank you for your time and help in advance!
macro String? encode2(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
{
if (padding >= 0xFF) return INVALID_PADDING?;
return encode(allocator, src, padding, alphabet);
}
Maybe a better place to ask these questions are on the Discord if you have an account: https://discord.gg/qN76R87The `@require` here is creating a contract. You can't give a padding that is 0xFF, that's a programming error. However, you might have data which is invalid – in that case the typical error is INVALID_CHARACTER. To pass in a too small buffer is a programming error since the output buffer must be calculated first, so that's why that is not an error either. This was a deliberate change to make the API tighter.
So it's quite possible to add an "INVALID_LENGTH" error, but that should only be there in case one does encryption / decryption where the length cannot be easily determined beforehand i.e. it's part of the unknown input which the function determines. But in the Base32 implementation this is not the case. Either use it with an allocator for it to allocate sufficient memory for the data, or calculate that the buffer you pass in is big enough (or run into asserts if it isn't)
BTW my last question still stands, however, that if there is a C library that is only a single header file that implements functions through macros, can it be used from C3? In C, for what I posted, you would need to first do "#define CPUDETECT_IMPL" and then include the header file. Could it be done from C3 somehow? As in, could this (or any) single-header library be used from C3?
And regarding the errors, can I have something like "Error" (in Odin), or an enum of errors? Sorry for this silly question, I realize I will have to read the source code of the libraries first.
Thank you for your help!
For that reason it's not an option for the standard library, but can certainly be useful for programs and libraries.
For faults, they are usually defined with `faultdef` which allows you to define one or more faults:
faultdef SOMETHING_WENT_SIDEWAYS, BIG_OOPS;
Then you use them as if they were constants: if (x > 0) return BIG_OOPS?;
If they are defined in another module, say "foo::bar::baz", then: if (x > 0) return baz::BIG_OOPS?;
Using path shortening "foo::bar::baz::BIG_OOPS" would also be valid, but is not necessary nor recommended.Is it possible to do something like:
faultdef {
FOO, // comment
BAR, // another comment
BAZ, // yadda
// Maybe this would work, too (for docgen if available)
QUX
}
or something like that?Does C3 have a way to generate documentation?
faultdef
FOO, // comment
BAR, // comment
QUX; // comment
There are third party tools to generate docs. You can also get some json output from the c3 compiler itself to base docs onAlso if you have 'tourist guide' for gamedev people please share links or short tips. I have no idea about C replacements myself because gamedev that I know is not using C.
ps somehow in my part of the internet your blog is not accessible with cloudflare saying access denied. Do you have any copy/backup of it elsewhere?
Some links
https://c3-lang.org/getting-started/
https://c3-lang.org/getting-started/design-goals/
https://c3-lang.org/faq/compare-languages/
The blog is on the internet archive https://web.archive.org/web/20250330145704/https://c3.handma...
There is not much of a guide I'm afraid. I translated some of the raylib examples to C3: https://github.com/c3lang/c3c/tree/master/resources/examples...
But those are straight up conversions of the original C code.
I think any C tutorial on gamedev would work fine to follow in C3, and then one can leverage features of C3 as desired.
More low effort questions. Question about optional debug traps: are they available in optimized builds? Are any LLVM sanitizers available? It usually means language needs to have APIs to mark up your own allocators/containers/etc.
Does language help parallel/concurrent programs to be more readable?
Debug traps can be kept in optimized builds, you can always do something like "-O3 --safe=yes". Right now it's not possible to set it per function or module, but that might be added if there is demand for it.
Because C3 also has contracts, the plan is to step by step increase static analysis to catch more of the contract violations at compile time.
For example:
<*
@require a > 10
@require b < a
*>
fn void test(int a, int b) {}
Will error at compile time for `test(5, 2)` (first @require), or `test(5, 10)` (second @require). These will also be checked at runtime, but catching low hanging fruits a compile time will be very helpful I think.LLVM address and thread sanitizers work. The memory sanitizer still has some issues but will be fixed.
Currently there are just cross platform threads available and some rudimentary thread pools. This needs to be expanded and we'll see if there are things the language could help with but there will not be any async built in.
TLDR (imho) C has failed to define pointer provenance but had to acknowledge its existence. LLVM and GCC have their own ideas on it which means their open issues on it are different. For practical languages aiming to be useful in embedded ie Rust there are complex workarounds: https://doc.rust-lang.org/std/ptr/index.html#exposed-provena.... What is relevant for C3 is that it is likely to be exposed to open issues from LLVM and people will need practical ways to solve them.
ps Don't take my word for it. My tldr might be wrong because of my gamedev experience (like we dont ship products with GCC).
Contracts are available in safe mode, which can optionally be enabled for optimised builds as-well, yes.
> Does language help parallel/concurrent programs to be more readable?
There's active interest in working in this area, if you're interested to help you're welcome to suggest or contribute.
There are some syntax choices that aren't ones I'd have made (eg I prefer `ident: Type` and types being uppercase, I don't like `fn type identifier ()` for functions etc), but coming from C, I can see how and why it ended up like it did, and overall this looks really good. Great work!
It is a pretty big improvement on C without changing the ABI. Maybe not the improvements I would make if I was smart enough to make a compiler, but better than doing C which I also enjoy despite it's warts.
Also I just checked the source code of hash map. What if I want to use a different hashing algorithm for "rehash"?
There is no one true implementation of a hash table either, for example, so I am not sure what to do with that. I want a thread-safe hash table, I wonder if it would ever make it into the standard library.
As for HashMap you are completely correct: there are many different types of maps that are needed. Concurrent maps, insertion ordered maps etc. And even variations of the small things: are keys copied or not!
I talked about this on a stream recently, how the standard library is in need of a lot of additional Maps, Sets and Lists.
So if you’re interested in contributing then you’re very welcome to do so.
Yeah, or if duplicates are allowed or not, initial size, load factor, and so forth.
Asking because my above question and this current post about C3 are related to this recent post by me, which had a good number of comments:
Ask HN: What less-popular systems programming language are you using?
Going to C++ competitors there is obviously Rust, but also Nim, Crystal, Beef and a lot of others. (And Jai is a C++ competitor too)
This is maybe more striking when comparing against C which was already very old and battle tested in 1989 when it was standardized. If you pick any of these languages you're accepting an unknowable amount of churn. It better be worth it.
C has plenty of issues too, even though it is very old, battle tested and standardized.
(How about all the UB in C, for example?)
That is why people are looking for C alternatives in the first place.
>It better be worth it.
It better be worth it for C too (and for C3; pun unintended but evaluated).
The notable commonality of these "C alternatives" is that they are still moving targets and yet that wasn't mentioned.
It's quite common for a project to pin to a stable version so this doesn't seem too unpredictable.
What I would like to see is a language that is essentially just C with the major design flaws fixed. Remove the implicit casting and obscure integer promotions. Make spiral rule hold everywhere instead of being able to put const at the beginning of the declaration. Make `sizeof()` return a signed type. Don't allow mixed signed/unsigned arithmetic. Make variables/functions private by default i.e. add `public` to make public instead of `static` to make private.
Keep the preprocessor and for the love of god make it easy to invoke the compiler/linker directly so I can write my own Makefile.
C3 (link[2]) is a fork of/inspired by C2, which appears to have incorporated a lot of Odin and Jai "flavoring". In the case of both C3 and Odin, it can be argued that part of their popularity is that Jai isn't publicly released. Consequently, they seem to pull in a lot of the crowd, that would be attracted to Jai. Another aspect of this, is the more C3 promotes itself (whether intentional or not), the more likely C2 will get faded out. Many will likely think C3 is the next iteration of C2 or simply know the name more, because pushed on HN and other social media.
C2 is over 11 years now. C3’s recent breakthrough this last half year is unlikely to have had much impact on its ability to grow the last 10 years.
If your programming language had a different name, then I would agree, but it doesn't. Many will assume that C3 is the next iteration of C2 and that it's outdated, despite the fact that C2 is still in development.
[1] Bas van den Berg, the author of C2.
While this rule has become somewhat diluted when C developed and gradually took on features from C++ (like types in function parameters), it's still very helpful to understand the guiding principle. (But those inconsistencies that crept in over time are also the reason why newer languages don't do that anymore).
1. Generics / templates
2. Destructors
3. Ownership
It is unfortunate that this only has the first one. There was a language called clay that had all three and kept easy integration with the C ABI, but it seems like that design has been lost.
Any RAII language will be considered a C++ competitor, not a C alternative. None of Zig, Odin, C3 or Hare has RAII.
It sounds like you are leaving tried and tested simple advancements in programming on the table just to avoid a superficial comparison.
Even then what C++ people want from a competitor is simplicity. Not to give up the essentials but to keep the crucial aspects that they can never give up and are the reason they put up with all of the complexity of C++.
I think when people want C semantics they will just use C in general, but if there is something that solves these extreme pain points in a simple way it might be enough to get someone to switch.
For scoping files, sockets and so on, there's actually a simple mechanism in C3 that allows creating code like that very easily using macros with "trailing body". Which is a little like trailing closures in Swift, but here there is no closure, just a macro.
There is an example here: https://c3-lang.org/generic-programming/macros/#trailing-blo... but it's used in the stdlib quite a lot.
How much have you used that? C has that and it will get cleared at the end of a scope too, but it is pretty exotic because it is so fragile. It's a catch-22, if something is so big that you need to allocate the exact size to save stack space, then it could become problematic and blow the stack. This is not a substitute for having allocations be freed at the end of a scope.
For scoping files, sockets and so on, there's actually a simple mechanism in C3 that allows creating code like that very easily using macros with "trailing body". Which is a little like trailing closures in Swift, but here there is no closure, just a macro.
I think stuff like this is interesting, but it's interesting in the context of straight C, not in the context of a new language where it could be done better.
In regular C you could already create a custom allocator and macros that would use the fact that a for loop will run something after the scope finishes to mimic some of these effects.
C's syntax may be a little funky, but it's real pain point is the actual semantics that will bite people over and over until they move on.
There are other contexts for managing mutex locks so they auto close and you could dream up one for database connections and transactions too.
The nice things about a context is you can always see it's there, destructors by design are a bit hidden which can make code harder to reason about.
I have never heard anyone say they are hard to reason about, especially if there is no garbage collection or inheritance.
Then how do you express read-only pointers ? Like C `const int* ptr`
#if defined(__SunOS)
presult = getprotobyname_r(proto,&result,tmp,sizeof(tmp));
if (presult == NULL)
return luaL_error(L,"protocol: %s",strerror(errno));
#elif defined(__linux__)
if (getprotobyname_r(proto,&result,tmp,sizeof(tmp),&presult) != 0)
return luaL_error(L,"protocol: %s",strerror(errno));
#else
presult = getprotobyname(proto);
if (presult == NULL)
return luaL_error(L,"protocol: %s",strerror(errno));
result = *presult;
#endif
The sometimes annoyingly small differences between platforms.There is both `$if` and `$switch` compile time statements for this: https://c3-lang.org/generic-programming/compiletime/#if-and-...
At the top level and `@if` attribute is used to achieve the same thing: https://c3-lang.org/language-common/attributes/#if
So the macros and compile time execution occurs after parsing in C3, but in C everything happens at lexing, before the code is parsed.
$if $defined(C3_V2_PLUS):
// new feature
fn f = \ -> foo(); // this won't parse in v1.0
$else
Callback f = ...;
$endif
This is also an issue if a future version of C3 introduces language level support, allowing newer compilers to compile code as if it were written for an earlier version. While this approach works well when teams standardize on a specific version’s feature set, they may still want to take advantage of performance improvements in the latest compiler.That said, this is a niche case and not something I’d consider a dealbreaker.
You just need a CLI option like java's --source, where you specify the source compatibility with different language versions.
What would you like to use it for?
Imagine using it on a 6809 system for example. :)
You can also extend the standard Library from inside your own project, modules are open for extension