[HN Gopher] Show HN: The C3 programming language (C alternative ...
       ___________________________________________________________________
        
       Show HN: The C3 programming language (C alternative language)
        
       Get it from here: https://github.com/c3lang/c3c  In 2019, while
       contributing to the C2 language, I started up "C3" as a pet project
       while waiting for pull requests to be approved...  Now it's 6 years
       later and C3 well on its way to 1.0, having released 0.7.0 last
       week.  Unlike other C alternatives, C3 tries to evolve C - but
       without concern to backwards compatibility to the latter.  What it
       adds to C is among other things:  - Module system  - Semantic
       macros and compile time introspection  - Lightweight generic
       modules  - Zero overhead errors  - Build-in slices and SIMD types
       - Gradual contracts  - Built-in checks in debug mode  You can find
       more details on the site: https://c3-lang.org It might be
       interesting to look at the examples: https://c3-lang.org/language-
       overview/examples/ so see how the language looks for some simple
       examples.   _Some other links that might be interesting follows:_
       I've posted about C3 on HN before, notably  -
       https://news.ycombinator.com/item?id=24108980  -
       https://news.ycombinator.com/item?id=27876570  -
       https://news.ycombinator.com/item?id=32005678  Here are some
       interviews on C3:  - https://www.youtube.com/watch?v=UC8VDRJqXfc  -
       https://www.youtube.com/watch?v=9rS8MVZH-vA  Here is a series doing
       various tasks in C3:  -
       https://ebn.codeberg.page/programming/c3/c3-file-io/  Some
       projects:  - Gameboy emulator https://github.com/OdnetninI/Gameboy-
       Emulator/  - RISCV Bare metal Hello World:
       https://www.youtube.com/watch?v=0iAJxx6Ok4E  - "Depths of
       Daemonheim" roguelike https://github.com/TechnicalFowl/7DRL-2025
        
       Author : lerno
       Score  : 88 points
       Date   : 2025-04-03 13:55 UTC (9 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | unclad5968 wrote:
       | I have used this language for a few things (csv parsing and some
       | simple personal cli tools). Other than the normal pre-1.0 issues
       | it's great. I wish it had a tagged union type, but it looks like
       | that's planned based on the github issue tracker.
       | 
       | 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.
        
       | dingdingdang wrote:
       | Does C3's compiler utilise the highly optimized C compilers like
       | clang and gcc similar to Vlang and Nim?
        
         | KerrAvon wrote:
         | it uses LLVM, see the bottom of TFA
        
         | sparky4pro wrote:
         | It uses llvm.
        
       | dooglius wrote:
       | How does this compare to Zig or Odin, which have the same goals
       | of improving upon C and have gotten occasional publicity here on
       | HN?
        
         | johnisgood wrote:
         | It is answered here: https://c3-lang.org/faq/compare-languages/
        
           | dkersten wrote:
           | I feel like its missing a point for "In C3 but not in Jai":
           | you can actually download and use C3...
        
         | pixelpoet wrote:
         | For one thing, C3 thankfully understands that there are more
         | mathemathical types people are interested in than just ints and
         | reals, for example vectors: https://c3-lang.org/language-
         | common/vectors/
         | 
         | 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.
        
         | lerno wrote:
         | As the author, let me add something beyond the comparison. Zig
         | and Odin are very different languages, Odin is - as its slogan
         | goes - "for the Joy of Programming". Zig on the other hand
         | doesn't feel that this is a goal. From what I can tell Zig fans
         | like to wrestle with the features of Zig to figure out how to
         | fit their solutions within the constraints of the language. A
         | mental challenge, similar to that of fighting the borrow
         | checker in Rust. People who "figured out" Zig tend to be
         | fiercely loyal to the language in a similar way as Rust
         | evangelists to Rust.
         | 
         | 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.
        
           | judofyr wrote:
           | > People who "figured out" Zig tend to be fiercely loyal to
           | the language in a similar way as Rust evangelists to Rust.
           | 
           | 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.
        
             | unclad5968 wrote:
             | So which language do you use then? I've never seen a
             | language that doesn't have bad things to say about other
             | languages. Zig bdfl himself accused vlang of committing
             | fraud a while back.
             | 
             | Every language designer takes things they like about some
             | languages and leaves things they don't like.
        
               | wk_end wrote:
               | > Zig bdfl himself accused vlang of committing fraud a
               | while back.
               | 
               | 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.
        
               | judofyr wrote:
               | > I've never seen a language that doesn't have bad things
               | to say about other languages.
               | 
               | 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/ho
               | w_to_convin....
        
               | unclad5968 wrote:
               | > than to push their language at all cost
               | 
               | Nobody said they do that
        
             | lerno wrote:
             | For what it's worth, I found the Zig community on the
             | biggest Zig discord very nice and welcoming. But _that
             | said_ , there is a lot of _" you have to understand Zig"_
             | sentiment. Also, there is a lot of "I discovered Zig and
             | it's finally showing me how to program" echoed as well.
             | 
             | 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.
        
           | lerno wrote:
           | Just to be clear, I don't mean the description of Zig to put
           | down the language or the community. It's the best I can do to
           | describe the difference between Zig on one hand and Odin/C3
           | on the other.
           | 
           | 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.
        
         | fuhsnn wrote:
         | Zig and Odin compiler may implicitly pass variables by
         | references[1][2], creating hidden aliasing, that's one thing
         | unacceptable for me coming from C, I haven't read about C3
         | doing this, hopefully it doesn't and not planned in the future.
         | 
         | [1] https://www.1a-insec.net/blog/25-zig-reference-semantics/
         | [2] https://github.com/odin-lang/Odin/issues/2971
        
           | lerno wrote:
           | C3 follows the C ABI, so no it doesn't do it and it's not
           | planned.
        
       | johnisgood wrote:
       | Are there any examples of using a C library (binding, FFI) in C3?
       | A quick search came up empty.
        
         | sparky4pro wrote:
         | Here you go:
         | https://ebn.codeberg.page/programming/c3/c3-opengl/ This one is
         | for OpenGL.
        
         | lerno wrote:
         | Did you see this? https://c3-lang.org/language-common/cinterop/
        
           | johnisgood wrote:
           | Oops, I missed that, thank you! Seems straightforward. I will
           | give it a spin later. :)
        
       | Sohcahtoa82 wrote:
       | tbh I think one of the things I really liked reading through
       | examples was the change in the switch/case behavior. I always
       | thought implicitly falling into the next case was an awful
       | design, and that a break is the more logical implicit behavior
       | except in the case (no pun intended) of stacked empty case
       | statements.
       | 
       | 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.
        
         | mjburgess wrote:
         | Things like that make grepping easier. And redundancy of syntax
         | makes reading-without-mistake faster. The more you put on the
         | page the lower the cognitive load, the more spare it has in it,
         | the easier it is to search for increasingly refined contexts of
         | use.
        
           | alcover wrote:
           | Agreed. It's tempting to make the tersest lang you can but in
           | the end what matters is ease of reading.
           | 
           | 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)`
        
         | lerno wrote:
         | I wrote a short blogpost about this before, trying to answer
         | the question:
         | https://c3.handmade.network/blog/p/8886-why_does_c3_use_%252...
        
       | sfpotter wrote:
       | One thing I just can't understand is _proactively_ using the ::
       | syntax. It 's sooo ugly with so much unnecessary line noise. Just
       | use a single period! I think one of the best decisions D made was
       | to get of -> and :: and just use . for everything.
        
         | lerno wrote:
         | `::` simplifies the module vs identifier resolution.
         | 
         | 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()`?
        
           | WhitneyLand wrote:
           | Sure, but in practice I believe most developers would find it
           | intuitive to just type . everywhere.
           | 
           | 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.
        
             | PartiallyTyped wrote:
             | N=1; I find :: a lot more obvious.
        
             | steveklabnik wrote:
             | I've lived in both and I prefer ::.
        
           | IshKebab wrote:
           | I would just explicit require pulling `foo` into scope, like
           | Rust does. Though in fairness Rust also uses :: for scope
           | anyway and I don't think it's as bad as sfpotter says.
        
           | WalterBright wrote:
           | > `::` simplifies the module vs identifier resolution
           | 
           | 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 .
        
           | sfpotter wrote:
           | This is the kind of thing I don't want to have to think about
           | as a programmer. The compiler should just make it work.
        
         | foul wrote:
         | I can understand it, I just hate it. I would prefer confusing
         | dots, a module builtin namespace, rebol backslashes, confusing
         | slashes, _anything else_.
        
         | melon_tusk wrote:
         | I can live with "this::that", but what drives me bonkers is
         | "this :: that", which is what Odin does. Other than that, Odin
         | is an incredible language.
        
           | deagle50 wrote:
           | that's a constant assignment, completely different. And
           | you're not going to see it 100s of times in a project.
        
       | alcover wrote:
       | Hello, your doc about _const_ says  "The const qualifier is only
       | retained for actual constant variables".
       | 
       | Then how do you express read-only pointers ? Like C `const int*
       | ptr`
        
         | lerno wrote:
         | If you pass them as parameters, then there are in/out/inout
         | annotations to limit usage. But other than that, there isn't
         | anything.
        
       | dkersten wrote:
       | Oh. This actually looks like something I could see myself using,
       | maybe. I thought I'd hate it, but after checking out the
       | examples, this is pretty nice.
       | 
       | 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!
        
       | Surac wrote:
       | as much as i like a better 'C' language, it is hard to use them.
       | so many things have been done in C and you find everything in the
       | net you would ever like to use. Using a better 'C' places you on
       | that strange island where no one hears you cry. Using C3 in a
       | company places you in a place where only you can understand the
       | code.
        
       | fuzztester wrote:
       | Any other C alternative or C-like languages that people here are
       | using more than experimentally?
       | 
       | 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?
       | 
       | https://news.ycombinator.com/item?id=43223162
        
         | lerno wrote:
         | Other than C3, there is Jai, Odin, Zig and Hare which are the
         | ones that have any traction right now that I know of. Many
         | interesting projects have been started but ultimately later
         | abandoned.
         | 
         | 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)
        
       | xormapmap wrote:
       | So many of the so-called "C alternatives" end up doing way too
       | much. I don't need algebraic data types or classes or an
       | integrated build system or a package manager.
       | 
       | 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.
        
       | micklemeal wrote:
       | Recently gave this language a spin with raylib and libtmx for
       | loading tiled maps. Out of C3, Zig, and Odin, I've had the least
       | trouble integrating C libraries with C3 (rolled my own bindings
       | for libtmx). Overall a big fan of the language and am hoping it
       | gets recognition on the level of the other languages mentioned
       | here.
        
       | owlstuffing wrote:
       | Overall, this is one of the better C killers I've seen. But
       | keeping macros while ditching conditional compilation? That seems
       | completely backward to me. Oh well.
        
         | lerno wrote:
         | What kind of conditional compilation are you missing?
        
           | spc476 wrote:
           | For adapting code to different versions of libraries for one
           | thing:                   #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.
        
       ___________________________________________________________________
       (page generated 2025-04-03 23:02 UTC)