I remember people making similar arguments when cars started to have power steering.
> Programming languages are hand-tools as well, in an essential sense. Like mathematics, the "interface" through which we manipulate a codebase is plain, raw text — sequences of symbols. We humans understand source code by reading it with our eyes; and then we write or modify it by editing it the same as any other text. We can do the whole thing on paper, or a blackboard, or even purely in our mind's eye.
I disagree; programming languages are primarily tools for programming on computers, and expecting them to be skeuomorphic to a particular approach may hold us back, just like expecting a CAD program to work the same as a physical drafting table or expecting a rendering engine to use a TV-like fixed framerate.
Being able to push secondary parts of a program to be foldable, or visible on mouseover but not by default, makes it easier to communicate understanding to other humans. And actually that's a more faithful recreation on how you'd communicate in writing - on a blackboard you might use small text, or footnotes, or a verbal explanation. A mathematical paper is almost never "plain text" in the sense of being a linear sequence of ascii characters and nothing more. A language whose design embraces IDEs can be a better tool than one that flattens everything into the binary of written in the code or completely invisible.
Whatever a programming language is, it has to be something you can hold onto in your head, because the human mind is where "programming" takes place. And there really aren't many forms that such a thing can take.
The step-sibling of programming languages, the mathematical proof, can really take any form we like; we're not limited to maths that can run on a computer, as long as we can "run" them in our minds. But we've still ended up with only two basic formalisms that almost(?) all mathematics is expressed in terms of: geometry (visual mathematics), and algebra (symbolic mathematics, including formal logic.) Because those are the formalisms that we have mental hardware to comprehend.
Anything that isn't in one of those shapes (say, an exhaustive computer-assisted proof-by-parts) might still be productive to prove; but we humans won't like it, and will always seek to replace it with something that we can get a mental "handle" on.
We'd certainly never take the formalisms that do work for us, throw them out, and replace them with non-human-mind-comprehensible formalisms instead. Why would we bother? What would we gain?
> Being able to push secondary parts of a program to be foldable, or visible on mouseover but not by default, makes it easier to communicate understanding to other humans. And actually that's a more faithful recreation on how you'd communicate in writing - on a blackboard you might use small text, or footnotes, or a verbal explanation.
Oh, I don't disagree. But all those things are useful for plain text, too. As you say, humans already think and manipulate text that way. And "advanced" text editors already provide facilities to manipulate text that way, without any understanding of programming-language syntax.
You can get very fancy about how you manipulate symbols... as long as "just manipulating symbols" is truly all you're doing. As pure symbol manipulation is something you can always also do in your head. Go much beyond that, though, and you stray from the land of "things humans can keep track of in their brains."
Can you think in terms of what a JetBrains codebase will look like after you refactor it, without actually doing the refactoring and seeing the result? How about with two or three such applications in play? It gets pretty hard, no? Because those aren't just pure symbol-manip. They're not just moving letters around on the mental blackboard that represents the current module. The non-local effects aren't strictly intuitive. Each layer of that that you have to hold solely in your head, bogs you thinking down. In a high-friction language, you just don't bother to try to work too far ahead with such changes; you just "take things one step at a time", applying each change and then re-learning the codebase in light of it; sometimes applying a change and then backing out once you realize you're got your sequencing wrong.
Sometimes working "with blinders on" like this is necessary (e.g. fixing up a big ugly three-way git merge); but why subject yourself to it if you don't have to? Wouldn't you rather be able to understand what you're doing, and where you're going?
> A mathematical paper is almost never "plain text" in the sense of being a linear sequence of ascii characters and nothing more.
I mean, they're usually written in LaTeX ;) But to be clear and precise, I was trying to invoke the concept of a https://en.wikipedia.org/wiki/Formal_language. Programming languages and (symbolic) mathematics are both formal languages. You manipulate them algebraically, by moving the symbols around, applying transformations that turn one valid statement into another valid statement.
Yes, there are isomorphisms, e.g. visual "programming languages" like Scratch. They're certainly neat, but as far as I've been able to ascertain, people can't really think in them the way they can think in symbols.
Ask a composer: what do you think about when you compose music? Do you think of the sound; or do you picture the score? Where music's concerned, I would guess pretty much everyone is thinking about the sound, because that's a lot more intuitive. Someone could probably write a song entirely by manipulating a score in their mind—and make it sound good!—but it'd be hell to do, in comparison.
I don't think the IDE should do "everything" for you, and you should understand how its building/compiling the software. But with statically typed languages and library definition hints. I think I would be wasting time searching docs if I didn't have the hints and type info. I can't remember everything I work with, it's impossible.
Just two among many reasons to use IDEs.
I don't know what kind of refactoring you are doing but for me yes I absolutely know what it will look like after the refactoring. In fact, that is usually why I'm doing it: refactoring to remove complication.
For example, the typical "extract method" refactoring lets me grab a piece of code that is almost duplicated elsewhere and turn it into a method with conditional logic to handle both conditions. That's a great simplification and make it much easier to hold in my head.
>Can you think in terms of what a JetBrains codebase will look like after you refactor it, without actually doing the refactoring and seeing the result? How about with two or three such applications in play?
Not sure what you mean here, IDE built-in refactorings are tools that I use because I know exactly what I want to do and what the codebase should look like after I do it, but the work to get there is tedious and repetitive. I have never had trouble imagining how they work or what the result will be. Compilers do optimizations that alter the structure of programs reliably; an IDE with a language model as rich as the IntelliJ suite's is little different from a compiler specialized to a few high-level optimizations.
>Programming languages and (symbolic) mathematics are both formal languages. You manipulate them algebraically, by moving the symbols around, applying transformations that turn one valid statement into another valid statement.
We seem to think about programming languages very similarly. Always nice to find a fellow traveler ;)
I'm not really a fan of Java though I will confess to liking C# a lot. My heart is with OCaml and Idris though. So why do I program in C#? Why do I choose such a "high-friction" language, as you put it? C# is most definitely not designed from the ground up for maximum developer ergonomics.
It's because of the ecosystem, and specifically tooling like the JetBrains IDEs. I'm more productive in a language like C# because of the depth of high-quality libraries available to me and the quality of the tooling I use. Yes, some of the worth of an IDE is in papering over ergonomic problems in languages. However, IDEs are certainly not required to write code in these verbose languages. I use them because they are powerful tools that are well adapted to the conceptual model of the languages they support, and they enhance my ability to use patterns and write code "fluidly" as you put it.
Even in more ergonomic and expressive languages this has value, probably even more than it has in C# or Java. These languages are easier to write without an IDE, but we don't know what we're missing because the high-quality tooling just doesn't exist. Idris' Atom support is a pointer in the direction I'm thinking of: https://atom.io/packages/language-idris
Just think about what an IntelliJ-level IDE could do for a language with a type system as rich as Idris'. You might be able to infer entire applications based off a handful of expressive type signatures. We have yet to see what writing in one of these languages is like with a suitably powerful design environment, and I fully believe that it will make writing them with a text editor look pedestrian just like IntelliJ did to Java.
You're welcome to use it as cleaner Java and it will work well for this.
You're equally welcome to make use of more advanced features.
You're also welcome to go nuts and use all the features but it's not recommended.
> We'd certainly never take the formalisms that do work for us, throw them out, and replace them with non-human-mind-comprehensible formalisms instead. Why would we bother? What would we gain?
The dominance of those formalisms is at least as much about the physical hardware as the mental hardware. In my head, a mathematical proof is usually something like a tree structure or even a wiki: I have an overview that A implies B and B and C together imply D, and then if I "zoom in" then there's all sorts of structure to the steps to get from A to B. Of course if I were to write this proof on paper or blackboard then I'd have to flatten it (though not completely; I'd certainly break it up into lemma x and sub-lemma y), but saying that a paper is the human-mind-comprehensible formalism is putting the cart before the horse. The kind of richer structures that we can use with computers - Jupyter-style notebooks, or wiki-like linked crossreferences - makes it easier for humans to comprehend, not harder.
> Can you think in terms of what a JetBrains codebase will look like after you refactor it, without actually doing the refactoring and seeing the result? How about with two or three such applications in play? It gets pretty hard, no? Because those aren't just pure symbol-manip. They're not just moving letters around on the mental blackboard that represents the current module. The non-local effects aren't strictly intuitive. Each layer of that that you have to hold solely in your head, bogs you thinking down. In a high-friction language, you just don't bother to try to work too far ahead with such changes; you just "take things one step at a time", applying each change and then re-learning the codebase in light of it; sometimes applying a change and then backing out once you realize you're got your sequencing wrong.
Completely disagree. The result of the refactoring is always the obvious thing that you'd expect, and having the IDE take care of the details of which characters change in which files makes it much easier to focus on the actual change you're making rather than get bogged down in the details. I can't imagine understanding programming as manipulating a flat sequence of symbols; it would be like trying to work in Brainfuck.
> Ask a composer: what do you think about when you compose music? Do you think of the sound; or do you picture the score? Where music's concerned, I would guess pretty much everyone is thinking about the sound, because that's a lot more intuitive. Someone could probably write a song entirely by manipulating a score in their mind—and make it sound good!—but it'd be hell to do, in comparison.
Again I think you're getting this completely backwards. I have the structure of the program in my head, and it's something much richer than a sequence of symbols; it's a graph of directed connections, and much more besides. An IDE that can do things like showing references on hover gives me a much closer representation of the program than a linear sequence of symbols, brings me much closer to being able to manipulate the program itself; manipulating the sequence of symbols sounds exactly like manipulating the score instead of manipulating the sound.
Also, good luck doing major refactoring with a text editor. In 2020 we can do better than find and replace.
As I said, just try to think in Java (rather than Java-esque pseudocode.) Try to write a Java program in advance, in your head, that only needs typing-in (in a plain text editor!), such that it's valid the first time you type it in. Imagine how carefully and meticulously you'd have to think, to get that code right. That required meticulousness is another measure of Java's inherent friction.
> good luck doing major refactoring with a text editor
A language can also be high-friction or low-friction when it comes to how hard it is to refactor code in that language. Though this comes from more abstract architectural constraints the language imposes, rather than things like syntax or identifier naming conventions.
For example: Erlang code is low-friction for refactoring, because code that lives in a separate process needs to be spoken to through a messaging ABI; which necessitates that every such process have an API-client module to encapsulate that ABI; where that client API then offers a de-facto interface/contract for clients of that API to hold to; which frees anything underneath that API from stability requirements, as long as the API itself remains stable. The fact that this is done "in the small", per process, rather than in the large, per package/library, means that Erlang "refactoring" almost always just consists of changing the shape of a process state-function, and then adding some polyfill logic to its client API module-function to keep the process working.
That's not necessarily a bad thing. For one, writing real world code in your head would require a level of trivia memorization (e.g. the standard library function signatures) that most people have deemed a waste of time. Secondly, meticulousness is usually a positive trait in a programmer. If Java's going to be negatively evaluated for that, then it needs to be shown that the kinds of meticulousness it requires don't actually add value in cases where it's used (which may not be like your cases).
Elixir is slightly higher-friction; all that weird extra Erlang syntax that Elixir eschews (the comma and semicolon statement-binding operators specifically) has a purpose in making Erlang “mentally lintable”, so Elixir loses some of that.
It gains in other places, though; for example, Elixir’s hygenic macros impose much less of a mental load than Erlang’s parse-transforms do. (Just the fact that you have to explicitly require the definition-site module before using any macro from that module, is a big boon to predicting what’ll happen when you go to run the code.)
Spin a blank "hello world" project in Android Studio and you get no less than 79 files, and it won't work the next time you update Android Studio because of some Gradle errors.
I don't know what went wrong, but "hello world" should not be 79 files, it should be something that I can hand-code in something less than an IDE.
That’s Android Studio’s default workflow designed around the expectation that it's going to be used for building Android apps in a particular style, and to optimize for what is usually appropriate for that, which is very much not “Hello, world!” optimized.
As much as Java is a needlessly verbose language, the problem you are looking at is an particular IDE / build system / project type combination issue, not a Java issue.
class MyApp extends AndroidApp {
void main() {
Document.write("hello world");
Document.write("<button id=\"foo\">blah</button>");
}
}
javac it, adb sideload the .class file (or .jar file), and you should be done for the day. Unfortunately the bare minimum is way way way more complicated than this. Weird XML files, infestations of manifests (generate them automatically from 'ls' and 'grep' damnit), Gradle files (worst abomination of a build system ever), API levels (how about you just infer it from the functions I use? I shouldn't need to tell you), signing (ok fine but this should only be needed when uploading to an app store), zipaligning (wtf, this should be automatic, don't make me do that nonsense).Ant? Maven?
Yeah I know it doesn't work like this now, but the designers of the entire Java/Android ecosystem should have designed it to be that simple.
The moment you get any of these build systems involved you instantly lose all hopes of DRY coding. Excerpts from an Ant build.xml file:
<target name="jar" depends="compile">
<mkdir dir="jar" />
<jar destfile="jar/HelloWorld.jar" basedir="classes">
<manifest>
<attribute name="Main-Class"
value="antExample.HelloWorld" />
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java jar="jar/HelloWorld.jar" fork="true" />
</target>
I see "HelloWorld" repeated 3 times. MASSIVE fail. I'd rather no build system than deal with this level of repetitiveness. It's bad enough that Java requires the file name == class name which is already a case of repetition (that design decision is fine with me but then files should start with "class {" and not "class Foo {")Scripting languages are programming languages, and a class with a method is only typical of a narrow class of languages (most of which that aren't Java are specifically designed to closely follow Java); leaving aside those programming languages typically called “scripting” languages, in C++ it is just a function, just like C; D is pretty similar (as are most procedural languages, including those like C++ that also support class-based OOP without being obsessive about it, and also most impure functional languages.)
In Haskell it's just: main = putStrLn "Hello, World!"
https://developer.gnome.org/gtk3/stable/gtk-getting-started....
Any higher level language and ecosystem than C should be less verbose and less involved than this. Otherwise, what's the point of being higher level?
This is a problem with the Android platform itself.
>Imagine if there was a hammer that was so badly-balanced that—despite being not too hard to lift—you needed to be wearing a powered exoskeleton to accurately hold and swing it.
You don't use IDE to be able to use language, you use IDE to increase your productivity. Instead typing whole for loop you just use shortcuts, instead of changing class name in 5 places you just change it in once. You save time.
>We can do the whole thing on paper, or a blackboard, or even purely in our mind's eye.
But do you remember that class `PurchaseHandler` has method `BegingPurchaseProcess`?
Ah, like a jackhammer? One of those huge ones attached to a digger they use to break open the roads?
It’s a right-tool-for-the-job kind of thing. Some roads can be mended with a shovel. Some need a jackhammer.
If you limit your development to “what a human being can hold in their head” you are artificially limiting yourself. The whole reason we use higher level languages is so that the machine can exceed what a human can hold in their head.
(But I should note that a jackhammer also doesn’t required a powered exoskeleton to wield. The power is instead “built into” the jackhammer itself. Less like an IDE; more like a batch command-line tool. I’ve got nothing against batch command-line tools! But their presence is usually orthogonal to the design quality of a language, so they’re kind of irrelevant here.)
Back to the point: programming is something human minds do. Not by holding the whole codebase in your head at once; but by loading and unloading parts from your mind, where at any given time you need a useful amount of mental-model loaded at once in order to see your way through to changing it. (This is why structured programming using macro-assemblers was a revolution in productivity: modules, functions, and block-statements are intuitive units to be mentally loaded/unloaded as a whole, freeing programmers from needing to keep an unbounded mental buffer of JMP spaghetti.)
The more high-friction the language, the less of it can fit in your mental buffer at a time, and so the more “cold lookups” your mind will suffer, where you need to look something up about the codebase (likely evicting something else in the process.)
Consider a process running on a computer without enough memory to keep the entire process’s hot state loaded into said memory at a time. The result is swapping: a slow, high-overhead process of constantly reloading state from disk and evicting other state in the process.
On computers, we can fix swapping by adding more memory—enough to comfortably hold the hot state. But human minds can’t just be upgraded like that. Rather than making our memories bigger, we have to make the state smaller — to pack it more tightly and efficiently into our memory. A low-friction language is one that packs better.
Using an IDE to paper over the problems of a high-friction language, is like seeing a computer system that’s swapping, and “fixing” it by replacing its slow HDD storage with fast RAIDed NVMe storage. Sure, performance increases. But the program is still doing something stupid — a bunch of needless swaps-to-disk. It’s just doing them quickly. The money that had been spent on that RAIDed NVMe storage, could have been much-better spent on just adding more RAM to the system.
And the effort that a human goes to to learn an IDE, could have been much-better spent on learning a language the mental model of which packs better into the human mind.
It is organizationally costly to upgrade the human brain to increase RAM. Much cheaper to increase HDD storage through rote memorization and even more cheaper to access External storage on StackOverflow. That is where Verbosity helps, to signal when to stream certain Paradigms into your active memory, and keywords to use when searching for answers online.
A 4mb Language can't run on a 3mb brain, so the efficiency it offers at compressing your mental model is useless. But if you have a 1mb Language kernel that dynamically streams 1mb of Paradigms at a time, and then uses the last 1mb to work on the code, then you are capable of functioning.
C# is the only language for which I have had to deal with large monolithic codebases.
For smaller codebases, I prefer VS Code. Especially now that I go back and forth between JS, Typescript, Python, and occasionally Go and Java. I’m also frequently editing yaml and JSON. Having one editor is more ideal.
Linting, snippets, autocomplete, are all things that prevent you from bending the nail or breaking off the head which makes maintenance harder for the person who has to find the nail and remove the nail. If I don't have to redo the nail, the cost of the nail and my time are also saved.
Low-friction programming languages just don't give people all those N dimensions of inter-module connectivity in the first place; and for what dimensions they do provide, they steer people away from using them too often, perhaps with opinionated linting, or by building the standard library out of the simple stuff, such that all code that interacts with the stdlib keeps to the same simple style to avoid style-clash.
This approach will also "reduce the cognitive load someone has to experience contributing to code that they didn't write"; but it reduces that cognitive load even for people reading the code in a text editor. Or for the maintainers throwing snippets of the code around in a PR / patch-submission mailing-list.
I think this excludes every programming language known to man, since 1980 or so :-))
The glaring hole with C# however is, it is designed for Visual Studios, and that is NOT available for Linux. This is a GIANT hole in the programming world. Visual Studios Code fills that gap, thankfully, and I can use it in engines like Godot to make games. While these may not be AAA titles, they aren't an insignificant portion of developers in the world. Not only that, but C# is used in other areas of development where the preferred platform is Linux.
Luckily, for me and those of my ilk, people have hobbled together a system in VSC where Linux works with Godot and the exports are fine, but it's not nearly as easy as a supported system like Visual Studios on Windows and Mac.
I was a lot earlier in my maturation at that point, but I still think my intuitions were sound. I like languages like Python which lack syntactic noise and make it easier to write compact self-explanatory things. I used to hate JavaScript but with ES7 onwards, I've begun to like it much more because it's easy to write compact, self-explanatory things.
I was forced to write Java for work a few months ago and rudely awakened in a painful manner. Yes, you adapt to it but deep down inside, you know that it sucks and you prefer going back to your better alternative.
I don’t have trouble working things out in other languages, as needed, but rarely find myself having these little bursts of inspiration.
That's another thing I love about Python. There's such a high chance that someone's already written something that would take me lifetimes to write, and it's so easy for me to just pull it in and use it.
A string literal can be any sequence of characters so it's up to you to type them, but when I'm accessing members of a class, I'm not interested in being able to try to access a random string that is not a member of that class.
Re: your thinking comment, I also think it's the opposite. When the IDE follows the constraints of the language, it's more intuitive than when it suggests typing random strings everywhere is fine. That's not how a language works, and neither do I find it constructive to think that way.
However, I vehemently disagree with some of your (implied?) points. In particular, some functions offered by IDEs and other codebase management tools are almost equally useful for both simple and complicated languages. For example refactoring tools, or if we really look at the stem, code-base analysis tools that 1. parse the language, and, 2. have APIs for modifications, are some of the "power tools" that could be useful for virtually any programmer.
As an IDE-averse person, I find myself reaching out for the simpler languages far more often, and do actively dislike high-ceremony systems. All the while, I am painfully aware of the limitations of using only a [good] text editor. My main alternative (to mainstream IDEs) is to context-switch between pure code-writing episodes and refactoring/analysis/etc. periods (using other tools, most on the command line or with output to a web-browser).
Also regarding your comparison between written programs and mathematics: I think that comparison is relevant ONLY for low-churn fundamental code (such as core libraries, or generic utility libraries) and rather poor in a LOT of "business logic"/"presentation layer" code. I myself play code-golf and run several _mental_ redesigns/refactorings when working on code that should be solid and not subject to frequent revisions. In such cases, yes, the code should be readable, well designed, and simple to follow (clarity slightly preferred over performance for the core). Nearly formal and exhibiting formal/math-like traits. On the other hand, "business logic" code is inherently unstable and could require numerous revisions purely due to external requirements. Such code usually shouldn't be optimized for tight simplicity. "the first thing that came to my mind"-level clarity with comments can suffice. Common IDEs do help with the latter type of code (while, I think, not much with the former).
Beside serious analysis and modification tools, there is an extra set of features provided by many IDEs, such as lookups (lookup function/library/etc. initiated while looking at code), autocompletion (based on parsed code or just string matching), etc. These are certainly nice, but I don't think they change the landscape much between IDEs and plain text editors (or rather, editor-and-a-documentation-lookup-web–browser-on-the-side).
So, for me, IDEs are certainly valuable even if I don't use them frequently. In a more ideal setup, I would write my code in my preferred editor without much hand-holding and with just the simplest of IDE-like features. And then, separately, do codebase "polishing" with dedicated analysis tools that may or may not look like conventional IDEs.
Personally, I'd rather use a plain-old "code editor" (with at most syntax highlighting, but no snippets, let alone autocomplete), in part because doing so will actively steer me away from languages that weren't designed with the User Experience of writing them in mind.
Imagine if there was a hammer that was so badly-balanced that—despite being not too hard to lift—you needed to be wearing a powered exoskeleton to accurately hold and swing it. That'd be a bad hammer, right? Hammers are hand-tools; it's an expectation that they'll work when used "manually", i.e. with raw human capability alone.
Programming languages are hand-tools as well, in an essential sense. Like mathematics, the "interface" through which we manipulate a codebase is plain, raw text — sequences of symbols. We humans understand source code by reading it with our eyes; and then we write or modify it by editing it the same as any other text. We can do the whole thing on paper, or a blackboard, or even purely in our mind's eye. That's kind of the idea behind having a source code representation of a program in the first place, divorced from a machine code representation — to give us humans a formalism we can intuitively handle. A programming language is something you can think in.
Seen through that lens, a programming language that requires the cybernetic prosthesis of an IDE to read and write it fluidly, is a bad programming language. You don't have an IDE loaded in your head. So how will you think in it? (Usually: by thinking in a vague pseudocode instead; or even, by thinking in a different language than the one you're writing in. Obviously, this is going to be lower-productivity than coming up with a mental model of the problem that can be directly typed into a computer.)
(Before anyone makes a supposition about my language biases: both "complex" and "simple" language designs can be inherently high-friction due to their design choices. Scheme has very little syntax, but it's also high-friction, in that it's easy to get lost in a sea of parens unless your editor helps you along with a feature like Emacs' paren minor-mode. Most modern code editors do have something like that, but it's still a form of lubrication to overcome a point of friction in the syntax; one that some of Scheme's linguistic siblings—e.g. Clojure—avoid by increasing syntax.)