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