11 ms·
Show HN: C3 – A C alternative that looks like C
- lerno 5y agoFor the docs, have a look here: http://www.c3-lang.org http://www.c3-lang.org With a primer for C programmers: http://www.c3-lang.org/primer/ http://www.c3-lang.org/primer/ It should compile fine on MacOS and Linux. There is CI for Windows but the readme is missing install instructions for Win. This might be helpful to get it up and running on Windows: https://gist.github.com/kvk1920/57e1851d106bed86ded8c3232895744c https://gist.github.com/kvk1920/57e1851d106bed86ded8c3232895... I am happy for all the feedback I can get, even if you just hate it completely :D
- enriquto 5y ago> C3 has less undefined behaviour "Well, there's spam egg sausage and spam, that's not got much spam in it." sorry, couldn't resist. So far it seems a beautiful thing. Will take a deeper look and try to form an opinion. Great work!
- lerno 5y agoSome things are hard to get rid of due to LLVM, but I'm doing my best to reduce them.
- Alekhine 5y agoNot to shit on decent work, but I don't see a reason to use this over Zig. It's cool, though. Funny how we can get stuck with a language from the early 70s and we're still in the process of replacing it half a century later.
- laumars 5y ago> Funny how we can get stuck with a language from the early 70s and we're still in the process of replacing it half a century later. I see that as a good thing. I’d much rather have a battle tested framework than everything being rewritten every 5 years in whatever is currently trendy simply because a new generation of developers are suffering from NIH (not invented here) syndrome. I mean, yes C has its problems so I’m all for using safer languages, but just take a look at the mess that is front end web development and tell me that the alternative isn’t better. I seriously hope languages like Rust do evolve into being much more than a fad. I’ve been in the industry a fair few years now and have seen languages fall in and out of favour (Pascal, Java, OCaml, Go, etc sure someone of them are still popular but nowhere near as much as when their respective hype machine was in town) and really what systems development really needs is another ‘C’ — as in a language that survives the next 50 years as a standard low level language that people can build solid operating systems from. Whatever language people want to code on top of that base is then fair game.
- jarcane 5y agoA language cannot "survive the next 50 years" if people are too hidebound to refuse to let go of the current 50 year old language. You are asking for the equivalent of those job listings that demand "10+ years experience" in a tech that's only been out for 5.
- laumars 5y agoYou’re being rather unfair with your interpretation of my post there. I’ve clearly demonstrated in my writing that I’m in favour of other languages growing. There’s a huuuge gulf of difference between my point about praising platform stability and yours about saying newer languages would never get change to prove themselves. Actually what’s happening right now with Rust being trialed for Linux kernel development, which I’ve acknowledged when I said I hope Rust turns out to be more than a passing fad.
- lerno 5y agoTo me this says that C got a lot of things right. I've noticed that even things people complain about, like the somewhat arcane promotion rules to "int" makes perfect sense given the constraints and the expected type sizes. It's not until now that this is somewhat breaking, and that's because with 64 bits `int` didn't follow the normal trend of being register sized.
- lelanthran 5y ago> Not to shit on decent work, but I don't see a reason to use this over Zig. Maybe you don't mean to sound harsh but from their About page: " It is an evolution of C enabling the same paradigms and retaining the same syntax as far as possible." I don't think Zig has the same goal. Also, they usefully compare the languages: "In Zig but not in C3 Pervasive compile time execution. Memory allocation failure is an error. Zig's compile time execution is the build system. Different syntax and behaviour compared to C. Structs define namespace. Async primitives built in. In C3 but not in Zig Module system Integrated build system Built-in strings, maps, vararrays Optional contracts Familiar C syntax and behaviour "
- user-the-name 5y agoA lot of that latter list seems to actually be in Zig though?
- mtzet 5y ago> I don't think Zig has the same goal. It seems to me that the only meaningful difference in their goals, is that C3 wants to stay as close to C as possible, including having familiar syntax. > Also, they usefully compare the languages [...] The comparison seems disingenuous. Zig has a module system where each zig file is a module and it has a mechanism to specify public parts of the module. One of the standout features of zig is the integrated build system, which uses a reliable caching mechanism to allow incremental builds without rebuilding the world. It's somewhat remniscient of bazel actually. The zig std library features hashmaps and variable length arrays. Strings are not different from slices of u8. It's unclear to me what C3 means by "built-in strings". The only reference I could find was under "crazy ideas", where they vaguely refer to needing to figure out memory management. That seems like a pretty big outstanding issue for a C competitor. So the unique feature of C3 appears to be the contracts system of pre- and postconditions, which are only used as language hints to the compiler.
- naasking 5y ago> Zig has a module system where each zig file is a module and it has a mechanism to specify public parts of the module. "Module system" has many meanings. In functional languages, it has much more flexible semantics than just namespacing and data hiding declarations, so perhaps C3 means it has this sort of module system.
- flohofwoe 5y agoThe "inconvenient truth" is that C will never really be replaced, just augmented by "better C" languages, or at most wrapped away under language bindings. Even if Zig's goal is to replace C (which is an important motivation to really cover all use cases of C), I think that most non-trivial real-world projects will actually be mixed C/C++/Zig projects, at least for the next few decades.
- lerno 5y agoI would say C3 is closer to Odin than Zig. Given that C3 isn't done yet, I'd personally recommend using Odin over Zig. So there is a clear philosophical difference between the two. Aside of course from Zig departing from C syntax all over. Zig is in many ways a more ambitious language. Not just in the language itself but in the tooling. Unlike C3 it has the ambition to "do things right" - often by doing things differently. Sometimes that pans out, but sometimes not. For example Zig adds a lot of UB "to be fast", but there's a particularly worrying intersection of (a) UB in overflow, (b) introduction of unsigned overflow and (c) implicit type widening which adds a lot of hidden UB / runtime aborts. The simple example here is `a : i32 = b + c + d`. Even knowing that b, c and d fits in a short is not sufficient to guarantee this will not have UB. And even if we know that `b + c + d` does not trigger UB, we cannot guarantee that `b + d + c` does not have UB(!). The usual solution it to require explicit widening casts, but that solution seems hard for Zig due to relying on a lot of non-power-of-two types in the core language. I guess also it's a matter of how much you think C sucks :D I like C, and C3 is basically just trying to tweak a few things C can't change due to legacy reasons. It doesn't try to be a new "let's write everything from scratch because C is bad" kind of language, if you hate C then C3 isn't for you.
- mikewarot 5y agoRefcounted strings are a very good thing. I really like the idea of modules without the .h files littering every directory.
- lelanthran 5y agoCoincidentally, I am also designing my own "Better C" language and I deeply considered the benefits and drawbacks of modules and headers and headers, for practical purposes, come out slight ahead. Headers have some really unbeatable advantages over module interfaces: the tooling is incomparable plus the header serves as the API documentation. While modules are great, in practice you have interop problems and you still need a tool (if it is a binary file) to extract the interfacing API. Modules are better from an elegance and clean-design PoV, but headers win on the practicality.
- GoblinSlayer 5y ago>plus the header serves as the API documentation But then all macros go to headers, even private ones. Also inline functions.
- lelanthran 5y ago> But then all macros go to headers, even private ones. I don't understand what you mean by this; specifically, whats so special about macros? You can always define module-private macros in the .c file and not in the header like with any other symbol. Yes, headers do mean that if modules A, B and C have shared private information then that information is not private (as it is shared via a header), but it doesn't mean that A cannot have private information that is visible only to A.
- nicoburns 5y agoWhy not generate the headers from the modules, then users don't have to worry about them.
- lelanthran 5y ago
- 01100011 5y agoAs a long time C programmer I love seeing these sorts of 'better C' languages. I absolutely abhor modern C++ syntax, but there's one thing I think they deserve credit for. The C++ community is thinking deeply about first principles, consistency, composability, memory models and forward progress guarantees. I hope anyone looking to improve C learns from the longsuffering of the C++ world while avoiding many of their syntactical mistakes and evolutionary half-steps. I really hope the C++ standards committee has a well funded legal purse. In 20 years when the lawsuits start coming from all the ex-programmers battling brain tumors they're going to need it.</s>
- pjmlp 5y agoI think money from Sony, Nintendo, AMD, NVidia, Microsoft, Apple, Google, Microsoft, IBM, Intel, Codeplay, Facebook should be enough for those lawsuits. :)
- jstimpfle 5y agoIMO, what C++ brings to the table is mainly everything that follows as a consequence from RAII. And RAII, that's just some syntax to make writing badly structured programs practical. Because it does the tedious parts of matching "parentheses", it allows you to use a lot of them, without consideration if you could refactor the program to get away with fewer of them. RAII also comes at the cost of requiring or encouraging "features" like exceptions and all the good stuff like copy/move constructors and what not. All highly non-orthogonal language features that require lots and lots of special cases and extra boilerplate.
- amelius 5y agoC++ already improved C. For example, you can now declare variables in the middle of a code block. Or write "for(int i = 0;" These things were not possible in old versions of C.
- flohofwoe 5y agoUnfortunately the few improvements are overshadowed by many more deprovements, the most critical being that the C subset in C++ has been forked into its own incompatible C dialect which is no longer compatible with C99 and beyond.
- FeepingCreature 5y agoAaaa, people are doing a lot of C alternatives lately, I should really make a post for my C alternative before the field is completely saturated... But it's not ready yet :( I kind of want to at least get hashmaps in before I go public. Re C3, I think the README could do with more sample code? Not exactly "Hello World", but something to get you hyped about using it. Things I like: - macros! macros are fucking awesome. giv examples - modules are just a straight win - built-in dynamic arrays, yess. - compile-time execution is kind of a precondition for macros. Hopefully the same system. - I've always wanted to play with generic modules. `import foo!(int) as IntFoo;` It seems a logical extension. - Result-based error handling for the win. Though it really depends on language support how straightforward this is; it can easily degenerate into very spammy error handling. Definitely would like to see examples of this. - Built-in strings: hopefully UTF-8! - No preprocessor. Heck yes, it's a crutch. - Pre/postconditions are nice, but they make a lot of mess on inheritance. - Immutability by default is definitely a win. Things you should totally steal from my language: :) - Format strings are just nice. - Packages as a generalization of modules: a package is a folder in the same way a module is a file. Dependencies between packages must be explicitly stated. This makes the build system's dependency tracking actually meaningful by effectively doing away with the global search path. I wish more languages would do this. - D recently acquired automatic C header file import. (Neat has this as a macro.) I cannot overstate how useful this is for hitting the ground running. - I don't know if your macro implementation has quasiquoting (it's hard to tell from the examples) but if not: add it. This makes macros immensely more convenient.
- slmjkdbtl 5y agointerested to know if you don't mind post it here
- FeepingCreature 5y agoSure, but keep in mind it's pre-pre-alpha and the current released version is kind of outdated: https://github.com/neat-lang/neat https://github.com/neat-lang/neat Because Neat is self-hosted and frequently depends on syntax features added a few commits ago, building from fresh source can take up to half an hour. You also need a D compiler (for the initial bootstrap version), but that comes with gcc nowadays. If you wanna try that, just run `bootstrap.sh`. (You may have to patch it to use gdc, not ldc, but the commandline should be the same.) This takes a while because it has to build the compiler something like 60 times, each with the previous version. Don't do that though! Gimme a ping and I'll slap a new release tag on it. The releases use the C backend to generate a C dump of the compiler, that can then be shipped and compiled on the target system. Neat is more a D-like than a C-like, but it only breaks C syntax in areas where I think C straight up made the wrong call, like the inside-out type syntax. Memory management uses automatic ref counting, with some optimizations to keep number of inc/dec manageable. The thing I'm most proud of is the full-powered macro system, which is really more of a compile-time compiler plugin system. Here's an example of using the C import macro to bind to a C library: https://github.com/Neat-Lang/neat/blob/master/demos/glfw.nt https://github.com/Neat-Lang/neat/blob/master/demos/glfw.nt Another good example of a macro would be listcomprehensions: https://github.com/Neat-Lang/neat/blob/master/src/neat/macros/listcomprehension.nt https://github.com/Neat-Lang/neat/blob/master/src/neat/macro... You can tell it's just compiler code that happens to be loaded at project compiletime. You can see listcomprehensions at work in the sparkline demo: https://github.com/Neat-Lang/sparkline/blob/master/src/sparkline.nt https://github.com/Neat-Lang/sparkline/blob/master/src/spark... `compiler.$expr xxx` is itself a macro, that parses an expression `xxx` and returns an expression that creates a syntax tree that, when compiled, is equivalent to having written `xxx`. It's effectively the opposite of `eval`. In that expression, `$identifier` is expanded to a variable reference to "identifier". So `ASTSymbol test = compiler.$expr $where && $test;` is equivalent to `ASTSymbol test = new ASTBinary("&&", where, test)`. (This shows its worth as expressions become more expansive.) All in all, this lets you write `bool b = [all a == 5 for a in array]`, and it's exactly equivalent to a plain for loop. You can see the exact for loop at line 103 in that file. `({ })` is stolen from gcc; google "statement expression". The one thing I'm still blocking on is hashmaps, once that's in I'll make a proper announcement post. And, of course, documentation. :-)
- pjmlp 5y agoExample of an unnecessary change, func void main() instead of void main(). A better C should just be like what Apple is doing for iBoot, or Microsoft with Checked C, by disabling what makes C unsafe while being mostly compatible. Changing stuff like - ptr = array into ptr = &array[0] - num_value = enum_value into num_value = base_value(enum_value) - char *str into cstring str - char str[10] into a proper array without pointer decay (alternatively like cstring, a new array like type declaration) If it is C like, while requiring major code rewrites, then it is just another attempt, regardless of how much valuable work has been put into it. C++ got its adoption by having been born in the same place as C and UNIX, as C pre-processor with zero friction in C toolchain, hence why it was quickly adopted by C compiler vendors. Any C replacement needs the same ease of transition, specially for domains that will never move beyond C, because reasons.
- tinco 5y agoIf you're breaking backwards compatibility anyway, why not go all the way? I agree there is a need for easy transition, but I think that ease mostly comes from being able to be compiled and linked in existing tool chains like this project does. I wrote a C compiler as well, got as far as compiling a hello world and all the includes it needs on a standard Linux or MacOS system. My plan was to have a sort of strict option that would be enabled if you wanted to use the new features like modules and namespaces, and the strict option would error on problematic C code. Anyway I stopped the project when Rust released their first version without garbage collection. It seems to me they solved all my concerns and they didn't even need to integrate C itself.
- lelanthran 5y ago> Anyway I stopped the project when Rust released their first version without garbage collection. It seems to me they solved all my concerns and they didn't even need to integrate C itself. Illustrates why projects don't do this: > If you're breaking backwards compatibility anyway, why not go all the way? Rust usage, even with millions of dollars of marketing and eleven years of hype, is still almost a rounding error in terms of usage.
- lonnydonovan 5y agoHow would you rate the difficulty of C compared to Python?
- killingtime74 5y ago10
- lonnydonovan 5y agoThat's what I hear
- tpoacher 5y ago"A C alternative that looks like c" (looks at hello world: looks nothing like c)
- eps 5y agoShould probably clarify if this is allowed - func void Point.add(Abc * p, int x) ... i.e. a method with the first argument of a non-matching type. If it's not allowed, then why not use this ?
- lerno 5y agoI deliberately removed the "static" methods, so that above would not be allowed. Why I found that to be a good idea is a longer discussion. The main motivation why it is nice to have it this way is that it's straightforward to understand what the type is when doing `&Point.add`. I have considered a `this` but it's not felt super important to have. Also, note that this is allowed: `Point *x = null; x.doSomething();` - this is made more obvious by taking the reference as an explicit parameter.
- mhd 5y agoSo, Objective-C #import plus some linting rules and minor syntax adjustments? I somehow doubt that this would be worth the change (especially if you have to "extern" stuff like a C++ programmer). People can live with all kinds of minor annoyances but tend to switch mostly for bigger changes. But hey, hope that something interesting grows out of this. I quite like almost-C languages, but then again I also liked the Bourne Shell macros ;) One of these days I have to write a Oberon-in-sheeps-brackets. For an interesting "C++ that doesn't look like C" variant, consider SPECS (Conway/Werther;1996)[1]. [1]: https://users.monash.edu/~damian/papers/HTML/ModestProposal.html https://users.monash.edu/~damian/papers/HTML/ModestProposal....
- guerrilla 5y agoNo, the list of features is No mandatory header files New semantic macro system Module based name spacing Subarrays (slices) and dynamic arrays built in Compile time reflection Enhanced compile time execution Generics based on generic modules "Result"-based zero overhead error handling Defer Value methods Associated enum data Built in strings No preprocessor Undefined behaviour trapped on debug by default Optional pre and post conditions Associated enum data sounds like variant types. It has namespacing and it has generics. That alone is almost worth it to me (not that I'd use a language with no community.) If it had lambdas, that'd be the quadrivium for me. Apparently it has struct subtyping too.
- lerno 5y agoNo, that's probably not a good description. I'm not saying you will appreciate the language, but it is basically C. - "Failables" (which is somewhat like Result) offers an alternative to error handling which mostly mirrors how one commonly does it in C, but with conveniences. Since it works a bit different from all other error systems, I'd have to point to the docs for a summary. :( - Semantic macros where the big win is that they are easier to read and write. (This is heavily inspired by the ASTEC macro system for C) - Generic modules, which works similar to macro-based generics in C, but easier to read and work with. - Subarrays (slices), yes they make a huge difference even though they're an obvious addition. - A bunch of GCC extensions - Optional design by contract There are no objects, and certainly no dynamic OO system. There are no constructors and destructors or similar explicit code. Calling C is straightforward, you just need to declare that it exists, like you would in C. So `extern func int printf(char*, ...)` -> now you can do `printf("Hello %s\n", "World");` the opposite also works, so if you define a function `func void foo() @extname("c3_foo")` you can then call it from C as `c3_foo()` (or skip the `@extname` but you would have to call `my_module_foo()` instead due to namespacing.
- gradschool 5y agoThis paper [1] should be required reading for all would-be C replacement authors who don't want to follow in the footsteps of their unsuccessful predecessors. As for my two cents, the design of modern programming languages seems to be based on a premise of distrust for the programmer, but the designers appear to have forgotten that the distrust goes both ways. In [2], an old book about C programming, readers are reminded that rarely used language features incur the risk of the compiler writer not having implemented them correctly. It's unlikely that any fancy new feature would make me prefer your C replacement because I don't even use all of the features of C most of the time. That being said, if you're crazy enough to write a C replacement in the first place, then maybe you'll be crazy enough to incorporate my crazy suggestion for function specialization. In functional languages, given a function f(w,x,y,z) with many parameters, it's straightforward to define another function g(a,b) with fewer parameters as being equal to f(H,b,a,K), a more general function specialized by fixed constants for some of the parameters and arbitrary permutations of the others. I'd like to be able to express the transformation that takes a pointer to the general function f as input and returns a pointer to the specialized function g, such that the pointer to g can be used in the same context as any other pointer to a function of that arity and type defined the normal way. I'd like it to be possible in a library written be me operating on user-supplied pointers to functions not known in advance, I'd like to avoid workarounds such as global static variables or thread-specific storage, and I'd like you to convince me that your implementation of this feature is too simple to be wrong. [1] "Some Were Meant for C" by Stephen Kell https://doi.org/10.1145/3133850.3133867 https://doi.org/10.1145/3133850.3133867 [2] "C Traps and Pitfalls" by Andrew Koenig edit: typo
- mrtweetyhack 5y agoIt already looks stupid
- dingosity 5y agoThis is interesting, but is there a version that compiles the examples somewhere?