Why modern languages don’t look like C



Despite the number of languages that call themselves C alternatives, it’s pretty rare to find them actually looking like it. C’s performance and semantics may be what’s most important, but what’s stopping newer languages from keeping the syntax too?

Sorry about the casting section. Somehow, I never managed to see the ambiguity in casting in the weeks I had planned to make this video, and only noticed it a few hours before finishing editing. Here’s the gingerBill article that I noticed it in:

source

40 Comments

  1. none of these are a problem if the code is clean and free of weird syntax hacks. if it's readable by a human, the compiler and highlighter will have no problem reading it. C style codes can look very deliberate if you don't feel an urge to cheap out on keystrokes

  2. I love Odin. Been messing with it recently to do some graphics drawing stuff and it`s syntax really clicks with me. It`s a bit "like C but saner". The pointer syntax is way easier to understand and follow than that one of C.

  3. C might have become common, but look at the larger heritage of ALGOL-family languages.
    Some of C's problems come from its own specific heritage, where types were basically added back in for portability in "NB" which became "C".

  4. c++ has had "using" as alternative to typedef for 10+ years, you can do it either way, you can use c++ compiler and a few c++ features you like and the rest c style.

    c version of strings is inefficient on modern processor so you can also mix something like pascal strings as well. (zero terminated means have to go one byte at a time through string with a 64 bit processor)

    There is no need to be trapped in the downsides of "traditional" c when doing c language. The point of c is simple and flexible, eg all the tricks you can do with #define.

  5. You have to remember two things. Programming languages are for the human not the computer, and because of this it is more important to reason about what you want reality to be rather than reason about what it actually is. That last one is fancy speak for 'you should think about what something is to you, not what it is to the computer'. When I write Kotlin or TypeScript, what I want to be able to say for a variable declaration is: "I have this binding to a value which can either change or not. It carries data of a type T. T itself may be mutable or immutable. So I would like to be able to specify, if required, that the value itself can also not be changed". So we end up with something like `const x: readonly Person = Person()` or `val x: ReadOnly<Person> = Person()` where Person is a mutable reference type. Syntax is not the issue here and neither is operator overloading. Its all fine really. The reason we like {mutability}{binding}: {type} = {expression} more is just because it is the order in which we look at things. Some languages declare const or mut after the binding name which I find more correct but more painful to read. It just works better to see const first because you already know "whatever comes next can not change" which for some odd reason sticks better than "this thing -> oh yea it can't change". Both are valid though and it just depends on what you are used to in the end, its just that more languages agree that mutability comes before the binding name. So really we are not abandoning C at all. We call something a C like language more for how it functions than what it looks like. If you express the same thing meaning the same thing, they are considered to be the same thing in that world. Rust is an example of what happens when you push a lot of tokens through the type brackets just because they did not get a proper place. You could argue that languages following after Rust will make a difference in the syntactic spaces where lifetimes, data type, obligation (auto traits) and capability (traits) lie. I find the type syntax bloated as heck, and the name of the game is really 'where do I put this in the grammar where it makes sense?' and not if we are able to express it which we clearly already can. Parsers and lexers really just do not care. Its about us, the humans, having to deal with tokens that we ourselves can no longer make sense of. The computer had zero issues. It just so happens that the 'const x: const T = …expr…' was more readable.

  6. Having the type in the middle also has the nice effect that it can be omitted. Some languages just let you do "x := 5;" and the type is infered at compile time (like the var or auto keyword).

    It's just nice to use

  7. Personally I find it nicer to read and write when the type is first, mainly because I always declare variables on individual rows, and when you declare a bunch of same-type variables they align much nicer when the type comes first.

  8. Maybe a little cherry-picking of languages here. The "modern" (if you want to call Python modern) choices shown is a small set. Pascal is an older language, but uses the name: type syntax. C# is a newer language, but uses the C/C++ style syntax. They're kind of all over the place.

  9. programming not math ( *, +, -) it is not supposed to use them as a math operator anymore

    they complicate the programming and rarely we use them as math operations.

    when we need them, we may use functions like add()…

    or using latex way $x=2*exp(a+b)$

  10. Some unneeded aesthetical fact: mathematicians use colon followed by type after var name in the Type Theory. Like “a : A” means “a has a type A”.

  11. I think this needs to be looked at from several angles.

    The first one, obviously, is that we have found a way to express almost all programming concepts and paradigms in C-style syntax, and until there is a fundamentally new way of doing things on the horizon, there is nothing new to invent in terms of C-style syntax. So, language designers will naturally look to develop new ways to express those ideas in a different syntactic format. And where I'm concerned, they should keep looking, because they haven't found a convincing alternative yet.

    Another way to look at it is that C and its derivatives are abstractions on a computing model. They fundamentally represent the way a computer architecture works in an abstracted syntax form. Java models how the JVM works, C# models how the CLR works, C models how a portable version of assembly for a von Neumann architecture works. C++ does the same, in essence, only adding a lot more ways to express the same thing and metaprogramming on top.

    And then there is the imperative vs. declarative realm. Imperative languages structurally care about what things are (types) and how they're organized in memory. How things work is encoded along with the relationships between things in ordered lists of instructions.
    Declarative languages care about the relationships between things first, make how things work implicit in the declaration and treat types as almost an afterthought. You can see this line of thinking in the way declarations are ordered in those respective language families.
    Let's say for instance I want to declare something in C which is implicit in its meaning but has a name, then I use a function.
    But since C clearly falls into the imperative camp, whenever I want to use this declared thing, I cannot just use the name, I have to write out the function call. This makes it explicit, while in a declarative context I would just use the name and let the compiler figure out the semantics.
    One is giving instructions, the other is stating intent.

    I'd love to have a way of organically declaring implicit behavior in C-style syntax, btw, ut so far, this seems to be quite hard to do in that syntactic context. Otherwise, C++ would surely have adopted it by now, just like it has adopted pretty much every idea about programming under the sun (badly, in some cases).

  12. The TypeScript-ification of actual real programming languages is the most cringe trend in modern software engineering. Embedded systems should not be coded in a language that uses a syntax similar to the terrible TypeScript syntax. No serious project should. But apparently now we've got frontend developers who barely understand system architecture who want to pretend to be systems developers. But they're too lazy to learn the real systems programming language (C), so they had to make a TypeScript clone for systems programming because that's all they know. I will never take Rust or any other language that uses TypeScript syntax seriously.

  13. Seed7 uses "declaration-kind type: name is value" structure, for example var number: A is 7 and there are no pointers, because pointers are dumb. And so the problem of standalone multiplications is solved – because it's always a multiplication, possibly overloaded, AND if you want to do a standalone expression you have to explicitly ignore it (kinda like void in JS but this one is actually practical because otherwise you get an error).

    p.s. and you can't declare multiple things in a row irrc, you have to declare each individual "declaration-kind type: name is value" thing, also you can't have undefined variables or use variables that are declared later than you use them (even if it's inside a function, functions also have to be parsed after all, and some of them run comptime).

  14. Really the biggest reason (which you kind of touched on at the very end) isn't even about pointers _specifically_. With `value : Type` you get a very unambiguous separation into two "universes" – an expression grammar, and a type grammar, and never the twain shall meet! Like, as a parser (or a human reader), you're normally in expression-land, and as soon as you see a `:`, you know you're now in type-land. The pointer stuff, and the casting stuff, are just two specific consequences of this.

    Also – you didn't mention it but this `value : Type` comes from ML which actually arose the same time as C — it's only now catching on!

  15. As someone who largely prefers C syntax over the typical modern syntax, I'd personally prefer a language that tries to fix the ambiguity in C's syntax by changing the pointer/dereference symbol and casting syntax than one that uses 'let x: type' or adding a 'fn' keyword before each function. It's a very silly irritation and ultimately just personal taste, but I feel like it requires writing extra information that doesn't really need to be there. Odin's 'x: type = value' is probably my preferred option of the non-C syntaxes, solely because it doesn't need an extra keyword anywhere lol. In the end though, it doesn't really matter, I've written plenty of code in TypeScript, Rust, and Haskell, and it never amounts to more than a slight annoyance, but I think my "perfect" language would have C syntax

  16. 4:24 just for correctness sake, treesitter does a parsing step, as in when you work with treesitter your goal is to have an AST to work on to make transformations/analysis (like, deciding how to highlight code)

  17. I do typedefs , like jpt, js. That's it and only have few of these. Have no problem reading it.
    Suer would be nice of some parts of C would be improved, but personally I preferer C syntax to those other ones, especially with my own macros and typedefs.

  18. 1:05 Actually it is possible to know what's a pointer and what's a multiplication. And the method is… Just use the space's. If there are no spaces after the name, then it's obviously a pointer. Else it's a multiplication operation.

  19. I haven't written any C code in over a decade and I'm still furious that in declarations, the pointer asterisk and array square brackets are part of the declarator rather than the specifier. "int* x" looks like, "Declare a pointer to an int, and call it x," whereas "int *x" looks like "Declare an int, and call it whatever x dereferences to."

  20. It's nicer for code completion tooling too. It's clear you want a type after a colon, but literally anything is possible at the beginning of a line in C.

  21. I've been writing mainly C & C++ for years, and never realized the intent was for the declaration to match usage. I just assumed they were crazy or picked the syntax that was easiest to parse with no regard for readability. imo this was a terrible idea to set as a fundamental goal

  22. Lexer does not need to know types. From lexers point of view its always ID, Star, ID (foo* bar; or a * b). Parser is responsible for "understanding" if it is a type or an op.

  23. It gets worse. If you have a semantic error in a program, then a good compiler can still check the remainder of your program using partial information. However, if semantics is required for proper parsing, then a semantic error (such as forgetting to declare a variable) now causes a parse error, which is far more difficult to recover from, to the point where simple parsers don't even try.

    And don't even get me started on SystemVerilog and VHDL (the languages I parse for a living).

  24. WTF is he saying at 10:18? He slurs as if he was drunk or something. It could be "winds up having to keep a symbol table around", but it sounds lik "slytph". And it makes no sense anyway. You need the symbol table no matter what.

  25. Okay so while I'm not generally a fan of whitespace as syntax (Python…) hear me out: What if putting a space between the asterisk and the identifier made it multiplication and not using a space made it a pointer? Because honestly who writes code like `a*b` anyway? Just enforce that you need spaces around binary operators so it has to be `a * b` and you may not have spaces between a unary operator and an identifier so `a* b` clearly indicates that the asterisk is part of the the type `a` (also the silly nonsense where the asterisk can be next to the type or the ident should not be allowed, it should only be allowed next to the type) and then you can still do `i++` since that's unary and you could even have `a ++ b` do something else because now we can tell if it's the binary or unary operator based on the spaces. Also nobody is defending the `(Type)variable` casting syntax, that was always dumb, perfectly reasonable to do `variable as Type`.

Leave a Reply

Your email address will not be published. Required fields are marked *

You might like

© 2026 Cantinho do Vídeo - WordPress Video Theme by WPEnjoy