[HN Gopher] Show HN: C3 - A C alternative that looks like C ___________________________________________________________________ Show HN: C3 - A C alternative that looks like C Compiler link: https://github.com/c3lang/c3c Docs: http://www.c3-lang.org This is my follow-up "Show HN" from roughly a year ago (https://news.ycombinator.com/item?id=27876570). Since then the language design has evolved and the compiler has gotten much more solid. Assorted extra info: - The C3 name is a homage to the C2 language project (http://c2lang.org) which it was originally inspired by. - Although C3 mostly conforms to C syntax, the most obvious change is requiring `fn` in front of the functions. This is to simplify searching for definitions in editors. - There is a comparison with some other languages here: http://www.c3-lang.org/compare/ - The parts in C3 which breaks C semantics or syntax: http://www.c3-lang.org/changesfromc/ - Aside from the very C-like syntax, one the biggest difference between C3 and other "C competitors" is that C3 prioritizes C ABI compatibility, so that all C3 special types (such as slices and optionals) can be used from C without any effort. C and C3 can coexist nicely in a code base. - Currently the standard library is not even alpha quality, it's actively being built, but there is a `libc` module which allows accessing all of libc. Raylib is available to use from C3 with MacOS and Windows, see: https://github.com/c3lang/vendor - There is a blog with assorted articles I've written during the development: https://c3.handmade.network/blog Author : lerno Score : 71 points Date : 2022-07-06 19:31 UTC (3 hours ago) | tialaramex wrote: | Aside - Documentation says: | | > It is possible to cast the variant type to any pointer type, | which will return null if the types match, or the pointer value | otherwise. | | That seems backwards to me. Maybe it's just me? Surely if the | types match that's when we get the pointer value ? | | My main point - Strings | | I think at this point good strings are table stakes. If you're a | general purpose language, people are going to use strings. Are | they going to make a hash table of 3D vectors? Maybe. Are they | going to do finite field arithmetic? Maybe. Are they going to | need networking? Maybe. But they are going to use strings. C's | strings are reprehensibly awful. C++ strings are, astoundingly, | both much more complicated _and_ worse in many ways, like there | was a competition. You need to be doing a _lot_ better than that | IMHO. | | I happen to _really_ like Rust 's str but I'm not expecting to | see something so feature rich in a language like C3. I am | expecting a lot more than from a fifty year old programming | language though. | _gabe_ wrote: | This looks awesome! The main thing holding me back from switching | from C++ to C is the lack of type safe generic programming. This | language looks like it not only solves that, but adds some other | interesting features like defer that I've been wanting to try out | :D. It looks like there are some examples of large projects | getting successfully compiled by C3 (the vkDoom). | | Since this is still only alpha 0.2, I'm curious how stable the | compiler is and whether the core language features are subject to | change? I'd love to start using this on some projects, but I'm | always afraid to adopt a language in its early stages. | addaon wrote: | Looking at the primer... // C typedef | struct { int a; struct { | double x; } bar; } Foo; // C3 | struct Foo { int a; struct bar | { double x; } } | | Very confused by this. The C code declares an anonymous struct | type, then aliases the typename "Foo" to that anonymous struct | type. The C3 code seems to declares a named struct type "Foo" -- | why isn't the C equivalent here just "struct Foo"? | | But then within the struct it gets weirder... the C code declares | a second anonymous struct, and then declares a member variable of | that type. The C3 code... declares a struct named "bar" and also | a member variable with name matching the type? Except the primer | says that these are equivalent, so the C3 code is declaring an | anonymous struct and a member of that type? Using the same syntax | as the outer declaration did to declare a named type but no | (global) variable?? Is this case sensitive? | | I don't think I can get further into the primer than this... even | taking the author at their word that the two snippets are | equivalent, I don't understand what's in play (case sensitivity? | declarations where variable name must match type name?) to make | this sane, and there's zero rationale given for these decisions. | brabel wrote: | > why isn't the C equivalent here just "struct Foo"? | | That would not be the equivalent... you would then need to | declare the type of Foo variables as: struct | Foo myVar; | | With the typedef, and I assume with C3, you would do the more | acceptable: Foo myVar; | addaon wrote: | From the primer, C3 follows the C++ rules here, not C rules. | Changing to using C++ struct/enum/union naming rules here is | a much less invasive change than what I'm discussing. | addaon wrote: | Oh gosh... digging further on the same page under "Identifiers" | it looks like case sensitivity is the key here. So "struct Foo" | declares a type "Foo", and "struct foo" declares a variable | "foo" of new anonymous type. I assume "struct Foo foo" and | "struct bar Bar" do exactly what you (don't) expect, and maybe | even "struct foo bar baz {}" to be the equivalent of the C code | "struct {} foo, bar, baz"... yikes. | | Edit: "Declaring more than one variable at a time is not | allowed." So there's no equivalent to the C code ""struct {} | foo, bar, baz"... not clear if "struct | IDontNeedANameButTheLanguageIsForcingMe foo {}; | IDontNeedANameButTheLanguageIsForcingMe bar; | IDontNeedANameButTheLanguageIsForcingMe baz;" is legal (modulo | that some of those semicolons are illegal I think?). | | Yeah, this needs some rigor in the docs. | lerno wrote: | No, you misunderstand. There is no `struct Foo foo`. Unlike C | `struct Foo` would only ever be valid at the top level. | Neither `struct Foo foo` nor `struct bar Bar` works. | | The reason why `Bar` is a type and `bar` is a variable or | function is to resolve the ambiguity of C syntax without | having to rely on unlimited lookahead or the lexer hack. | Basically it allows tools to parse the code much more easily | than they would C. | | You can declare multiple fields in a struct, e.g. `struct Foo | { int x, y; }`, but you can't write `int x, y = 123;` like in | C. This is because it would create ambiguities in the | extended for/while/if syntax C3 adds. | Cloudef wrote: | This doesn't seem very well thought out at all | [deleted] | _gabe_ wrote: | > Very confused by this. The C code declares an anonymous | struct type, then aliases the typename "Foo" to that anonymous | struct type. The C3 code seems to declares a named struct type | "Foo" -- why isn't the C equivalent here just "struct Foo"? | | I'm curious how familiar you are with C? In C++ you can do: | struct Foo { // ... }; Foo myVar; | | But that's not how it works in C. In C this would be: | struct Foo { // ... }; struct Foo myVar; | | Which is why many C developers typedef the struct so that they | don't have to prefix struct types with the keyword. | | > I don't think I can get further into the primer than this... | even taking the author at their word that the two snippets are | equivalent | | Maybe don't judge the author on these things if you're not | familiar with how C would work in this case? There's nothing | wrong with not understanding a piece of code, but it's | generally not a good idea to assume you have understanding of a | language like C if you understand C++. People often conflate | the two, but there are many quirks of C that C++ doesn't | necessarily need to do and vice versa. | Cloudef wrote: | I hate when c++ does this. I don't want automatic typedefs, | keep the struct so I know what it is. Also causes annoying | name conflicts ... | [deleted] | boardwaalk wrote: | The number of languages that have separate namespaces for | the two things is very small. See all the C descendants | that discarded that idea: C++, C#, Java, Rust, Go. You | probably ought to just find a decent naming scheme to avoid | those conflicts... | Ontonator wrote: | I can't speak for the others, but Rust definitely has | separate namespaces for them. See https://doc.rust- | lang.org/beta/reference/names/namespaces.ht... | Bytewave81 wrote: | Not the type of namespace being referred to. In this | case, it's referring to the requirement to refer to all | types of a certain category with a keyword, as in `struct | Foo`. In Rust, you can refer to a struct named Foo as | just `Foo`. | hvdijk wrote: | C++ didn't discard that idea entirely, you can have a | struct tag and a function with the same name and you need | to be able to do that in order to use stat(). | SV_BubbleTime wrote: | > Which is why many C developers typedef the struct so that | they don't have to prefix struct types with the keyword. | | Yes, guilty. | lerno wrote: | The first part about the name is just like C++: you use the | name without `struct` unlike C where structs has its own | namespace. That's what it's meant to illustrate. | | The second question is more subtle. In C, the syntax is `struct | { ... } [optional member name] ;`. Because there is no | anonymous struct at the top level, the anonymous structs inside | of a struct has a different syntax, also eschewing the final | `;`, changing the syntax to `struct [optional member name] { | ... }`. If the C syntax structure is desired a final `;` would | be required. This syntax change comes from C2. | addaon wrote: | But what if I want to name the inner struct so I can refer to | Outer::Inner (for example, for use with sizeof or similar, or | to create a temporary to hold a copy of that type) later? Do | I need to use typeof(Outer::inner)? And then of course | multiple members of the same type... | lerno wrote: | You can't name the inner struct. So here are the options: | | Say that you want this from C: struct Foo | { struct Bar { int x; int | y; } bar; }; struct Foo f; | f.bar = (struct Bar) { .x = 123 }; | | The struct in C3: struct Foo { | struct bar { int x; int y; | } }; Foo f; | | It is correct that we don't get the inner type, but because | C3 has inference for {} assignment can happen despite that: | f.bar = { .x = 123 }; // Valid in C3 | | You can grab the type using typeof if you want, but | interestingly there's also the possibility to use | structural casts, which are valid in C3: | struct HelperStruct { int x; | int y; } HelperStruct test = | (HelperStruct)f.bar; // structural cast | | But the normal way would be to create a named struct | outside if you really need a named struct, e.g.: | struct Bar { int x, y; } struct Foo { | Bar bar; }; | addaon wrote: | Also, just to be clear, the removal of semicolons | deliberately makes the language whitespace sensitive, right? | That is, struct Foo { } foo | | declares a variable of a new empty struct type, but | struct Foo {} foo | | is a syntax error? | lerno wrote: | C3 doesn't have declaration of structs/unions in variable | declarations. So both are equivalent and are syntax errors. | FpUser wrote: | what is it with fn()? If we are so much into being terse then int | abc() should be just fine. If we need readability than function() | instead of fn() would do better. | stormbrew wrote: | If you haven't heard of it before, look up the "most vexing | parse" for the kind of thing languages are trying to avoid with | this syntactic approach. That specific one is a c++ thing, but | C also has its own versions of it (that c++ inherits). | | Basically the C grammar is only context free if you cheat in | the lexer, and any language trying to improve on C is likely to | try to avoid that (or just give up and go wild). | yellowapple wrote: | fn() strikes me as a reasonable compromise between those two | concerns. | azinman2 wrote: | From the post: | | - Although C3 mostly conforms to C syntax, the most obvious | change is requiring `fn` in front of the functions. This is to | simplify searching for definitions in editors. | WalterBright wrote: | Should compare with DasBetterC too! | | https://dlang.org/spec/betterc.html | keikobadthebad wrote: | > requiring `fn` in front of the functions. This is to simplify | searching for definitions in editors. | | You don't have to do that even in C, you can use this style for | function defs static int myfunc(...) | { ... } | | and search or grep for ^myfunc\( | | to isolate the definition as opposed to the use. | ChickeNES wrote: | I was confused by this, thought they added `fn` to simplify the | parser which to my understanding is why many languages moved | away from C style function declarations. I'd rather they move | the return type to the end like in Rust and Go for example | IshKebab wrote: | Or just use an IDE. It's 2022 people! We don't have to code | like it's 1985. | Cloudef wrote: | I still haven't used a IDE that has made my developer | experience better than simple text editor and some cli tools. | They all seem to fail spectacularly if you have to do | something bit more different than they were designed for | (usually regarding how the project is built), and often feel | very sluggish. Also the project files or build files they | generate are usually horrible, and not great for version | control. | oaw-bct-ar-bamf wrote: | Use the IDE for adding an index to your codebase for better | navigation & editing and cross file refactoring. | | Use whatever headless buildscripts you fancy (make?!) to | build your project. | | Problem solved. | wruza wrote: | Use cscope and/or [ex]ctags and there was no problem in | the first place, even in 1985. | jonathankoren wrote: | cscope? Now there's a name I haven't heard in 23 years. | jonathankoren wrote: | Code search and an debugger that isn't a pain to use are | the main advantages of an IDE. | | I don't think I've ever seen the build and code repo | tooling ever work on any professional codebase I've | worked on, save one. | lerno wrote: | Interestingly removing `fn` was something I've considered more | than once, but I had people ask me to keep it. Even though you | can get searchability in C by adhering to certain conventions, | that's not the same as being able to search ANY code base in a | simple manner, which I believe is what people wanted. | keikobadthebad wrote: | I doubt many C programmers were waiting for someone to force | a function naming style on them after 50 years. | 1vuio0pswjnm7 wrote: | While I admire C for its size economy, I have yet to find a | reliable solution to searching _any_ C source code for | function definitions. I have some crude hacks for this that | are not 100% reliable. | | I have no idea if a new keyword like fn is the answer, but I | appreciate that someone sees this as an unsolved problem. | Maybe there is some existing solution created many years ago | that I have yet to discover. | | "If everyone would just adhere to these conventions or this | particular style, then it is is easy to search for function | definitions." | | Right. And if that were a feasible solution, then, for | example, searching, browsing and extracting data from the | www, not to mention writing www browsers, would be easy, too. | | The reality is that there are inifinite "conventions" and | "styles" in use, and these keep changing. | gnulinux wrote: | Yeah but that's a very quirky style; I don't want to change the | structure of my code just to make it searchable! | keikobadthebad wrote: | Sticking fn in front of every def isn't quirkier? | | Some parts of the kernel use this style. | | It's also advantageous because it enables long return types | and long function names neatly at the same time. | [deleted] | axy wrote: | Glanced. 17 or 18 years ago I implemented my own programming | language too so I can only say: keep on. Just a quick note in | terms of Art and Beauty: what I expect from a new language is | expressiveness, and encouragement of good programming techniques, | so that a code written by an average programmer in the worst mood | would not look as a total mess. | EdSchouten wrote: | I'm puzzled with what the market for these kinds of languages | are. | | C is sort of a dead end. There is very little innovation there. | And that's fine; the users of the language seem to want it that | way. They just want to write software the same way they've been | doing for the last 20 years. Why would such a conservative user | base want to switch to a different language like C3? | | Linus once said this about Subversion: "Subversion has been the | most pointless project ever started... Subversion used to say, | 'CVS done right.' With that slogan there is nowhere you can go. | There is no way to do CVS right." Could C3 be the Subversion of | programming languages? | 0xFACEFEED wrote: | > C is sort of a dead end. There is very little innovation | there. | | C is a small language. There are benefits to that. But it also | has a handful of historical oddities. Innovation here means to | keep C small while also getting rid of those quirks. | | C++ is _enormous_. Rust is headed in the direction of similar | enormity. | jonathankoren wrote: | C++ is a monsterous cruel joke compared C, but if you're just | looking for some syntactic niceties, nothing stops you from | writing C in C++. | stormbrew wrote: | > C++ is enormous. Rust is headed in the direction of similar | enormity | | I don't think this is a fair characterization of rust really. | For the most part the things on the horizon still for rust | seek to reduce complexity by filing down sharp edges that | force you to write complicated code. Stuff like GATs seem | complicated until you repeatedly slam your head into the lack | of them trying to do things that "seem" natural. | | C++ on the other hand (after 2011) just never saw an idiom it | didn't like enough to throw on the pile and there's little | coherence to the way the language has grown in the last | decade. | 0x20cowboy wrote: | I love C because it's so minimal. | | And it's not a dead end at all - embedded systems, wasm, wasi, | really fast things, and aside from assembly it's one of the | first things on newer platforms (risc v for example). | | I like Go for the same "try to keep it minimal" reasons, and | keen to try Zig when I have some time. | | I think the bias you are implying might be misplaced. | com2kid wrote: | The problem with C is that its... kind of minimal, but it | also needs tons of extensions to get things done. | | Want multiple heaps? That'll be a compiler extension. Want to | specify exactly where a variable is laid out in memory? | Compile extension. | | Have a memory layout that isn't just a flat map of address | space? Better reach for a compiler extension. | | Hardware registers? Eh, overlay it with a union or a struct | or something, it'll be fine. Unless you want some sort of | non-trivial typing on those registers. | | People forget that C is written against an abstract C | machine. C is _not_ written against the underlying hardware | platform. And the abstract C machine may differ from your | target hardware by a fair bit. | kloch wrote: | > C is sort of a dead | | C is like a table saw without a blade guard. Simple yet | precise, powerful, flexible, and will cut your fingers off if | you aren't careful. | | But it's often exactly the kind of dangerous saw we need | sometimes. | | There's no point in trying to improve it - there are plenty of | other, safer saws for that. | rwbt wrote: | > I'm puzzled with what the market for these kinds of languages | are. | | There's a significant number of C programmers who want | something slightly more modern and convenient but don't want to | write C++ due to a number of reasons. | | I think Zig, D are examples of this niche but syntax wise they | don't completely look like C. | bachmeier wrote: | > I think Zig, D are examples of this niche but syntax wise | they don't completely look like C. | | This is technically true, but someone that likes writing C | while wanting a few extra features would mostly be able to | write the same C code they always have if they stick to D's | betterC. (I'm not familiar enough with Zig to comment on | that.) | | Edit: Should also add that D now compiles C code, so a C | programmer could continue to write C as they want, write a | few D functions where those features help, and compile them | without writing any binding code. | com2kid wrote: | > I think Zig, D are examples of this niche but syntax wise | they don't completely look like C. | | That is because C's type declaration format is provably, as | in actually provably, terrible. | | There is a reason the majority of modern languages have | switched away from how C declares types. | | Honestly it'd be nice if the C committee could find a way to | tack on a new variable declaration syntax to C, so everyone | didn't have to keep looking up, or using tools, to declare | non-trivial function pointers. ___________________________________________________________________ (page generated 2022-07-06 23:00 UTC)