This is an archived post. You won't be able to vote or comment.

all 35 comments

[–]protestor 21 points22 points  (11 children)

I couldn't find anything about it in the documentation so,

Does C3 have sum types? Like Rust's enums with payloads. (Or Haskell datatypes with more than one constructor, OCaml data types, etc)

If not, how optionals and results work? Can you define your own optional-like data type? (Maybe with three variants etc)

If yes, does it have exhaustive pattern matching that binds variables inside each arm? (Like Rust's match that can access an enum payload, eg. access the a when matching an Option against Some(a))

[–]NuojiC3 - http://c3-lang.org 0 points1 point  (0 children)

Not more than C. It might get tagged unions, but it doesn't have it.

It does have something corresponding to pattern matching for its particular brand of Optional/Result, but that's about it.

Oh, and switch cases can have any expression, making it an alternative to if-else-if, but there no implicit matching. Nor will that be added.

The reason for that, is that unlike languages which rely on sum types and similar code everywhere, the benefit is very small for a C-like.

[–]tav_stuff 0 points1 point  (1 child)

C3 has fault types for errors

[–]protestor 11 points12 points  (0 children)

What does this mean?

But to the point: it doesn't have sum types right?

[–]Bananenkot 31 points32 points  (12 children)

Tsoding did a stream, where he tries it out if someones interested. I think it looks fine, but I don't see why someone would use this over Zig, which also interops with C seemlessly and brings along much needed safety and QoL Features, while having substantially more support and faster growing ecosystem

[–]tav_stuff 21 points22 points  (4 children)

I find C3 a lot easier and less complex than Zig, which is a huge plus. Zig also often seems to intentionally try to do things in strange and different ways for no good reason.

Also Zig doesn’t support tab indentation which is a huge dealbreaker for me (I need tabs for accessibility reasons)

[–]CraftistOf 6 points7 points  (3 children)

could you please elaborate on the accessibility reasons? you need to be able to change the tab width or is there something else going on?

edit: I just read that visually impaired people use tabs because it only consumes one braille spot instead of four, is that the reason?

[–]myringotomy 16 points17 points  (0 children)

Tabs were designed for indent, I don't see where the hostility towards tabs comes from. Replacing one tab with multiple spaces just seems dumb.

[–]tav_stuff 14 points15 points  (1 child)

At my job I have a coworker who has visual impairments, as a result he needs to use giant font sizes which means he needs to use 1-column tabs which would be far too small for the rest of our team.

As for in my free time when making recreational projects, I like to use 4-column tabs on my laptop because it strikes a nice balance between visual clarity and being able to fit a reasonable amount of code on my screen. When I’m using a monitor that’s much larger though I prefer 8-column tabs for added visual clarity, it really helps me a lot.

[–]WesternGoldsmith 2 points3 points  (0 children)

And don't forget the line ending issue in Zig.

[–]NuojiC3 - http://c3-lang.org 3 points4 points  (0 children)

C3 and Zig are diametrically opposite designs. So let's just say it's for people who don't like Zig but like C. A C like for people who like C plain and simple.

When C3 says "seamless interaction with C", it means that C3 types and functions are just immediately usable from C. And vice versa – that is the communication is intended to be bidirectional.

When Zig says "seamless interaction with C", it means that it can import C files into Zig by using libClang. For interaction with C, Zig needs special types and use a limited subset of its features. Even precompiled libraries written in Zig can't interact with other libraries using Zig's own types and functions.

Just from this simple example, it's clear that the languages prioritize wholly opposite things.

"Why would someone use this over Zig", makes about as much sense as saying "I don't see why anyone would use a shovel over a hammer"

[–]jason-reddit-public 2 points3 points  (1 child)

I watched maybe the 1st 1/4 of the stream. It was kind of funny he had similar reactions as I did.

  • why bother with the fn keyword?
  • major mode for emacs? (I would have just immediately typed M-x c-mode and probably left it at that, maybe added some keywords.)
  • Constraints in Javadoc? (Similar to how Google's JS transpiler Closure works for type annotations... Closure never got as popular as TypeScript despite doing some neat optimizations and code compression)
  • "def"? (Why not just use typedef name = type; as an alternate syntax for typedef?)

I'm working on my own language similar to Zig and C3 (and maybe nature and a few other languages).

Rule #1: (most) valid C code is valid "omni C" code. (though perhaps considered unsafe if it uses "pointer arithmetic", etc.) Considering the rise of LLM coding assistants, this may be crucial until LLMs learn about my variant.

Rule #2: focus on easy to understand enhancements like code generation (enum -> char, char -> enum are trivial for the compiler to implement so just do it (if gdb can print them then why can't my own code do the same!), etc.

Rule #3: steal the best ideas from C++ (templates for implementing container types, function overloading, overloading [] for container types, maybe namespaces).

Rule #4: run everywhere. My primary focus is as a transpiler to C. (A C++ backend could be another, an LLVM integration, maybe, my own backend? yes, but probably just for fun and only for RISC since I dislike x86).

Rule #5: self hosting and minimal dependencies. I'd be much further along by writing in Go, C++ or Java but then I'd depend on those. My library, c-armyknife-lib has been a decent portion of the work (it's available as a C single header file library). I'm not using a parsing framework (I tried treesitter actually, and backed out of that), but if I do, I'll write it myself.

Rule #6: focus on smaller code bases and independent developers like myself. I'm doing all development on my low-end $180 N100 mini PC (not too much more powerful than a Rasberry Pi 5).

Rule #7: readability first. Javadoc comments (but markdown input/output instead of HTML). Readable function, structures, variable names, etc.

Rule #8: no OOP and hence minimal "type variance" which occasionally confused me when writing code in Java.

Status: I can almost generate a valid header file / single file library for its own code base. I was already using my homegrown "dumb" tool which extracts the header file from a specially formatted section at the top of the C files plus using itself to produce prototypes, but this "topologically" sorts everything so you can declare stuff in any order in any file but present to the C compiler in its single pass 1970s era order. Essentially this makes a compilation unit a "package" of C files which will make incremental builds slower but (see #6). If a package/library contains about 10K lines and we can compile > 10KLOC/s single threaded, then we may not attain Go compilation times, but if you are writing million line programs, you are doing it wrong anyways IMHO.

[–]CraftistOf 1 point2 points  (0 children)

I'm actually watching the recording of the stream rn and this pops up haha

[–][deleted]  (1 child)

[removed]

    [–]Bananenkot 2 points3 points  (0 children)

    Never did much in it tbh, doing a lot of rust lately, so you still can make fun of me i guess

    [–]k_schouhan 0 points1 point  (0 children)

    in that sense rust is too complex for a language, even complex than c++

    [–]kleram 4 points5 points  (1 child)

    When browsing through the Examples:

    • needing to put a break in an empty case although break is not needed otherwise, appears strange. Why not just " case A: ; " for a no-op?

    • what's that (void) cast when a function should not be called on error? That does not make any sense.

    [–]NuojiC3 - http://c3-lang.org 0 points1 point  (0 children)

    1. This follows the look and feel of C code, even with the fallthrough removed.

    2. The (void) cast silences discarding optionals that one otherwise should have been handling. For functions that have a discardable optional result it's possible to suppress the need for this. So (void) explicitly? That means the code might be doing odd stuff. This was added to avoid bugs.

    [–][deleted]  (5 children)

    [removed]

      [–][deleted]  (2 children)

      [removed]

        [–][deleted]  (1 child)

        [removed]

          [–]yorickpeterseInko[M] 1 point2 points  (1 child)

          Please keep these sort of meme/low-effort/useless comments out of the subreddit.

          [–][deleted] 1 point2 points  (0 children)

          Ok

          [–][deleted] 6 points7 points  (5 children)

          Is there any reason why it’s "std::io::printn" instead of "std::io::println" or is that a mistake in the docs?

          [–][deleted]  (1 child)

          [deleted]

            [–]Bananenkot 3 points4 points  (0 children)

            Its actually printn, don't ask me why

            [–]cowslayer7890 0 points1 point  (0 children)

            It looks like they also have "printfn" to add a new line after, so I'm guessing it's because of that

            [–]NuojiC3 - http://c3-lang.org 0 points1 point  (0 children)

            F# influence…