5 ms·
I don't think C is un-opinioniated. It's opinions are just quite popular, so people don't think they are opinions.
by zucker42 7y ago
I don't think C is un-opinioniated. It's opinions are just quite popular, so people don't think they are opinions.
- skohan 7y agoI mean, there are always matters of degree. Sure C imposes some structure by nature of it's syntactic choices, but it's extremely wide-open when compared to other programming languages. Lisp, for example, is very opinionated when it comes to things like side-effects. Go is opinionated when it comes to how memory should be managed. Rust is opinionated about when data should be readable and writable, and passed between threads. C doesn't impose any of these restrictions, largely by virtue of being low-level, and moreover, it should be possible to implement whatever data transforms are implemented in any of those other languages in C as well. The inverse is not the case, so I would argue that C is more permissive than any of those languages.
- robto 7y agoIs Lisp opinionated about side-effects? Maybe Clojure is, but have you looked at Common Lisp or Emacs? I can't think of a less-opinionated-about-side-effects application than Emacs.
- kazinator 7y agoLisp is opinionated about side effects like this. * In the absence of language extensions, you don't have a pointer type that can be incremented all over the place and dereferenced. You don't just mutate memory willy nilly. * In relation to the above point, (in the absence of a language extension like "locatives") there is no address-of operator; we can't pass the address of a variable somewhere to have it changed. This can be simulated with a lexical closure, whereby we provide the function to do perform the change, which is called from somewhere. "Use lexical closures as thunks if you want var parameters" is quite opinionated. * All mutation takes place through data-structure-specific functions (like rplaca for mutating the car of a cons cell), or dedicated special forms (like setq for mutating a variable). The use of generalized places like (incf (car x)) compiles into type-safe function calls. On one particular Lisp, CLISP, that looks like: 1> (macroexpand '(incf (car x))) (LET* ((#:TEMP-3341 X) (#:NEW-3340 (+ (CAR #:TEMP-3341) 1))) (SYSTEM::%RPLACA #:TEMP-3341 #:NEW-3340)) there is no idea there of calculating some effective address and just clobbering bits through that pointer. The original value is accessed using car, and the mutation happens opaquely inside the SYSTEM::%RPLACA function. That could be inlined by the compiler, but the model is basically that mutation of objects is done via an API. * The argument evaluation order of standard functions is well-defined, so side effects occurring in argument forms are well sequenced: (let ((x 0)) (list (incf x) (incf x) (incf x))) --> (1 2 3) ;; required result in ANSI Lisp (Scheme bungles this by having less of an opinion; it has C-like unspecified argument eval order.)
- earenndil 7y ago> Lisp, for example, is very opinionated when it comes to things like side-effects Ermm...no, it's not. Functional programming languages do tend to do that, but most lisps aren't functional--certainly, it's not a defining characteristic of the language family. > C doesn't impose any of these restrictions, largely by virtue of being low-level, and moreover, it should be possible to implement whatever data transforms are implemented in any of those other languages in C as well. The inverse is not the case, so I would argue that C is more permissive than any of those languages. 1. Turing equivalence. 2. Because it's so close to the hardware, c inherits the opinions of all the major ISA designers.
- skohan 7y ago> Ermm...no, it's not. Functional programming languages do tend to do that, but most lisps aren't functional--certainly, it's not a defining characteristic of the language family. Fair enough. I haven't worked with Lisp since university days, but I think the point stands with any strict functional language. > 1. Turing equivalence. Ok technically it's possible to write programs which perform the same computation any two turing-complete languages, but what I mean is that, for example, it's easier to emulate how an arbitrary Go program performs that computation from C than in the other direction. edit: > 2. Because it's so close to the hardware, c inherits the opinions of all the major ISA designers. That's one way to look at it. Another way is that C just gives you an interface to the hardware as it is, and doesn't make many judgements about which abstractions should be built on top of it by default.
- wbl 7y agoC is not close to the hardware: vectors are a bolted on afterthought, especially multidimensional arrays. The carry flag and floating point environment are afterthoughts. One of my coworkers uses assembly for just this reason!
- goldenkey 7y agoC does not have vectors. C++ has them. Do you mean arrays? Arrays are just translated to pointer arithmetic formulas based on the type/size of the array's elements.
- kllrnohj 7y ago> C doesn't impose any of these restrictions, largely by virtue of being low-level, and moreover, it should be possible to implement whatever data transforms are implemented in any of those other languages in C as well. You're conflating lack of guardrails & helpers with "low-level". C is not lower level than Rust or C++, not at all. It's just more dangerous, and that danger brings no "low-level-ness" advantages. > it should be possible to implement whatever data transforms are implemented in any of those other languages in C as well. Define what you consider "possible" here. There's a bunch of things I'd consider "not possible" in C that you can do trivially in C++/Rust, such as the codegen templates & coroutines perform, for example. If you throw enough macro pre-processor hacks at it to the point it doesn't reassemble C in the slightest sure you can sort of pull it off. But is that sort of thing considered "possible in C"? Or have you just made a second language in front of C that happens to compile to C via the hello world of compilers? Even with macros I'm not entirely sure you can actually do something like static_assert in C since you can't put an #error in a macro substitution. Or more practically defining the backing type of an enum
- kazinator 7y agoC is not opinionated enough w.r.t. side effects and order of evaluation, which means that programmers have to learn to be highly opinionated "human compilers" to avoid writing bogus code.
- jerf 7y agoThere's a reasonably unambiguous way to measure how "opinionated" a language is, which is to look at the size of its runtime necessary to run programs in that language [1]. C's is not huge, but it is necessary and non-zero. By contrast, many other languages have huge runtimes. There are some languages that can get smaller than C. Assembler, obviously, is basically the smallest. Rust does have a default runtime, but it's thin, and most of the rest of it can be shut off. Still, it's not a long list of languages that are less opinionated than C. [1]: If you want to get really technical, this is "relative to the hardware it is running on". C gets a bit of a bonus here because the hardware has over the decades actively targeted C. Particularly visible in the processor's support for calling conventions, and manipulating a "stack" so natively. (While a stack is really, really convenient, it is not, strictly speaking, necessary for programs to be structured that way.) But for the sake of simplicity I'll ignore that for now.
- skohan 7y agoI think of "opinionated" more as a measure of how how much structure a language imposes on you as a programmer. For instance, Javascript has a massive runtime, but I'd say it's an extremely unopinionated language. If you want, you can implement a Javascript function which rewrites itself every time it's executed. In my opinion Rust is an extremely opinionated language.
- jerf 7y agoThat's a perfectly valid definition of opinionated, and it measures another aspect of a language. Languages have a lot of aspects. I used this aspect because upthread we were discussing memory management and "portable assembly". In another context I'd happily use this concept instead.
- kazinator 7y agoIndeed. For instance, C has opinions like: - If you're a variadic function, you're forbidden to know directly how many arguments you have been given and what their types are" - If you're any function, you may know who called you, without resorting to platform-specific extensions that come with various limitations. - You may not know which areas of your activation chain are root pointers, for implementing accurate garbage collection. - You may not dynamically construct a function call with arbitrary arguments and invoke it