Okay, libraries can't provide new inabilities. But that's why we have linters, static analyzers, style guides, compiler options and warnings, and ultimately code reviews and good taste.
I think the big problem (and sometimes benefit) of old languages is their culture. For example, C programmers don't trust anything written since K&R. This is both good and bad. Creating a new language doesn't force existing communities to change; it means creating a new community and either waiting for the old ones to die off, or (ideally?) driving them into obsolescence.
I remember when Wikipedia was new and there were lots of studies finding errors in their articles. Instead of replacing Wikipedia, those techniques for finding errors were simply incorporated into the editing process (as much as possible). I wish that as new classes of bugs came onto our radar, we could go back and refine old code instead of throwing it out with the bathwater. (Even OpenSSL wasn't beyond saving, judging by BoringSSL and LibreSSL. These projects still need more work, of course.)
Well, a lot of the point of these constraints is to reduce complexity, as the writer mentions. When you add constraints with most of those tools, you're actually adding complexity to your tooling, build process, and cognitive load if not to your actual code base.
And yet with those, we still have security vulnerabilities in C programs. That, I think, is a strong argument for languages which have very specific constraints. (For example: Rust.)
Do you have any idea how much software is run behind a firewall? If someone is even able to run my software that means they have full access to a shell and compilers for multiple programming languages. As such, there are much easier ways to run malicious code than to exploit a buffer overflow in my C or C++...
So really, your argument only applies to public facing servers or web browsers. Unless you're writing one of those, I think your argument is weak.
> So really, your argument only applies to public facing servers or web browsers.
Or, you know, literally anything touching a file format that might be downloaded. PDFs, word documents, images, zip files, movies, music, sounds, icons that show up in your file explorer, user profiles, any sort of web client (email, games, chat, wget, package managers, ...) lots of poison apples out there.
This kind of nonsense is why the best advice to handling USB drives you find on the ground is to toss them in a fire barrel - everyone thinks they're "behind a firewall" and that they're totally protected! They have all the protection of a wet tissue in a hurricane.
Oh, but I'm just writing a terminal! I don't touch file formats! It's safe to auto-execute commands in response to pasted newlines! ...and then you've helped enable the spread of malware through clipboard hijacking Javascript. Oops.
Firewalls are not a panacea. Firewalls are a roadbump.
> Unless you're writing one of those, I think your argument is weak.
I think your security is borderline nonexistent. I think I cannot trust you with C. I think someone is probably in your systems if you're in charge of the security side of IT, of anywhere of significance, unless you delegated enough responsibility to make you a figurehead.
I mean no offense by this. A lot of reasonably talented and smart programmers make the same mistake as you, and go write a bunch of exploitable vulnerabilities in C as a result. It's not like you're one of those madmen disabling ASLR, NX, stack cookies, or some of the many other mitigation techniques being used to try and clean up after this tragedy of our times. ...right?
Look, you're just another arrogant novice who runs around telling people how to be safe so you can convince yourself that you're safe. Maybe you'll grow out of that, or maybe you'll stay obnoxious forever. I doubt you want an actual answer, and I'm pretty certain you don't deserve one.
However, giving you the benefit of the doubt - There tens of thousands of programmers out there (myself included) who write software to analyze data. We don't make "apps" for users, and we don't download random PDFs or MP3s for input. Your view is so limited, you can't even imagine software that isn't a product or a tool for advertising, but trust me - a lot of software is written in other environments, and malicious users or malicious data is not a real concern.
Anyways, don't bother to reply. I'm sure you won't admit you're wrong, and I'm not interested in continuing this discussion.
> There tens of thousands of programmers out there (myself included) who write software to analyze data. We don't make "apps" for users, and we don't download random PDFs or MP3s for input. Your view is so limited, you can't even imagine software that isn't a product or a tool for advertising, but trust me - a lot of software is written in other environments, and malicious users or malicious data is not a real concern.
Ahh. It's funny - you think I can't think of all the software where security isn't an issue, and I think you can't think of all the software where security is an issue.
I'm aware that there is software where, rightly, security isn't a huge concern. It exists. Some software for crunching data internally counts! And some software is written where the front door is wide open anyways. But the scope for software where security isn't a concern sure as shit isn't "everything but public facing servers or web browsers", as I took you to imply. It's also not "everything but products and advertising tools". Point of sales systems, industrial control software, internal financial software, routers, telecom infrastructure... all kinds of wonderful "internal", bespoke systems, that most definitely need to care about security and protection against malicious users, or malicious employees. Industries where mandatory vacations are a security policy against fraud, where your software will be generating audit logs, where defense in depth matters against external threats even if you could trust your employees.
Which are just chock full of C programs. Sigh.
> I'm not interested in continuing this discussion.
Fair enough. Thank you for taking the time you did.
> Look, you're just another arrogant novice who runs around telling people how to be safe
Yeah, probably. Not so arrogant as to be certain I can think of every reason my software might actually need to care about security, or to try and write security critical C in good conscious, though.
> so you can convince yourself that you're safe.
I'd never succeed at that. Safer, maybe, but never safe.
> Maybe you'll grow out of that, or maybe you'll stay obnoxious forever.
For the sake of progress and a safer future, let's hope for the latter.
As a third party to this discussion, you really need to check your attitude and up the charity a few notches. I was finding the conversation interesting but for wading past all the time you spent typing while avoiding communicating.
"For example, C programmers don't trust anything written since K&R."
That is an incredibly broad generalization. Perhaps you were implicitly assuming some qualifiers? :)
---
"..that's why we have linters, static analyzers, style guides, compiler options and warnings.."
But all those things are still just glorified retrofits on the job of the language. Are there any examples where a new library in a new domain adds inabilities that weren't originally enforced (or even conceived of) in the language?
One example I can think of is reading/writing files. The original C had simple numbers for file descriptors, which meant that in a large application you might try to read from a write-only fd, or vice versa. The C++ standard library solved that by creating separate types for istreams and ostreams. No reason we couldn't apply that idea to C as well.
On a tangent, I'm fascinated by the fact that Go hasn't bothered to steal that idea. For example, a goroutine can read or write any of the channels it has access to. If you try to juggle too many at once, that might cause the odd typo bug. It'll be interesting to see if source/sink channel types ever get retrofitted to Go in a decade or so.
>C programmers don't trust anything written since K&R.
C is one of the most used languages in the world, and the number of programmers is surpassed perhaps only by a handful of languages. That alone tells me any generalisation you make about C programmers is most probably erroneous.
"COBOL is one of the most used languages in the world, and the number of lines of code is surpassed perhaps only by a handful of languages. That alone tells me any generalisation you make about COBOL programmers is most probably erroneous."
One a name and one phrase changed but it's still technically true. Clearly something wrong with your argument unless you think COBOL is also superior to other languages due to uptake.
My reading of the OP was just that there are tons and tons of C programmers, who almost certainly use C for many different reasons, and it's hard to believe any sweeping generalisation of such a large diverse group.... and therefore it's difficult to believe that none of the large and diverse number of C programmers trust anything after K&R.
> But that's why we have linters, static analyzers,
Which tend to be based on heuristics, rather than any sort of rigorous foundation. There's no shortage of static analysis tools for C and C++ that reject good code and accept bad code.
> style guides,
Which can be very onerous, limiting your ability to define useful abstractions, leaving you with just the bad parts of the base language (e.g., Google's C++ Style Guide).
> compiler options and warnings,
No amount of compiler options or warnings will force you to use an abstract type, well, abstractly, in a language that has unrestricted memory access (C and C++), unrestricted reflection (Java, .NET, anything dynamic), or both (some Common Lisp implementations).
This is not Orwellian newspeak. In a Turing Machine, everything is possible. But nobody wants to program in it because it's too hard. Same thing with assembly. Everything that the CPU can do, you can do in assembly. But everyone wants out of assembly if at all possible. Why? Because the price for the freedom was too high.
Same thing with jumping to any line in a program. Sure, there are times when it could make your life easier, but they aren't very many. And what's horrible is when someone a decade ago, who doesn't even work there anymore, used it to make their life easier, and you have to figure out their bit of "cleverness". It's worth not being able to do that in order to keep that from being done to you. (This is the way civilized society works, by the way. I give up the freedom to kill random other people in order to not live in a society where I worry a lot about random other people trying to kill me.)
Within the constraints ("I can't do X"), you find two kinds of freedom. First, "I no longer have to worry about somebody else doing X in code that I need to work on". Second, "I gain Y".
For an example of the second kind, garbage collection means that I no longer have to keep track of when I should free memory. By giving up something (the ability to allocate and free memory myself), I gained freedom. This is a net win if I don't need the thing I'm giving up.
You're not trying to write something specific. Give up the things you don't need in order to write the program you're trying to write, if doing so reduces the number of things you have to do in order to write that program. Give up something you weren't going to use anyway in order to have less that you have to do. That's a win.
My feeling when I program in ml / lisps. Limited set of abstractions and composition rules. Fuels my mind. My brain choke at the first mutable statement sequence.
ML lets you define just about any abstraction you want, and actually enforce it, both statically (using abstract types) and dynamically (using exceptions). The only thing that's limited in ML is which abstractions can be enforced using only static means.
ML is mostly sum and product types, function composition and induction (I never cared about patterns even though they're high grade convenience). The rest is up to my imagination I guess.
The module system lets you define abstract types. The module system is the first defining characteristic of ML, the zeroth being type inference. Without the module system, ML simply isn't ML.
The importance of abstract types can't be overstated: they can enforce nontrivial invariants of data structures (e.g., that self-balancing search trees are actually balanced), by just reducing the scope in which they could possibly be broken, and without bringing in the machinery of dependent types.
I don't disagree, but it doesn't contradict my point. ML module correlate functions. The basic feature that helped me are still there. I'd even say that without this I couldn't understand the value of Modules later on. Level zero functional programming enforce a strong thinking hygiene around parametricity. Modules and functors are just the next step.
I've been slowly working on writing something up on this subject that shows how the constraint "energy must be conserved" allows us the freedom to solve a large number of problems just by describing them in terms of energy. Hopefully to tease out a more clarity on how we can transform abstract symmetries into practical solutions to real problems.
I like this analogy! Are you thinking of “energy” as some measurement of the amount of freedom a specification gives implementors to make their own design decisions? (Possibly modulo some set of changes deemed “trivial”, like renaming identifiers, reordering independent statements, etc.)
Oh, my bad. I'm certainly aware of the usefulness of the conservation of energy when you need to relate the initial and final states of a physical system solely under the action of conservative forces. But I thought you were talking about using the conservation of energy as an analogy for a strategy for tackling programming problems.
Energy is a linguistic construct for solving problems in abstract models of reality. That's not too different from how programming works, it's just a different set of models. Thus energy is just a special case of a more general problem solving technique which can be employed in other domains.
"Constraints provide freedom" is a nice saying but I think it's commonly applied to forms like poetry or sculpture. Unlike the discipline of creating a sonnet, virtually all modern programming languages provide few constraints on the form as a whole compared to the requirements of poems or realistic sculpture (whatever local hurdles they might involve) - remember the old saying: "A Real Programmer can write FORTRAN programs in any language".
I think programming languages do provide something unique and distinct from libraries.
Essentially a language is a generative, infinitely extensible tool. Another way to put it is that a language provides a geometry to move through the "space" of all possible programs. And different language make different potential programs closer.
> "Constraints provide freedom" is a nice saying but I think it's commonly applied to forms like poetry or sculpture.
Just like natural language can be used to write anything from poetry to shopping lists or even hate mail, programming languages can be used to write anything from beautiful code (say, a concisely expressed implementation of a nontrivial algorithm, based on a deep insight on the programmer's part) to mundane automation scripts or even spam bots and viruses.
> Unlike the discipline of creating a sonnet, virtually all modern programming languages provide few constraints on the form as a whole compared to the requirements of poems or realistic sculpture (whatever local hurdles they might involve)
I've found something very analogous to the discipline of writing a sonnet: writing programs where no single code path can fail. Where every single instruction or expression can be legitimately reached, and thus there is no need to throw exceptions or return error codes. For me, this is a form of art: the art of not branching more than it's necessary to. It requires a sense of aesthetics and an expressive technique far superior to those of most programmers, where the latter can be considered analogous to the native speakers of a natural language in which poetry is written.
> remember the old saying: "A Real Programmer can write FORTRAN programs in any language".
I'd love to see a Real Programmer write FORTRAN programs in Prolog, before yelling at the nearest person in frustration.
> Essentially a language is a generative, infinitely extensible tool.
Obviously, every general-purpose language lets you extend the universe of programs that exist. But relatively few languages let you extend the language itself, and even fewer do so in a disciplined way:
(0) Famously, Lisp and its derivatives let you define custom language constructs by essentially compiling them into Lisp using macros. Among the derivatives, particularly notable is Racket, which offers sophisticated ways to pattern match on syntax (objects).
(1) ML doesn't let you define new language constructs, but it lets you define abstract data types (not to be confused with abstract classes) whose internal representation isn't visible to its users, thus behaving as if they were primitive types.
> Constraints provide freedom. (...) This is not Orwellian newspeak.
This is just contravariance of function arguments. For every restriction on what the caller may supply, the callee earns the freedom to assume something about what it is supplied (namely, that the restriction holds). This is mathematically a triviality, of course. From a higher level point of view, the terminology might change, but the same essential fact remains: when a module/class/component/whatever author claims a freedom for themselves, they're denying freedoms to their users. This is inescapable.
The real problem is, if freedom is never absolute, but rather a matter of give and take between different software components, how do we find the right “distribution of freedoms” across software components? This is the essence of modular software design, and it's highly nontrivial.
i agree with your comment. limitation is what gives rise to creativity. however, consider that one can impose their own limitations. i can restrict myself to a subset of assembly, or c.
i would guess that even with today's popular verbose languages with hundreds of libraries, most programmers only know or use a subset of the language and libraries. strangely when i try to use these languages i feel too constrained. only because i feel i am trying to fit my own thinking into some other programmer's thinking.
sometimes this seems worth it. other times (most times) not.
whereas with assembly, i only have to fit my thinking to what the computer can do. this seems like what i should be doing if i am trying to program a computer.
"The more constraints one imposes, the more one frees one's self. And the arbitrariness of the constraint serves only to obtain precision of execution."
- Igor Stravinsky
I see having pointers to support dynamic memory allocation as something different from the ability to refer to memory addresses. For example, in C, one can [1] write a program that attempts to write data to any memory address. With Java, that is not possible. With pascal, Wirth's intention was similar, but IIRC variant records provided an undesired escape hatch.
[1] probably not without invoking implementation-dependent behavior.
One inability I am thinking about lately is disallowing primitive types on public members. Take C#, but properties and methods would only allow returning and taking class/struct types.
This way you enforce strict use of objects, enforcing expressivity. No more `double meters` but `Meter meters`.
A separate class for each unit of measurement isn't the right way to perform compile-time dimensional analysis. It doesn't easily support forming compound units from more basic ones.
What you call “obsession”, I call “preventing preventable mistakes”, and it's actually a good thing. There are perfectly viable ways to perform compile-time dimensional analysis, for example, parameterizing scalar types by the exponents of “primitive” dimensions.
That sounds even worse. Why should primitive types be treated as second-class entities that can only arise as private implementation details of a class?
In any case, my original point still stands:
(0) Dimension errors are really what one seeks to prevent by discouraging things like `double meters;`.
(1) Making a separate class for each unit of measurement doesn't compose or scale well. Dimensional analysis is a fundamentally structural business, and this really calls for compile-time arithmetic with the exponents of primitive dimensions. But, of course, if your hammer is Java-shaped...
> Why should primitive types be treated as second-class entities that can only arise as private implementation details of a class?
Because primitives are meaningless. String has no meaning. "Message" does. "Contact person" does and has conceptually nothing in common with a "Message". Why should they be both of type String?
The real problem with primitive obsession is that it stifles software evolution. Suddenly Message needs to keep other data or have some methods associated with it. Every place where it was used is suddenly invalid because you need to go from String to class and inheriting from primitive is just silly if at all possible.
That's what you want to prevent. It has almost nothing to do with units or dimensional analysis.
That depends on your problem domain. If I'm implementing a library of data structures and algorithms (which I have to do, because most standard libraries are subpar on this front), then I definitely want to work with raw integers or sequences (but probably not strings - they should be just a special case of sequences).
> "Message" does. "Contact person" does and has conceptually nothing in common with a "Message".
No disagreement in this particular case. It's indeed best to have separate types for things on which different operations make sense. But, personally, I find it more tasteful to have little to no algorithmic logic at the level of business domain entities. Algorithms work on mathematically structured data.
But math is a problem domain as well. I rather have specialized types to work with there as well, for above mentioned reasons (intention, documentation).
67 comments
[ 0.31 ms ] story [ 72.0 ms ] threadI think the big problem (and sometimes benefit) of old languages is their culture. For example, C programmers don't trust anything written since K&R. This is both good and bad. Creating a new language doesn't force existing communities to change; it means creating a new community and either waiting for the old ones to die off, or (ideally?) driving them into obsolescence.
I remember when Wikipedia was new and there were lots of studies finding errors in their articles. Instead of replacing Wikipedia, those techniques for finding errors were simply incorporated into the editing process (as much as possible). I wish that as new classes of bugs came onto our radar, we could go back and refine old code instead of throwing it out with the bathwater. (Even OpenSSL wasn't beyond saving, judging by BoringSSL and LibreSSL. These projects still need more work, of course.)
So really, your argument only applies to public facing servers or web browsers. Unless you're writing one of those, I think your argument is weak.
Or, you know, literally anything touching a file format that might be downloaded. PDFs, word documents, images, zip files, movies, music, sounds, icons that show up in your file explorer, user profiles, any sort of web client (email, games, chat, wget, package managers, ...) lots of poison apples out there.
This kind of nonsense is why the best advice to handling USB drives you find on the ground is to toss them in a fire barrel - everyone thinks they're "behind a firewall" and that they're totally protected! They have all the protection of a wet tissue in a hurricane.
Oh, but I'm just writing a terminal! I don't touch file formats! It's safe to auto-execute commands in response to pasted newlines! ...and then you've helped enable the spread of malware through clipboard hijacking Javascript. Oops.
Firewalls are not a panacea. Firewalls are a roadbump.
> Unless you're writing one of those, I think your argument is weak.
I think your security is borderline nonexistent. I think I cannot trust you with C. I think someone is probably in your systems if you're in charge of the security side of IT, of anywhere of significance, unless you delegated enough responsibility to make you a figurehead.
I mean no offense by this. A lot of reasonably talented and smart programmers make the same mistake as you, and go write a bunch of exploitable vulnerabilities in C as a result. It's not like you're one of those madmen disabling ASLR, NX, stack cookies, or some of the many other mitigation techniques being used to try and clean up after this tragedy of our times. ...right?
You really don't get it, but your arrogance is overwhelming. I don't care who you trust.
> I mean no offense by this.
Yes you do.
Then point out what I'm missing.
However, giving you the benefit of the doubt - There tens of thousands of programmers out there (myself included) who write software to analyze data. We don't make "apps" for users, and we don't download random PDFs or MP3s for input. Your view is so limited, you can't even imagine software that isn't a product or a tool for advertising, but trust me - a lot of software is written in other environments, and malicious users or malicious data is not a real concern.
Anyways, don't bother to reply. I'm sure you won't admit you're wrong, and I'm not interested in continuing this discussion.
I appreciate that.
> There tens of thousands of programmers out there (myself included) who write software to analyze data. We don't make "apps" for users, and we don't download random PDFs or MP3s for input. Your view is so limited, you can't even imagine software that isn't a product or a tool for advertising, but trust me - a lot of software is written in other environments, and malicious users or malicious data is not a real concern.
Ahh. It's funny - you think I can't think of all the software where security isn't an issue, and I think you can't think of all the software where security is an issue.
I'm aware that there is software where, rightly, security isn't a huge concern. It exists. Some software for crunching data internally counts! And some software is written where the front door is wide open anyways. But the scope for software where security isn't a concern sure as shit isn't "everything but public facing servers or web browsers", as I took you to imply. It's also not "everything but products and advertising tools". Point of sales systems, industrial control software, internal financial software, routers, telecom infrastructure... all kinds of wonderful "internal", bespoke systems, that most definitely need to care about security and protection against malicious users, or malicious employees. Industries where mandatory vacations are a security policy against fraud, where your software will be generating audit logs, where defense in depth matters against external threats even if you could trust your employees.
Which are just chock full of C programs. Sigh.
> I'm not interested in continuing this discussion.
Fair enough. Thank you for taking the time you did.
> Look, you're just another arrogant novice who runs around telling people how to be safe
Yeah, probably. Not so arrogant as to be certain I can think of every reason my software might actually need to care about security, or to try and write security critical C in good conscious, though.
> so you can convince yourself that you're safe.
I'd never succeed at that. Safer, maybe, but never safe.
> Maybe you'll grow out of that, or maybe you'll stay obnoxious forever.
For the sake of progress and a safer future, let's hope for the latter.
That is an incredibly broad generalization. Perhaps you were implicitly assuming some qualifiers? :)
---
"..that's why we have linters, static analyzers, style guides, compiler options and warnings.."
But all those things are still just glorified retrofits on the job of the language. Are there any examples where a new library in a new domain adds inabilities that weren't originally enforced (or even conceived of) in the language?
One example I can think of is reading/writing files. The original C had simple numbers for file descriptors, which meant that in a large application you might try to read from a write-only fd, or vice versa. The C++ standard library solved that by creating separate types for istreams and ostreams. No reason we couldn't apply that idea to C as well.
On a tangent, I'm fascinated by the fact that Go hasn't bothered to steal that idea. For example, a goroutine can read or write any of the channels it has access to. If you try to juggle too many at once, that might cause the odd typo bug. It'll be interesting to see if source/sink channel types ever get retrofitted to Go in a decade or so.
Channels can be designated as receive-only or send-only in variable and function declarations:
C is one of the most used languages in the world, and the number of programmers is surpassed perhaps only by a handful of languages. That alone tells me any generalisation you make about C programmers is most probably erroneous.
One a name and one phrase changed but it's still technically true. Clearly something wrong with your argument unless you think COBOL is also superior to other languages due to uptake.
http://www.nhplace.com/kent/PS/Lambda.html
Which tend to be based on heuristics, rather than any sort of rigorous foundation. There's no shortage of static analysis tools for C and C++ that reject good code and accept bad code.
> style guides,
Which can be very onerous, limiting your ability to define useful abstractions, leaving you with just the bad parts of the base language (e.g., Google's C++ Style Guide).
> compiler options and warnings,
No amount of compiler options or warnings will force you to use an abstract type, well, abstractly, in a language that has unrestricted memory access (C and C++), unrestricted reflection (Java, .NET, anything dynamic), or both (some Common Lisp implementations).
> and ultimately code reviews and good taste.
If only the latter weren't in short supply...
This is not Orwellian newspeak. In a Turing Machine, everything is possible. But nobody wants to program in it because it's too hard. Same thing with assembly. Everything that the CPU can do, you can do in assembly. But everyone wants out of assembly if at all possible. Why? Because the price for the freedom was too high.
Same thing with jumping to any line in a program. Sure, there are times when it could make your life easier, but they aren't very many. And what's horrible is when someone a decade ago, who doesn't even work there anymore, used it to make their life easier, and you have to figure out their bit of "cleverness". It's worth not being able to do that in order to keep that from being done to you. (This is the way civilized society works, by the way. I give up the freedom to kill random other people in order to not live in a society where I worry a lot about random other people trying to kill me.)
Within the constraints ("I can't do X"), you find two kinds of freedom. First, "I no longer have to worry about somebody else doing X in code that I need to work on". Second, "I gain Y".
For an example of the second kind, garbage collection means that I no longer have to keep track of when I should free memory. By giving up something (the ability to allocate and free memory myself), I gained freedom. This is a net win if I don't need the thing I'm giving up.
You're not trying to write something specific. Give up the things you don't need in order to write the program you're trying to write, if doing so reduces the number of things you have to do in order to write that program. Give up something you weren't going to use anyway in order to have less that you have to do. That's a win.
My feeling when I program in ml / lisps. Limited set of abstractions and composition rules. Fuels my mind. My brain choke at the first mutable statement sequence.
The importance of abstract types can't be overstated: they can enforce nontrivial invariants of data structures (e.g., that self-balancing search trees are actually balanced), by just reducing the scope in which they could possibly be broken, and without bringing in the machinery of dependent types.
For a great video on this, see Runar Bjarnason's talk "Constraints Liberate, Liberties Constrain" (https://www.youtube.com/watch?v=GqmsQeSzMdw).
I like this analogy! Are you thinking of “energy” as some measurement of the amount of freedom a specification gives implementors to make their own design decisions? (Possibly modulo some set of changes deemed “trivial”, like renaming identifiers, reordering independent statements, etc.)
I think programming languages do provide something unique and distinct from libraries.
Essentially a language is a generative, infinitely extensible tool. Another way to put it is that a language provides a geometry to move through the "space" of all possible programs. And different language make different potential programs closer.
Just like natural language can be used to write anything from poetry to shopping lists or even hate mail, programming languages can be used to write anything from beautiful code (say, a concisely expressed implementation of a nontrivial algorithm, based on a deep insight on the programmer's part) to mundane automation scripts or even spam bots and viruses.
> Unlike the discipline of creating a sonnet, virtually all modern programming languages provide few constraints on the form as a whole compared to the requirements of poems or realistic sculpture (whatever local hurdles they might involve)
I've found something very analogous to the discipline of writing a sonnet: writing programs where no single code path can fail. Where every single instruction or expression can be legitimately reached, and thus there is no need to throw exceptions or return error codes. For me, this is a form of art: the art of not branching more than it's necessary to. It requires a sense of aesthetics and an expressive technique far superior to those of most programmers, where the latter can be considered analogous to the native speakers of a natural language in which poetry is written.
> remember the old saying: "A Real Programmer can write FORTRAN programs in any language".
I'd love to see a Real Programmer write FORTRAN programs in Prolog, before yelling at the nearest person in frustration.
> Essentially a language is a generative, infinitely extensible tool.
Obviously, every general-purpose language lets you extend the universe of programs that exist. But relatively few languages let you extend the language itself, and even fewer do so in a disciplined way:
(0) Famously, Lisp and its derivatives let you define custom language constructs by essentially compiling them into Lisp using macros. Among the derivatives, particularly notable is Racket, which offers sophisticated ways to pattern match on syntax (objects).
(1) ML doesn't let you define new language constructs, but it lets you define abstract data types (not to be confused with abstract classes) whose internal representation isn't visible to its users, thus behaving as if they were primitive types.
This is just contravariance of function arguments. For every restriction on what the caller may supply, the callee earns the freedom to assume something about what it is supplied (namely, that the restriction holds). This is mathematically a triviality, of course. From a higher level point of view, the terminology might change, but the same essential fact remains: when a module/class/component/whatever author claims a freedom for themselves, they're denying freedoms to their users. This is inescapable.
The real problem is, if freedom is never absolute, but rather a matter of give and take between different software components, how do we find the right “distribution of freedoms” across software components? This is the essence of modular software design, and it's highly nontrivial.
i would guess that even with today's popular verbose languages with hundreds of libraries, most programmers only know or use a subset of the language and libraries. strangely when i try to use these languages i feel too constrained. only because i feel i am trying to fit my own thinking into some other programmer's thinking.
sometimes this seems worth it. other times (most times) not.
whereas with assembly, i only have to fit my thinking to what the computer can do. this seems like what i should be doing if i am trying to program a computer.
"The more constraints one imposes, the more one frees one's self. And the arbitrariness of the constraint serves only to obtain precision of execution." - Igor Stravinsky
Someone should learn about Algol family of programming languages.
But you are right, the original statement is absurd. Ada and (at least Turbo-) Pascal have pointers. And so does Java.
And I am not listing all of them.
[1] probably not without invoking implementation-dependent behavior.
This way you enforce strict use of objects, enforcing expressivity. No more `double meters` but `Meter meters`.
It's completely orthogonal to you argument.
In any case, my original point still stands:
(0) Dimension errors are really what one seeks to prevent by discouraging things like `double meters;`.
(1) Making a separate class for each unit of measurement doesn't compose or scale well. Dimensional analysis is a fundamentally structural business, and this really calls for compile-time arithmetic with the exponents of primitive dimensions. But, of course, if your hammer is Java-shaped...
Because primitives are meaningless. String has no meaning. "Message" does. "Contact person" does and has conceptually nothing in common with a "Message". Why should they be both of type String?
The real problem with primitive obsession is that it stifles software evolution. Suddenly Message needs to keep other data or have some methods associated with it. Every place where it was used is suddenly invalid because you need to go from String to class and inheriting from primitive is just silly if at all possible.
That's what you want to prevent. It has almost nothing to do with units or dimensional analysis.
That depends on your problem domain. If I'm implementing a library of data structures and algorithms (which I have to do, because most standard libraries are subpar on this front), then I definitely want to work with raw integers or sequences (but probably not strings - they should be just a special case of sequences).
> "Message" does. "Contact person" does and has conceptually nothing in common with a "Message".
No disagreement in this particular case. It's indeed best to have separate types for things on which different operations make sense. But, personally, I find it more tasteful to have little to no algorithmic logic at the level of business domain entities. Algorithms work on mathematically structured data.