New language Carbon

Re:Servo

The JavaScript engine was incompatible. Servo’s code is still being scavenged for use in FireFox.

Why recompile Windows 1 sources for Windows 11 when Windows 1 is small enough to run in 640k and contains no spyware? :thinking: :smile: Best of all, Windows 1 wasn’t written in C++, it came out in 1985. C++ came out in 1995 with the first ISO standardized version in 1998. Seriously, I don’t use Windows for anything.

1 Like

The important part is that I still use it every day. Yes, I am not 100% happy with it. But so far I was not really ocnvinced by any of the other languages. I use it every day becaue when we started a new project at work, I decided to do it in C++. Of course I did, what else would we use?

Of course in that context there was a lot more constraints than “which language is cooler”. I asked my colleagues if they would be ok. I knew they all had experience with C and some with Java. But also the language exists for a long time, several people in and outside the company have good experience with it, etc.

The only reason I do not complain about Go or Rust is that I spent about 15 minutes with each and couldn’t really make sense of what they bring to the game. They don’t seem to solve any of the problems I’m having. Rust syntax is just as cryptic, and Go has other problems such as performance being limited by the Garbage Collector, and back when I looked at it it had no generics. Likewise I had a lot of complaints about Java back when I worked on it (that language has no “const” at all, how are you even supposed to write code with it?!)

Also if you look at things like the TIOBE index you can see how Rust, despite having a very vocal community, is actually still a very “small” language in terms of adoption. This is not what I would bet on for a 20±year long project like Haiku. What would you have picked 20 years ago? D, maybe? Would that choice look reasonable today?

So, if we were considering changing languages for Haiku, that’s what I’d look for first: a mature language that guarantees stability for several decades. And yes, that probably means not picking one of the exciting new things. We have enough problems to solve already with building the OS, let’s not make it harder by picking a language where there is generally few experienced people behind it.

And almost to prove that point, look at the other alternative operating systems. Which one have you heard most about recently? Haiku seems to be the leading one, and behind it are maybe (in unsorted order) ReactOS or Serenity, and maybe the Amiga family. But it’s been a while since we heard anything of RedOX OS, the one written in Rust. Their last release notes don’t look super exciting to me.

Yes, I agree, there are a few things we could change, but I’m not inclined to do total redesigns. To me it seems the best way to have the new, supposedly better, but still unfinished version, while some people decide to maintain the old one for the time being, and things never move on from the old to the new.

Let’s work from what we have, maybe in R2 there can be a few breaking changes, but I would not start from a blank page, just identify the main “pain points” in the existing API and see what we could replace them with. But to start from scratch? I wouldn’t even know where to start, and there are a lot of things I would keep just the same anyways.

I don’t think so. Haiku is a C++ project with devs who have been, for some of them, doing C++ for several decades. I don’t see the point in switching to another language, losing all that experience, and discovering that the new language just comes with a different set of problems.

Also I think right now, Rust and Swift are still much too young to really know their problems. We will learn about that after a few years, where people are maintaining old Rust codebases, the language has gone through a few revisions, etc.

I think having one language as a first-class citizen really works well. For UNIX, it is C, and everything is measured in terms of “but can it interface with the existing C code?”. As a result, a lot of the other languages, which are designed to run on UNIX like systems, will always have some way to interface with C.

For BeOS and Haiku, this language is instead C++. I would not change that. We have to accept that here, we have chosen C++ as the common language for our APIs. So I put the challenge on the other side: can other languages add support for interfacing with C++ just like they did with C?

There is at least one precedent for that: Swift is built by Apple with one of the goals being to interface with the existing Objective-C code, and I think they have done a pretty good job of it. And now it looks like we will soon have Carbon as an option with similar promises. If we have to start with something, it has to be the one?

And there is also the approach taken by Serenity, we talked about it a few weeks earlier: they started a new language by making a compiler that outputs C++ code. This is a lightweight approach in terms of development efforts and allow them to experiment with the language design. Then later they may implement a proper compiler when they decide the language is ready.

4 Likes

You can use Kotlin for example. It transpiles two-way to C++ so you can have it build C++ statics/libraries if needed: Kotlin Native | Kotlin . That said I never worked with Kotlin. I just know it can do this.

I dont really understand the point of zig as a C alternative. At least their front page is not selling it to me at all… “no hidden control flow, no hidden memory allocations, no preprocessor, no macros”. I dont really see any of those as problems in C.

The example code on the front page shows an imperative language, with minor differences to C (ok, it has type inference and modules), parsing json. If I wanted to parse json why would I use C? Is this just so it appeals to JS developers? Also the example code has const payload = ; … is that valid? It looks like really nasty and broken syntax.

C is miles from perfect but I dont see how this is the solution…

Interestingly, Carbon is created using C++.

So, they’ll still need C++…

…Until the import engine is complete.

Not sure about this one, since Kotlin still relies on the JVM. That being said, it seems to be an okay programming language. Personally preferred Kitlin to Java, but haven’t spent as much time with the former compared to the latter.

That’s exactly the opposite of what the linked page says.

Just like for Java, the JVM is not anything magical, it’s a virtual machine for a made up CPU, that’s all. And if you decide to, you can compile Java or Kotlin or any language to run natively. Or you can write a C compiler that outputs .class file to run on the JVM.

Haiku developer on Haiku forum says hey, I seem to hear a lot more about Haiku than other obscure systems… I wonder why that is.

The reason for this behaviour isn’t because of the relationship between C and Unix, but because the C ABI is very, very thin, almost transparent. So it’s not a lot of work to use this ABI, although it also delivers almost nothing of value you might as well. It’s a “lowest common denominator”.

These days, every 64-bit platform of note (other than Microsoft’s MSVC) uses the Itanium C++ ABI (yes, even though Intel’s Itanium CPU family didn’t make it the ABI did), so you’ve got the exact same quality of guarantees for that as you get for C, however the C++ ABI requires interoperability with a lot of arcane C++ internals, so once again the lowest common denominator is the subset of the Itanium ABI which applies to C.

If C++ is supposed to be a “first-class citizen” on Haiku I’d say citizenship seems like a bad deal. Even modern C is more elegant than the 1990s C++ APIs offered by BeOS and thus Haiku in 2022.

Suppose I’m wondering how big the file “bigfile.huge” is, in Rust of course I can just ask for this file’s metadata and ask the length of the file, it’s an unsigned 64-bit integer.

In C++ 17 I can do similar directly through std::filesystem although weirdly I get the maximum possible size of unsigned integer (are files actually going to be too large for a 64-bit integer? It seems unlikely)

But in the Be Storage Kit I’m asked to get myself a BFile, or BEntry or something, and then I call a method on it, which takes an out pointer, and the out pointer should point to a prepared off_t variable - the return type of the method is a status value indicating if it worked. That probably felt familiar to 1990s C programmers, but it’s not idiomatic C++ in 2022.

1 Like

Because Zig can import C source and apply its rules to it, making it easier to see bugs and undefined behaviors. It’s not a complete product but hopefully, once it’s finished, it will have a package manager and other nice things that C doesn’t have by default.

My whole life is not in this forum, thanks.

Not really, the C ABI is also full of strange things you don’t even think about:

  • In which order are the parameters pushed on the stack, left to right or right to left?
  • Does the caller pop arguments from the stack, or is it the callee that should do it?
  • How do you implement function with variable number of arguments? If you handle them differently from other functions, how do you know from the symbol name how to call a given function?
  • What if your language has exceptions? or coroutines? Will that mesh well with C code?
  • Are your strings NULL terminated? Or do you have a length indicator at the start?

It’s just that C has been there in UNIX and other systems for a very long time, it’s the one thing everyone knows about, and so other languages more or less follow the same conventions.

The examples I listed are not made up, you will for example find that Pascal passes arguments in the opposite direction, has the “length at start” way to handle strings, and so on.

So, really things kind of standardized on C because it was tied with UNIX from the start. On classic Mac OS, the system API was closer to Pascal. On Windows they had some support for both but it was mostly the C style.

But you don’t get the Metadata directly, you get it through some indirection, because getting the metadata may fail. And if you don’t handle that correctly, your program just stops and shows a message like this:

Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }

You can check that here as I wrote a test program to make sure what would happen: Rust Playground

according to std::filesystem::file_size - cppreference.com, you may also get an exception, or if you don’t like exceptions, you get an error code from an argument that you have to prepare in advance.

I’m not sure what your point is. In all 3 cases, you either get a size in return, or you get an error in the typical way errors are returned in the local language and dialect, and you have to somehow check if an error happened. The BeAPI option is the only one that doesn’t immediately stop your program if you forget to handle the error. Is that a good or a bad thing? It’s up to you to decide. When you’re writing and debugging the code maybe you want the program to immediately stop. When it’s running on your customer machine, maybe you don’t. But it isn’t so hard to handle that, at least in the C++17 case, you can usually manage to catch exceptions in a reasonable place, and try to not lose any data.

1 Like

Which brings us back to the old-age choice between “look before you leap” or “pray for forgiveness”. BeAPI seems to be the former, C++ exceptions are the later. Personally I prefer exceptions if they are not abused for flow control… which unfortunately is done way too often.

The C language doesn’t specify anything about how parameters are passed to functions, that’s a “calling convention” and is implementation defined. On a modern CPU the parameters will mostly live in registers, not on the stack at all. This didn’t magically stop C from working, it’s just a different calling convention.

C doesn’t have exceptions, or co-routines, or lots of other fancy modern features, it also doesn’t have virtual functions, destructors, and lots of other C++ features. The Itanium ABI (the de facto ABI on Unix today) specifies these because it’s a C++ ABI.

Likewise, although the weird choice to not remember how long strings are but instead use a sentinel in the text itself is widespread in Unix because of C, it’s not crucial to the ABI that strings in your language work this way.

Ah no, though I can see why you might not realise. In the Rust case this is indeed true, Result is a sum type, it’s either an error or it is your Metadata. But C++ didn’t really have sum types back then, so there are two options. In the C++ 17 API with exceptions, you either get your result or your control flow is wrenched away and you take an exception somewhere. In the Be API (and the no except C++ 17 API) you get both a size AND an error code. Maybe the error code is OK? What does the size mean if it isn’t, or if you don’t check? Who knows. The Be Book doesn’t specify.

You got a Result from your call, you gave your main() function a signature indicating that it can fail, and you wrote code to explicitly propagate Err Results from main() to its caller, so it seems to me that what you wrote did handle this error, and you decided it should show the message. Or, I suppose, you copy-pasted and the code you worked from decided that.

That’s true, since at least C++ 98 it’s idiomatic C++ to use exceptions for this. Haiku is newer than C++ 98 but of course BeOS was not.

Rust won’t compile without choosing what to do with the error, you chose to have it abort the program and report the error to stderr. If you explicitly throw it away (as you might have done by mistake with the BeAPI) then in that case you don’t have a file size, so you can’t mistakenly press on with zero or undefined values or whatever.

But I actually wasn’t interested in the error behaviour, although of course what Haiku does there is similarly bad to most software from that era (the 1980s and 1990s). I was focused on the unergonomic out pointer. The out pointer is a weird hack from the early C days, it wasn’t necessary by the time C89 was standardized but of course the C standard library blesses a lot of out pointers because much of it pre-dates C89. The weird thing is that this unergonomic behaviour spread not only into new C code (where it wasn’t a good idea but might make your archaic C compiler work) but into this older C++ where it never made sense. And that’s how it’s in the 1990s Be API.

1 Like

That might be desirable for some, but it is an example of the fine line between modern “let the machine do all the work” and how it was when I started “you need to actually understand why that is bad”. Hand holding is great for hobbyists, because it encourages then to write code. When you get to pro level, programmers that only rely on the tooling to get safe code are just bad programmers.

If you working for a living you must be adaptable and capable of jumping between different tooling. You can’t force zig or rust or whatever. You need to work with what you are given.

Example: yesterday I wrote a NTP client for BeOS PowerPC. I did it for fun. I used BeIDE. What I noticed - the ide gave me absolutely no help. No surprise. It is from 2000. That is how it was back then. No code suggestions, no squiggly underlines, the best way to check the code was the compile it. You know what? I now have a working NTP client. You can do anything if you want to. You don’t need fancy tooling if you have experience.

Package managers are one of those things that is great, till it is not. On the one hand they make you feel like you are one command away from solving most problems with a third party library, but the big issue with them is availability. It relies a lot on being online, versioning and goodwill of third parties to update with the latest compiler versions and language features.

3 Likes

Modern C++ has gone in a strange direction and relies too heavily on the STL. The interator stuff is also hideous.

The thing I like about the BeAPI is that is has a pretty similar design to a lot of other libraries from that time (Qt, VCL, even MFC) so I think it is pretty sane.

That’s fine. Rust was designed to be safe. The problem is, and @SamuraiCrow backs me up here in a reply above, to do anything useful outside of the Rust ecosystem, you need unsafe code. So really, Rust solves the safety issue (so do other languages to differing levels) but to do low level stuff you end up writing unsafe code.

1 Like

I would go so far as to declare that no universal notion of “code safety” exists. There are many examples where the very same coding concept (e.g. goto) may be seen as “absolutely unsafe” or “reasonably safe”.

That being said, any situation where the language syntax explicitly forbids some coding concept, at the same time it imposes some limitations on what can be expressed in that language.

As for goto with all its unsafety, it allows to program loops, that is, write a routine for loop concept without having loop primitive in the language. This is important, because if you can program the loop coding concept, you can customize or extend it, while in case of just loop primitive you can only use it without such extension / customization.

1 Like

I can’t really agree with that. It’s like saying you don’t need a seatbelt in your car because good drivers don’t get involved in car accidents? I think this debate was settled a few decades before I was born and fortunately all drivers have to wear seatbelts now.

Can we make cheaper, lighter and more efficient cars by removing all the safety features? Yes, certainly, the airbag, ESP, and so on add quite a lot of complexity and weight. Should we do it? I don’t think so.

The situation is the same for programming languages I’d say. Yes, some of the safety features are annoying and get in the way a little bit. Yes, they probably make your program less efficient. But, it helps a lot. And we can still teach people why these things are here, and what they’re useful for.

Also, if you take the example of Rust, bad programmers will have a hard time getting things to compile at all if they don’t understand the memory model. This seems better to me than languages where they will instead get crashes at runtime?

If you like to write low level code for fun, sure, that’s fine. I enjoy it too. But in my paid job, I appreciate that most of our time isn’t spent discussing memory leaks, or stack corruptions because someone forgot to pop a register when exiting a function in hand-written assembler. Instead we can talk about data structures and how to implement algorithms efficiently, for example. Seems a lot more productive to me.

4 Likes

Not everybody has this luxury. C99 is a requirement support language we have for a complex library system (besides high level languages). And yes, these are production type systems. I don’t like driving without safety features but if this is the only thing you have you learn how to drive to get the risk next to 0. To be honest the type of crap code i’ve see from high-level programmers is gruesome. That’s the problem with safety features. People totally forget how to write programs. As long as it compiles it’s good, right? Not the first time I had to debug for days a crash in Swift Foundation classes because a compiling program managed to push the memory manager over the cliffs. Nope, I prefer the more convoluted C++ class model where I can actually deduce where something can go wrong instead of wading through vendor convoluted stack traces to figure out why the hell a Swift program segfaults in a totally banal code.

High level code is good. Safety features are good. But they make people lazy and foster ugly, broken and unmaintainable code.

Sorry for the rant. Rant-mode off.

1 Like