C, the most widespread general-purpose programming language, does things that are extremely difficult or impossible in highly abstract languages like Haskell or LISP, which doesn’t seem to match the notion of all three being a helpful way to think about the world.
Most of what we wind up doing with programming languages is building software tools. We prefer programs to be written such that the thinking is clear and correct, but this seems to me motivated more by convenience than anything else, and it rarely turns out that way besides.
I would go as far as to say that the case of ‘our imperfect brains dealing with a complex world’ is in fact a series of specific sub-problems, and we build tools for solving them on that basis.
On the other hand, it feels like there is a large influence on programming languages that isn’t well captured by the tool-for-problem or crutch-for-psychology dichotomy: working with other people. Consider the object-oriented languages, like Java. For all that an object is a convenient way to represent the world, and for all that it is meant to provide abstractions like inheritance, what actually seems to have driven the popularity of object orientation is that it provides a way for the next programmer not to know exactly what is happening in the code, but instead to take the current crop of objects as given and then do whatever additional thing they need done.
Should we consider a group of people separated in time working on the same problem, an independent problem? Or should we consider that working with people-in-the-future is something we are psychologically bad at, and we need a better way to organize our thinking about it? While the former seems more reasonable to me, I don’t actually know the answer here. One way to tell might be if the people who wrote Java said specifically somewhere that they wanted a language that would make it easier for multiple people to write large programs together over time. Another way might be if everyone who learned Java chose it because they liked not having to worry that much about what the last guy did, so long as the objects work.
On the other-other hand, an example was staring me in the face that points more closely to your old intuitions: I just started reading The Structure and Interpretation of Classical Mechanics, which is the textbook used for classical mechanics at MIT. Of particular note is that the book uses Scheme, a LISP dialect, in order to enforce clarity and correctness of understanding of mechanics. The programming language is only covered in the appendix; they spend an hour or two on it in the course.
The goal here is to raise the standard of understanding the world to ‘can you explain it to the computer.’
C, the most widespread general-purpose programming language, does things that are extremely difficult or impossible in highly abstract languages like Haskell or LISP
Can you give an example? I’m surprised by this claim, but I only have deep familiarity with C of these three. (My primary functional language includes mutable constructs; I don’t know how purely functional languages fare without them.)
The usual example here is memory control. The point of the higher-level languages is to abstract away the details of memory and registers, so there is no malloc/free equivalent when writing in them; for this purpose they use garbage collection.
Of course, eventually people found a need for addressing these kinds of problems, and so features to allow for it were added later. C reigns supreme in embedded applications because of the precise memory and I/O capabilities, but there is stuff for embedded Haskell and embedded LISP now. But note that in these sources they are talking about stuff like special compilers and strategies for keeping the automatic garbage collection from blowing everything up, whereas with C, you just mostly write regular C. Also interrupts.
My intuition is strongly opposite yours of ten years ago.
For example, there are Domain Specific Languages, which are designed exactly for one problem domain.
C, the most widespread general-purpose programming language, does things that are extremely difficult or impossible in highly abstract languages like Haskell or LISP, which doesn’t seem to match the notion of all three being a helpful way to think about the world.
Most of what we wind up doing with programming languages is building software tools. We prefer programs to be written such that the thinking is clear and correct, but this seems to me motivated more by convenience than anything else, and it rarely turns out that way besides.
I would go as far as to say that the case of ‘our imperfect brains dealing with a complex world’ is in fact a series of specific sub-problems, and we build tools for solving them on that basis.
On the other hand, it feels like there is a large influence on programming languages that isn’t well captured by the tool-for-problem or crutch-for-psychology dichotomy: working with other people. Consider the object-oriented languages, like Java. For all that an object is a convenient way to represent the world, and for all that it is meant to provide abstractions like inheritance, what actually seems to have driven the popularity of object orientation is that it provides a way for the next programmer not to know exactly what is happening in the code, but instead to take the current crop of objects as given and then do whatever additional thing they need done.
Should we consider a group of people separated in time working on the same problem, an independent problem? Or should we consider that working with people-in-the-future is something we are psychologically bad at, and we need a better way to organize our thinking about it? While the former seems more reasonable to me, I don’t actually know the answer here. One way to tell might be if the people who wrote Java said specifically somewhere that they wanted a language that would make it easier for multiple people to write large programs together over time. Another way might be if everyone who learned Java chose it because they liked not having to worry that much about what the last guy did, so long as the objects work.
On the other-other hand, an example was staring me in the face that points more closely to your old intuitions: I just started reading The Structure and Interpretation of Classical Mechanics, which is the textbook used for classical mechanics at MIT. Of particular note is that the book uses Scheme, a LISP dialect, in order to enforce clarity and correctness of understanding of mechanics. The programming language is only covered in the appendix; they spend an hour or two on it in the course.
The goal here is to raise the standard of understanding the world to ‘can you explain it to the computer.’
Can you give an example? I’m surprised by this claim, but I only have deep familiarity with C of these three. (My primary functional language includes mutable constructs; I don’t know how purely functional languages fare without them.)
The usual example here is memory control. The point of the higher-level languages is to abstract away the details of memory and registers, so there is no malloc/free equivalent when writing in them; for this purpose they use garbage collection.
Of course, eventually people found a need for addressing these kinds of problems, and so features to allow for it were added later. C reigns supreme in embedded applications because of the precise memory and I/O capabilities, but there is stuff for embedded Haskell and embedded LISP now. But note that in these sources they are talking about stuff like special compilers and strategies for keeping the automatic garbage collection from blowing everything up, whereas with C, you just mostly write regular C. Also interrupts.