Hacker News new | past | comments | ask | show | jobs | submit login
C2 Programming Language: An Evolution of C (c2lang.org)
62 points by mabynogy on Oct 6, 2017 | hide | past | favorite | 25 comments



Looking at the language, I fail to see a point. It seems to fix things that are slightly nagging, but not real problems with c (lack of real modules, structs, default arguments, integer sizes) while failing to fix the real problems (strings, buffer checks, metaprogramming, type introspection (_Generic doesn't really solve this, and neither does __typeof__(). A slightly enhanced __typeof__() plus a slightly enhanced _Generic might have a shot)).

No more includes/header files seems to imply no more preprocessor, which is a shame because while it may have been hackish, it was one of c's greatest strengths.

> more logical keywords (public/local replaces static)

My friends, have you heard of the wonderous c preprocessor?

    #define func
    #define local static
    #define public
Agree with what comex said about compilation/building.

For a slightly nicer alternative to c, what about zig[1]?

1: http://ziglang.org/


> Looking at the language, I fail to see a point. It seems to fix things that are slightly nagging, but not real problems with c (...) while failing to fix the real problems

I agree. What caught my eye was how they kept C's "clockwise/spiral" rule for declarations. If their goal was to actually improve the language over C then it makes no sense to keep one of C's main shortcomings. Heck, even C++ improved on this problem by introducing support for trailing return type declarations in C++11.

In addition, it appears C2 still lacks support for anonymous functions, which is simply absurd in today's age.


Why does no more headers imply no preprocessor.


Because #include is a major part of the preprocessor. Because I didn't see any other preprocessor use in the example programs.


Maybe they meant that you do not need "permission" from a language to use a preprocessor, and so can use one anyway.


From a cursory look at the documentation this interests me, but with one huge caveat: does this support incremental compilation?

The documentation goes on at some length about how the C2 compiler parses and analyzes all source files at once, before generating code. Supposedly this improves compilation speed because:

The code generation (and optimization) steps require a lot of time. So if you have 100 C source files and the 99th file has a syntax error, the compiler has to go through a lot of work before showing the diagnostic. In C2 it only takes very little time. So during development, developers never have to wait for diagnostic messages.

Except that during development of C programs, in practice, most changes will only require recompiling one or two files. The rest of the files have already been compiled and don’t have to be examined at all until the link step, which is relatively fast.

Admittedly, C is simple enough, and the header file and process-per-source-file models inefficient enough, that the C2 compiler might be able to parse and typecheck 100 files in an amount of time similar to what make and clang take to build two. I haven’t tested it, but let’s assume so. Then if I have an error, I indeed wouldn’t have to “wait for diagnostic messages” (for too long, anyway). And it would be nice that the same speed could be achieved regardless of what I change, whereas in C touching a common header file may well require rebuilding everything. Then again, this fundamentally doesn’t scale well.

But what if I don’t have an error? With an incremental C build, I don’t have to wait to run and test my code, either. If the C2 compiler doesn’t support incremental compilation, it’ll have to redo code generation for everything, using the same backend (LLVM) as my C compiler. Thus, the ‘feature’ of compiling everything at once becomes an antifeature.

It is possible to implement incremental compilation in a compiler more ‘natively’ than C’s file-based approach, and this can turn module-at-once compilation from a mixed bag for performance to a pure win. If the C2 compiler has done so, I shouldn’t be complaining! But since it’s not mentioned in the documentation pages I looked at, I’m guessing it hasn’t.


If any of you who found this interesting also know Prolog, I am working on a compiled language you should check out (The compiler is written in Prolog).

https://github.com/GordianNaught/Juicy


With the large number of new languages coming out (and maturing) these days, I've always found it annoying that there's not been a real attempt at a better C. So this is nice to see.

I don't count Rust, because one of the good things about C is that it's quite easy to write a compiler for. A better C shouldn't require a too clever compiler.

Another attempt I stumbled across recently is Zig: http://ziglang.org


> I don't count Rust, because one of the good things about C is that it's quite easy to write a compiler for. A better C shouldn't require a too clever compiler.

Why is that relevant?


Maybe one reason is portability? Although the Rust strategy of leveraging LLVM is a good answer, there are situations -- a partial implementation for the sake of analysis, or a clean room implementation for reasons of IP or national security -- where a simple language retains an edge.


There was a new implementation of rust in c++ https://github.com/thepowersgang/mrustc


Yeah but it still needs original rustc for verification (borrow checking).


Packaging needs to be improved, especially given that it requires their fork of llvm and clang.

deb/rpm files or at least a PKGBUILD would help make it a lot more accessible.


looks solid, but seems to lack a few features I would want in a modern C replacement: if-expressions, tagged unions, immutability by default.


Your list of features seem to match up well with goals of my language. If you made mention of type-inference, then it would sound like a description of it.

https://github.com/GordianNaught/Juicy


From my amateur language nerd viewpoint, it's a crime for any newly designed statically typed language to lack tagged unions with exhaustiveness checking. Talk about an easy win for correctness!


Why is imutability by default important to you? Why not just add const to your definition?


Why make it easy to use something bad and hard to use something good? The concept of "this variable is mutable" makes more sense than "this variable is constant" anyway; saying that a variable is mutable is like _adding features_ to it (i.e.: the ability to mutate it).


The very name "variable" implies changing value. When you define something that doesn't change you are defining a "constant".

I also don't think a mutable variable is bad or hard to use. Is this bad/hard to use?

    for (int i = 0; i < 10; i++)
Or is this bad....

    int line_number = 0;
    char *line;
    while (read_line_from(stdin, &line))
    {
        // TODO handle line
        line_number += 1;
    }
I don't think so. I think much of this buzz about how bad variables are comes from Rust merely existing and no practical examples.

What inherently makes this

    for (mut int i = 0; i < 10; i++)
better than our last example for loop?

If you're actually against mutability where it doesn't need to be you can create tooling that can warn you of where you can add const. I think that's much better.


> The very name "variable" implies changing value.

Variables, both in ordinary mathematics and in the semantics of programming languages, are given meaning by substitution, not mutation. This is true even in the semantics of imperative languages[0][1].

[0] https://en.wikipedia.org/wiki/Hoare_logic

[1] https://en.wikipedia.org/wiki/Predicate_transformer_semantic...


Your examples are too minimal. In most real-world cases there is a bunch of other variables also in scope of the loop. If all those variables are const you know that they are not changed in the loop (or elsewhere), simplifying understanding of the code and also reducing the chance of someone making the code more stateful later. With default-immutable this const is the default, making it harder to fuck up.


yet no closures

(arguably, the only thing easy to do in assembler that you cannot comfortably write in C)


this really looks like a point on the interpolation between C and Go. not that thats a bad thing.

but assuming that I think they should just have pulled in the type syntax from Go


How is struct function different from c++ class member function?


Interesting. Deserves far more upvotes.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: