[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)