Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

why does every attempt to replace C++ make syntax of the new language significantly uglier than C++?

why do I need to type "let" and "fn", etc?

why not make the syntax very similar if you actually want to make it easy to migrate?



It's somewhat related to solving one of C/C++'s real problems: the syntax is very context sensitive. That makes tool development harder.

Personally I think it's great that most new languages seem to move in roughly the same direction when it comes to syntax.. like

"x: int" instead of "int x" for variable declaration, and something similar on functions "fn", "fun" or "function" prefix before functions "let"/"const" for constants and/or single assignment variables "var" for variables etc.

I think there's a reason so many language designers are making similar decisions on these things.


why is this great? what's the reason? x: int is significantly less readable than int x and involves typing one more character. Why?

what is the advantage of type int fn foo versus int foo() {} ?

why type var in front of a variable when int x makes it clear its a variable?



“Significantly less readable” is subjective.

It is simpler to parse, and degrades better with type inference. That’s 90% of it, I’d imagine.


sure, well, it's not subjective if you have to type more characters to basically achieve the same thing. I guess I just don't really understand why you have to fix C++ shortcomings while also changing things that work fine. Is there a C++ developer who is having trouble declaring variables as int x?

By making big changes to syntax, you are just making it less likely this ever gets truly adopted.


Simple types may be easier, complex types are a real pain for both humans and compilers, and they end up hiding the name in the middle of the thing. Brevity is neither the only nor most important thing on which to judge syntax.


A language with good type inference lets you type less because you don't need the colon or the type, where as a language with it first you always have to type something like var for a "figure it out" type.


Something like "int* a, b" not doing what people expect it to do is a very common C++ footgun.


auto + implicit coercion + pass by value semantics and copy constructors create real bugs and pitfalls in C++ code. Avoiding these semantics and deferring to explicit syntax leads to better code.

Optimizing for character count is dead.


From TFA:

  // Returns the smallest factor of `n` > 1, and
  // whether `n` itself is prime.
  fn SmallestFactor(n: i32) -> (i32, bool) {
This is so unlike C++ I cannot see how anyone can consider it a successor language. It might, in fact, be a much better language, but it is not a successor when you're going to change syntax this much, and in a way that will be hard to automate (precisely because of the "most vexing parse" problem(s).


Is that really so different from the following C++ equivalent?

auto SmallestFactor(int32_t n) -> std::pair<int32_t, bool> { ... }


I guess I don't understand what role the -> plays there. I must be too old school, I suppose.


In this particular example, all it does is reverse the order of return type wrt arguments and function name. I still find this syntax more readable with complicated return types because you no longer have to carefully parse it to see where the type ends and the name begins.

In the more general case, having the return type follow the function name means that it can reference parameters in sizeof(), decltype() etc.


I can't speak for the C++ example, but in your code snippet `->` points to the return type. The function takes an integer argument and effectively turns it into a tuple, so: `f(i32) -> (i32, bool)`.


Swift is a successful successor to Objective-C and their syntaxes and idioms are worlds apart. It’s about what class of problems you can solve in the language.


I don't agree with your analysis. But that's fine.


Even C can't be parsed purely syntactically; you need to know whether or not an identifier names a type. C++ is worse: https://en.wikipedia.org/wiki/Most_vexing_parse


right, so some simplifications or improvements are fine, but it doesn't follow that we should now have to type var, let or fn everywhere - it just makes the body of code a lot uglier

auto time_keeper = TimeKeeper(Timer());

this is nice and clean


> it just makes the body of code a lot uglier

I’d suggest learning a couple more languages - after you’ve learned a few, you get used to it - you don’t even see the code. All I see is assignment, variable, parameter…

(But seriously, the vast majority of imperative languages have syntax which is near-enough the same that it really doesn’t matter - much like two spaces vs four spaces, you can spend days, weeks, months arguing about which is better… or you can flip a coin, pick one, and after 5 minutes of using it you’re used to it, freeing up your brain to worry about more useful things :) )


missing the point here, if the goal is to migrate people away from C++ to this new thing, it's easier. less error prone to keep syntax similar. That's why Java was so successful.


Copy constructor much?

(Also, I would take var over auto any day, to be honest; but no, they had to override the old useless keyword for some mysterious reason.)


The reason is obvious: to avoid adding a new keyword and breaking existing code that might use it as an identifier.


C++ had to add new keywords anyway.


It does, but N new keywords are always going to mean less breakage than N+1 new keywords.

On top of that, the best keywords - e.g. in this case "let" or "var" - are also the ones most likely to clash with identifiers in existing code.


Which if you projected the same syntax to 'let' syntax you get:

let time_keeper = TimeKeeper(Timer());

Which is one less character to type.


> why type var in front of a variable when int x makes it clear its a variable?

Unless `int x` is followed up by an opening parenthesis, in which case it suddenly is a function definition and not a variable. Which also means a parser now needs lookahead for one of the most common statements in the language.

`let` means variable, always. Not only that but in most cases (>90%) you can omit the type as it's obvious. Now you just have to type `let s` instead of `String s`, with the difference that `let` also tells you this variable is immutable, which isn't even possible in many languages in the first place.

> involves typing one more character

Who cares. 99% of devs don't, otherwise they'd use APL or its relatives, where the entire Conway's Game of Life is a single line. There are more important factors than the number of symbols on screen.


In the context of a language that needs to coexist with C++ codebases, it is a weird decision.

However, Carbon's syntax makes technical sense, and is in line with many contemporary languages (Rust, TypeScript, Swift, Kotlin). It is easier to parse, which is important for tooling and IDEs. `fn name` is easy to grep for. `let` works well with type inference and destructuring assignment, which are becoming standard features in modern languages (although Carbon still supports C-style aggregate initializers, which is not so great).


Not every, see Circle.

The only that really has a chance.



Except just like with C++ being a C superset at source code level, Circle is a C++ superset, no need to rewrite existing code, that is the big difference.


Then it's still C++'s syntax, and Circle's syntax is still "ugly". Every modern languages adopt the "ugliness" style of type after name to solve most vexing parse, yet somehow C/C++ programmers are still confident in their "beautiful" syntax.


Syntax being ugly isn't the problem for migration, rather 40 years of history deploying code into production.


That's irrelevant to the discussion about syntax being ugly.




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

Search: