3 ms·
There are so many ways things can break if used incorrectly. I don't disagree with most of the things you listed. But what's the conclusion? > The real problem
by jstimpfle 9y ago
There are so many ways things can break if used incorrectly. I don't disagree with most of the things you listed. But what's the conclusion?
> The real problem with pointers is that they're effectively an algebraic data type
No, they are not. Join my religion and preach: pointers are just data, pointers are just data. And be free.
C is about machine representation, and it's typed just enough to map to hardware (specify ABI of functions). Don't be deluded to think you can represent all (or even many) invariants in types. Some other languages try to do that (and it always ends up in a big mess).
If you want more "safety" - which (for the things you still need to implement) just means crash in a more friendly way, not less - then use a managed language. But they come with their own pros and cons, of course.
To address the issue with nanosleep, this is not what we were talking about. nanosleep does expect a null pointer in the second argument. (And I think in this rare case an assert(req) would be justified. Or maybe just split it into 2 functions).
- simias 9y agoBut they are not just data because the language handles NULL differently from other values. The compiler is allowed to treat NULL pointers differently from other pointer values. NULL is special in C, it's not just a pointer to address 0. >C is about machine representation, and it's typed just enough to map to hardware (specify ABI of functions). I don't understand that at all. How do enums map to hardware? How do structs? Clearly the line is arbitrary. Would adding generics mean that C maps less to hardware? What would that even mean? And if C is all about talking directly to the hardware, how come the language has no first class support for SIMD, for preload, for the various CPU flags? And to get back to our point, how does NULL map to hardware? A CPU has no issue addressing at 0, it's nothing special. An assembler doesn't treat address 0 differently from any other, unlike C explicitly does. Join my religion and preach: C is not a macro assembler, C is not a macro assembler. >Don't be deluded to think you can represent all (or even many) invariants in types. Some other languages try to do that (and it always ends up in a big mess). [citation needed] >To address the issue with nanosleep, this is not what we were talking about. nanosleep does expect a null pointer in the second argument. (And I think in this rare case an assert(req) would be justified. Or maybe just split it into 2 functions). My point is that in this case the rem pointer is an algebraic data type. Passing NULL here doesn't mean "please store rem at (void*)0" it means "I don't want rem". Except this is not expressed in C's type system so you don't know that unless you read nanosleep's implementation or its documentation. the rem pointer, unlike the req pointer, is nullable, therefore it's effectively an option type but as far as C is concerned they're the same type. If you assert() it then you can catch the problem early on at a runtime cost (and only when the situation eventually arises). If you make this part of the type system the compiler can validate that statically at compile time.
- jstimpfle 9y ago> How do enums map to hardware? How do structs? As integers? Sequentially in memory? And that's about as much as I care. > Would adding generics mean that C maps less to hardware? What would that even mean? It means that you have control. That's what you need for good and (equally important) consistent performance and low memory footprint. You have pretty good basic low-level abstractions (integers, pointers, structs, functions) that are very convenient to work with (much more convenient than your typical assembler language), and at the same time as low-level as you care to go most of the time. And even then, toolchains have good support to go even lower when you need to. > [citation needed] My personal experience with simple languages like C, Python, and complex typed ones like C++, Haskell. (There are also boilerplate ones like Java (not interested, thanks), or ones that swallow errors too easily to be productive, like sh, Javascript). Also, look at other projects. Look how successful game programmers use C++ (In my filter bubble I watched people like Jonathan Blow, Mike Acton, Casey Muratori on Youtube, and have been following Sean Barrett or looked at imgui or quake3). What they do is pretty much C. I have also been following the Haskell community quite a bit and watched C++ experiments like boost, and they simply have a different focus. They don't get things done (in general), their builds are time consuming and brittle, their code is hard to understand, etc... > And if C is all about talking directly to the hardware, how come the language has no first class support for SIMD, for preload, for the various CPU flags? It's not assembler... It's "portable assembler", as some people like to say. And if you need support for SIMD etc, just take second class support or drop to inline assembler. No point in arguing here. > And to get back to our point, how does NULL map to hardware? A CPU has no issue addressing at 0, it's nothing special. An assembler doesn't treat address 0 differently from any other, unlike C explicitly does. There are always these language lawyer type unfortunate exceptions and complexities that can be explained by a little bit of history. Personally I'm mostly on x86 and I sometimes assume it's binary 0, but actually I don't really care that much. > A CPU has no issue addressing at 0, it's nothing special. An assembler doesn't treat address 0 differently from any other, unlike C explicitly does. Making my point. It's just data. I think NULL is actually just ((void * )0), but it's the representation of 0 that is special on some architectures. That the representation might not be all binary 0s on some architectures is probably pretty complicated to explain. As far as I'm concerned it's a language lawyer thing and I don't care. C might technically not be simple, but your use of it can (and maybe should) be simple. > My point is that in this case the rem pointer is an algebraic data type. What is is? If you insist on looking at it like that, why don't you use Haskell? Though, you will have to take a performance hit and might have a harder time developping new features, because modularity is very bad due to complex typed interfaces. By the way, I know how the nanosleep interface works. I don't have any problem understanding and remembering that and using it correctly (and it's not like I have used it more than 3 times in my life). I don't think I would make an error, but I wouldn't care if the null option would go away completely, either. It's much harder to correctly use struct timeval / timespec than calling nanosleep. The null thing is totally a theoretic non-issue.