Start by learning assembly for some small, manageable microcontroller, like PIC16 (if they still sell those) or a low-end Atmel, preferably one that you could wire up yourself. Prove to yourself that you can make LEDs light up by writing bits to ports. This will give you a good understanding of what computers actually do behind the scenes.
Follow that up with C, for your desktop machine. Implement some common data structures like linked lists and binary trees (you could do it in assembly as well, but it’d be too annoying). Get a good feeling for pointers, memory allocation, and recursion. Learn what clean syntax feels like.
Finally, learn Scheme, which is a language of pure abstraction completely divorced from any hardware, operating system, or syntax (ok, this isn’t 100% true, but it’s close). Understand how you can manipulate very simple building looks to build powerful expressions, and how code can be used to generate other code and pass it around. Implement an object-oriented “meta-language” in Scheme.
At this point, you’ll be well equipped to learn any other language you want in a manner of days. You will probably never use assembly, C, or Scheme ever again, but which would you rather have—a single tool, or a factory inside your mind that lets you create tools at will ?
Oh, and stay as far away from C++ as you possibly can, it’s pure poison.
I recommend outright reversal of the above process. If you absolutely must learn assembly language, do it once you can already program. The same applies to other excessively low level languages with a lot of emphasis on memory management rather than semantic expression.
I actually started with Basic, then went to Perl, then Python (which I didn’t grok all that much at the time), and finally Forth, which is probably lower level than C in some respects but was somehow easier for me to stick with. I tried picking up C and couldn’t get past Hello World. With Forth (specifically the RetroForth project—which was a bit less smooth at the time) I built my own linked lists, dictionaries, string splitters, and stuff like that, using concatenation that maps more or less directly to machine code. Now when I look back at these other languages I see real stuff going on instead of magic. Maybe this is the equivalent of practicing fencing with a weighted sword.
This still misses a few ares. You would not be completely ready to learn OCaml (or Haskell) with sophisticated type inference system. You would probably not be ready to learn Erlang (unless you used something like Termite if your Scheme turned out to be Gambit). If you picked an R5RS Scheme, you would probably miss some parts of the power of macros. You would miss APL/J array processing. You would probably (unless you pay attention to using some Scheme object system) miss SmallTalk object orientation and also multiple dispatch-capable object oriented programming (which is not SmallTalk, but, say Common Lisp Object System).
As for calling C syntax clean… Well…
Anyway, for long-term learning you need to ask what concepts to learn—you will most probably have a choice of languages (but no one specific languages will include all).
You would not be completely ready to learn OCaml (or Haskell) with sophisticated type inference system. You would probably not be ready to learn Erlang (unless you used something like Termite if your Scheme turned out to be Gambit).
All true, but you should be able to pick those up easily enough if you have internalized the other concepts. For example, type inference is much easier to understand when you realize that, underneath it all, there’s no such thing as a “type” anyway, just pointers pointing to memory blocks of various sizes; and that, on the other hand, you could construct whatever notion of a “type” that you want, by using functional programming.
I have to admit, though, that I was never a big fan of macros, in any language.
As for calling C syntax clean… Well...
I meant, as compared to raw assembly.
Anyway, for long-term learning you need to ask what concepts to learn
That was kind of my point: instead of learning a specific set of concepts, learn just enough of the right ones, so that adding new concepts becomes easy.
type inference is much easier to understand when you realize that, underneath it all, there’s no such thing as a “type” anyway, just pointers pointing to memory blocks of various sizes
Well, it looks that either you have some minimal experience with abstract algebra or you will need to learn some of it while working with complex type systems.
Learning new powerful abstraction to the level of being able to exploit it for complex tasks is a matter of a few days of full-time learning/thinking/tinkering per se, so learning new languages will still not be trivial. And you have to spend a few weeks collecting minimal best practices.
I was never a big fan of macros, in any language
Given that only macro-assembler and Lisp-like languages even had complex enough macros to matter until recently…
Well, from my experience I can say that implementing a Lisp-macros-like system for Pascal did help me simply to cope with some project. Some say that macros are just for fixing deficiencies of the language; while it is partially true, macros are useful because you will never have a language precisely fit for your task at hand in a large project.
As for calling C syntax clean… Well…
I meant, as compared to raw assembly.
But on the same distance from hardware, there is also Pascal. I remember being able to read Pascal without knowing much of it, and it is still not verbose enough to be annoying to some people. While learning C, it is a nice idea to write down every way to write a hard-to-read code buried in the syntax that you come up with. After a while, it will make a useful checklist when cleaning up sloppy code.
That was kind of my point: instead of learning a specific set of concepts, learn just enough of the right ones, so that adding new concepts becomes easy.
The main problem is that unless you have learned some concept you don’t really know whether you need to try to apply it. You give a nice set of starter concepts, of course, but I found it useful to show that there are very numerous concepts not mentioned—and it is a good idea to be aware of them.
A piece of advice not to take seriously is to look at http://www.falconpl.org/index.ftd?page_id=facts . Afterwards, one could find where all the mentioned concepts are implemented sanely and learn those languages...
Well, it looks that either you have some minimal experience with abstract algebra or you will need to learn some of it while working with complex type systems.
Or both !
so learning new languages will still not be trivial.
It depends on what you mean by “trivial”. Learning a programming language to the point where you can effectively employ it to solve a complex real-world problem will never be easy, for the same reason that learning a human language to the point where you can converse in it will never be easy. There are always a bunch of library APIs (or dictionary words), edge cases, and best practices (or colloquialisms) to memorize. But understanding the basics of the language does not have to be hard.
macros are useful because you will never have a language precisely fit for your task at hand in a large project.
Technically, this is a language deficiency in and of itself. I rarely find myself wishing I had more macros in Python, or even C#. I do wish there were macros in Java, but that’s because they still haven’t implemented closures correctly.
But on the same distance from hardware, there is also Pascal.
I dislike Pascal because I find its pointer syntax to be needlessly obscure. That said, I haven’t used Pascal for like 15 years, so my knowledge could be hopelessly outdated.
You give a nice set of starter concepts, of course, but I found it useful to show that there are very numerous concepts not mentioned—and it is a good idea to be aware of them.
Sure, but you’ve got to draw the line somewhere; after all, there are as many concepts as there are language designers ! Many of them can be rolled into more general concepts (f.ex., all kinds of different type systems can be rolled into the “type system” category). Others aren’t even concepts at all, but simply useful tools, such as regular expressions or file I/O. You can’t learn everything at once, so you might as well start with the big ideas, and go from there.
for the same reason that learning a human language to the point where you can converse in it will never be easy. There are always a bunch of library APIs (or dictionary words), edge cases, and best practices (or colloquialisms) to memorize. But understanding the basics of the language does not have to be hard
All this is simple to look up—programming is not fluent speech, it is writing. The problem is that similar words have radically different combinations of meanings. And also, sometimes there are totally new concepts in the language. You see it better after you try learning a language where concepts do match your expectations.
Technically, this is a language deficiency in and of itself. I rarely find myself wishing I had more macros in Python, or even C#.
Well, I have written significant amount of code in Python and I did have to use workarounds that would be cleaner as macros… If you consider your language a good fit to your task at any time, you are likely just not asking for the best. It can be mitigated if your requirements are understandable.
I dislike Pascal because I find its pointer syntax to be needlessly obscure
It is still the same. But C syntax is plainy malicious even in assignments, so why care about pointers. Somehow, Google managed to create a clean C-derived syntax in Go by streamlining a lot of rules.
You can’t learn everything at once, so you might as well start with the big ideas, and go from there
But it is also clear that you should always know that you are not learning some magical set of all basic concepts, just the concepts that are simpliest to learn in the beginning.
All this is simple to look up—programming is not fluent speech, it is writing.
Have you ever tried learning a foreign language ? Maybe it was easy for you—I know people who seem to have a natural aptitude for it—but for me, it was basically a long painful slog through dictionary-land. Yes, from a strictly algorithmic standpoint, you could look up every word you intend to read or write; but this works very poorly for most humans.
If you consider your language a good fit to your task at any time, you are likely just not asking for the best.
I think your demands might be a bit too strict. I am perfectly ok with using a language that is a good, though not 100% perfect, fit for my task. Sometimes, I would even settle for an inferior language, if doing so grants me access to more powerful libraries that free me from extra work. Sure, I could “ask for the best”, but I have other goals to accomplish.
But C syntax is plainy malicious even in assignments...
How so ? Perhaps you were thinking of C++, which is indeed malicious ?
But it is also clear that you should always know that you are not learning some magical set of all basic concepts, just the concepts that are simpliest to learn in the beginning.
I agree with you that there’s no magical silver bullet set of concepts, but I also believe that some concepts are vastly more important than others, regardless of how easy they are to learn. For example, the basic concept you internalize when learning assembly is that (roughly speaking) the computer isn’t a magical genie with arbitrary rules—instead, it’s a bag of circuits that moves electrons around. This idea seems trivial when written down, but internalizing it is key to becoming a successful programmer. It also leads naturally to understanding pointers, on which the vast majority of other languages—yes, even Scheme—are built. I doubt that you can properly understand things like type inference without first understanding bits and pointers.
English, French (I usually forget the latter and recover it when I have any proximate use for it). My native language is Russian. It is a big relief when learning French that most words have the same translations in many contexts. This multi-translation problem is way more annoying than simply looking up words.
Sometimes, I would even settle for an inferior language, if doing so grants me access to more powerful libraries that free me from extra work
This actually confirms my point. You will have to choose inferior language from time to time, and its lack of tools of adapting language to your task is either local incompetence of language authors or lack of resources for development of language or lnaguage community arrogance.
But C syntax is plainy malicious even in assignments…
How so ? Perhaps you were thinking of C++, which is indeed malicious ?
“i+= i++ + ++i;” can be reliably compiled but not predicted. There are many actual everyday examples like “if(a=b);”.
Of course, it is not even close to C++, which takes malicious semantics a few levels up.
basic concept you internalize when learning assembly is that (roughly speaking) the computer isn’t a magical genie with arbitrary rules
leads naturally to understanding pointers, on which the vast majority of other languages
Any command-line programming environment will make you internalize that computer has some rules and that it does what you order—literally.
x86 assembly is quite arbitrary anyway. Maybe LLVM assembly (which is closer to “pointer machine” than to “random access machine) would be nicer. After all, high-level languages use specially wrapped pointers even in implementation.
I doubt that you can properly understand things like type inference without first understanding bits and pointers.
You cannot properly understand some performance implications, maybe. But the actual input-output correspondence can be grokked anyway. Of course, only higher-order functions have a strict proof that they can be understood without proper understanding of imperative semantics.
This multi-translation problem is way more annoying than simply looking up words.
It’s possible that you are much better at automatically memorizing words than I am.
You will have to choose inferior language from time to time, and its lack of tools of adapting language to your task is either local incompetence or lack of resources or arrogance.
Wait… what ? Are you saying that, when I have some practical task to finish, the best solution is to pick the most elegant language, disregarding all other options—and that not doing so makes me arrogant ? I am pretty sure this isn’t right. For example, my current project involves some Bluetooth communication and data visualization on Windows machines. There are libraries for Java and C# that fulfill all my Bluetooth and graphical needs; the Python library is close, but not as good. Are you saying that, instead of C#, I should just pick Scheme or Haskell or something, and implement my own Bluetooth stack and drawing APIs ? I am pretty sure that’s not what you meant...
“i+= i++ + ++i;” can be reliably compiled but not predicted.
Ok that’s a good point; I forgot about those pre-/post-increments, because I avoid them myself. They’re pretty terrible.
On the other hand, the regular assignment operator does make sense; the rules that let you say “if(a=b)” also let you say “a=b=c”. The result of an assignment operator is the RHS. I don’t see this as a bad thing, though it might’ve been better to use “eq” or some other token instead of the comparison operator “==”.
Any command-line programming environment will make you internalize that computer has some rules and that it does what you order—literally.
True, and that’s a good lesson too, but programming in assembly lets you get close (though not too uncomfortably so) to the actual hardware. This allows you to internalize the idea that at least some of these rules are not arbitrary. Instead, they stem from the fact that, ultimately, your computer is an electron-pushing device which is operating under real-world constraints. This is important, because arbitrary rules are something you have to memorize, whereas physical constraints are something you can understand.
You are right about x86 assembly, though, which is why I mentioned “a small microcontroller” in my original post. Their assemblies tend to make more sense.
But the actual input-output correspondence can be grokked anyway.
You are right, though this depends on which problem you’re solving. If you approach the programming language completely in abstract, then yes, you can understand things like input-output correspondence from the strictly algebraic point of view. What you won’t understand, though (at least, not as readily), is why all these language features were created in the first place, and which problems they are designed to solve. But if you never intend to write practical programs that perform applied tasks, maybe that’s ok.
It’s possible that you are much better at automatically memorizing words than I am.
Or simply annoyed by different things.
Are you saying that, when I have some practical task to finish, the best solution is to pick the most elegant language, disregarding all other options—and that not doing so makes me arrogant?
Sorry for unclear phrase. I mean that language’s lack of tools is language’s arrogance.
rules that let you say “if(a=b)” also let you say “a=b=c”
“a=b=c;” vs “a=c; b=c;” is not much; the former syntax simplifies injection of vulnerabilities (intentionally or incidentally).
Instead, they stem from the fact that, ultimately, your computer is an electron-pushing device which is operating under real-world constraints
You are right about x86 assembly, though, which is why I mentioned “a small microcontroller” in my original post. Their assemblies tend to make more sense
I have written in C for these microcontrollers—physical constraints visibly leak into the language, so if you are learning C anyway, you could delay learning assembly.
why all these language features were created in the first place, and which problems they are designed to solve. But if you never intend to write practical programs that perform applied tasks, maybe that’s ok.
If you learn just Scheme and OCaml you still can understand what type system and type inference gives you.
You can appreciate steam engine without knowing nuclear physics, after all.
I mean that language’s lack of tools is language’s arrogance.
I’m still not sure what you mean by that. Are you suggesting that all languages should make all possible tools available ? For example, should every language, including C, Javascript, Java, C#, Ruby, Python, Dart, etc., provide a full suite of Bluetooth communication libraries ? I agree that it would be really neat if this were the case, but IMO it’s highly impractical. Languages are (so far) written by humans, and humans have a limited amount of time to spend on them.
“a=b=c;” vs “a=c; b=c;” is not much; the former syntax simplifies injection of vulnerabilities (intentionally or incidentally).
What do you mean by “injection of vulnerabilities” ? Also, “a=b=c;” should be more correctly rendered as “b=c; a = b;”. This makes it possible to use shorthand such as “if ( (answer = confirmRequest()) == CANCEL) … ”.
so if you are learning C anyway, you could delay learning assembly.
Sure, you could delay it, but it’s best to learn it properly the first time. There are certain essential things that are easy to do with assembly that are harder to do with C: for example, balancing your branches so that every iteration of the main loop takes the same number of cycles.
If you learn just Scheme and OCaml you still can understand what type system and type inference gives you.
If you were a person who only knew Scheme, how would you explain “what type inference gives you”, and why it’s useful ?
I mean that language’s lack of tools is language’s arrogance.
I’m still not sure what you mean by that. Are you suggesting that all languages should make all possible tools available ?
It was a clarification to some specific phrase in my previous comment. The original phrase answers both your questions. I specifically said that it can be lack of resources or competence, not only arrogance. And this is specifically about tools that allow you to tailor the language to your specific task, so that there are no problems with language that you are prohibited from solving. Somebody can always write a bluetooth library.
certain essential things that are easy to do with assembly that are harder to do with C: for example, balancing your branches so that every iteration of the main loop takes the same number of cycles
This is not essential for many applications, even with what is now called microcontrollers. Learning optimization on that level is something you can do while having a good grasp of other concepts already.
If you were a person who only knew Scheme, how would you explain “what type inference gives you”, and why it’s useful ?
Type inference allows you to write with strict typechecks and catch some kinds of errors without cluttering the code with type specifications for every variable.
And this is specifically about tools that allow you to tailor the language to your specific task, so that there are no problems with language that you are prohibited from solving. Somebody can always write a bluetooth library.
That makes sense, and I do wish that more languages supported more capabilities, but I think it’s unrealistic to expect all languages to support all, or even most, or even some large fraction of real-world tasks that are out there. There are vastly more tasks than there are languages: graphics (raster, vector, and 3d, on various systems), sound, desktop user interfaces, bluetooth, TCP/IP networking, bio-sequence alignment, finance, distributed computation, HTML parsing and rendering, SQL access… and that’s just the stuff I’d had to handle this month !
Learning optimization on that level is something you can do while having a good grasp of other concepts already.
I think the opposite is true: performing this kind of optimization (even on a “toy” program) is exactly the kind of task that can help you internalize those concepts.
Type inference allows you to write with strict typechecks and catch some kinds of errors without cluttering the code with type specifications for every variable.
I agree with you there, but I’ll play Devil’s Advocate, in my attempt to adopt the perspective of someone who only knows Scheme. So, can you give me an example of some Scheme code where the strict typechecks you mentioned are truly helpful ? To me (or, rather, my Schemer’s Advocate persona) this sounds inelegant. In Scheme, most entities are pairs, or data structures built of pairs, anyway. Sure, there are a few primitives, but why should I worry about 5 being different from 5.0 or “5” ? That sounds like a job for the interpreter.
That makes sense, and I do wish that more languages supported more capabilities, but I think it’s unrealistic to expect all languages to support all, or even most, or even some large fraction of real-world tasks that are out there
You didn’t understand my point correctly. Language per se should not support directly, say, bluetooth—because bluetooth will change in an incompatible way. Language could live without a bluetooth library—why not, there is always FFI for dire cases. But the question is about allowing to define a nice API if a need arises. More or less any metaprogramming tool that is not constrained in what it can create would do—those who want to use it, will wrap it in a layer that is nice to use, you can then just incorporate their work.
Common Lisp didn’t have any object system in the first edition of the standard; CLOS was prototyped using macros, documented, and then this documentation was basically included in standard. Of couse, macro use could be somewhat more clumsy or more explicit for any reason (make it easier to control overuse, for example) - this is not a problem. The problem is there when you have zero ways to do something—for example, to define a non-trivial iteration pattern.
So, can you give me an example of some Scheme code where the strict typechecks you mentioned are truly helpful ? To me (or, rather, my Schemer’s Advocate persona) this sounds inelegant. In Scheme, most entities are pairs, or data structures built of pairs, anyway. Sure, there are a few primitives, but why should I worry about 5 being different from 5.0 or “5” ? That sounds like a job for the interpreter.
Sorry? I was talking about things that help to catch errors. In any small snippet the errors are simple enough to find for this to be unillustrative. It only helps you when you have some kind of wrong assignment in 1K+LOC.
Learn assembly, C, and Scheme, in that order.
Start by learning assembly for some small, manageable microcontroller, like PIC16 (if they still sell those) or a low-end Atmel, preferably one that you could wire up yourself. Prove to yourself that you can make LEDs light up by writing bits to ports. This will give you a good understanding of what computers actually do behind the scenes.
Follow that up with C, for your desktop machine. Implement some common data structures like linked lists and binary trees (you could do it in assembly as well, but it’d be too annoying). Get a good feeling for pointers, memory allocation, and recursion. Learn what clean syntax feels like.
Finally, learn Scheme, which is a language of pure abstraction completely divorced from any hardware, operating system, or syntax (ok, this isn’t 100% true, but it’s close). Understand how you can manipulate very simple building looks to build powerful expressions, and how code can be used to generate other code and pass it around. Implement an object-oriented “meta-language” in Scheme.
At this point, you’ll be well equipped to learn any other language you want in a manner of days. You will probably never use assembly, C, or Scheme ever again, but which would you rather have—a single tool, or a factory inside your mind that lets you create tools at will ?
Oh, and stay as far away from C++ as you possibly can, it’s pure poison.
I recommend outright reversal of the above process. If you absolutely must learn assembly language, do it once you can already program. The same applies to other excessively low level languages with a lot of emphasis on memory management rather than semantic expression.
I actually started with Basic, then went to Perl, then Python (which I didn’t grok all that much at the time), and finally Forth, which is probably lower level than C in some respects but was somehow easier for me to stick with. I tried picking up C and couldn’t get past Hello World. With Forth (specifically the RetroForth project—which was a bit less smooth at the time) I built my own linked lists, dictionaries, string splitters, and stuff like that, using concatenation that maps more or less directly to machine code. Now when I look back at these other languages I see real stuff going on instead of magic. Maybe this is the equivalent of practicing fencing with a weighted sword.
This still misses a few ares. You would not be completely ready to learn OCaml (or Haskell) with sophisticated type inference system. You would probably not be ready to learn Erlang (unless you used something like Termite if your Scheme turned out to be Gambit). If you picked an R5RS Scheme, you would probably miss some parts of the power of macros. You would miss APL/J array processing. You would probably (unless you pay attention to using some Scheme object system) miss SmallTalk object orientation and also multiple dispatch-capable object oriented programming (which is not SmallTalk, but, say Common Lisp Object System).
As for calling C syntax clean… Well…
Anyway, for long-term learning you need to ask what concepts to learn—you will most probably have a choice of languages (but no one specific languages will include all).
All true, but you should be able to pick those up easily enough if you have internalized the other concepts. For example, type inference is much easier to understand when you realize that, underneath it all, there’s no such thing as a “type” anyway, just pointers pointing to memory blocks of various sizes; and that, on the other hand, you could construct whatever notion of a “type” that you want, by using functional programming.
I have to admit, though, that I was never a big fan of macros, in any language.
I meant, as compared to raw assembly.
That was kind of my point: instead of learning a specific set of concepts, learn just enough of the right ones, so that adding new concepts becomes easy.
Well, it looks that either you have some minimal experience with abstract algebra or you will need to learn some of it while working with complex type systems.
Learning new powerful abstraction to the level of being able to exploit it for complex tasks is a matter of a few days of full-time learning/thinking/tinkering per se, so learning new languages will still not be trivial. And you have to spend a few weeks collecting minimal best practices.
Given that only macro-assembler and Lisp-like languages even had complex enough macros to matter until recently…
Well, from my experience I can say that implementing a Lisp-macros-like system for Pascal did help me simply to cope with some project. Some say that macros are just for fixing deficiencies of the language; while it is partially true, macros are useful because you will never have a language precisely fit for your task at hand in a large project.
But on the same distance from hardware, there is also Pascal. I remember being able to read Pascal without knowing much of it, and it is still not verbose enough to be annoying to some people. While learning C, it is a nice idea to write down every way to write a hard-to-read code buried in the syntax that you come up with. After a while, it will make a useful checklist when cleaning up sloppy code.
The main problem is that unless you have learned some concept you don’t really know whether you need to try to apply it. You give a nice set of starter concepts, of course, but I found it useful to show that there are very numerous concepts not mentioned—and it is a good idea to be aware of them.
A piece of advice not to take seriously is to look at http://www.falconpl.org/index.ftd?page_id=facts . Afterwards, one could find where all the mentioned concepts are implemented sanely and learn those languages...
Or both !
It depends on what you mean by “trivial”. Learning a programming language to the point where you can effectively employ it to solve a complex real-world problem will never be easy, for the same reason that learning a human language to the point where you can converse in it will never be easy. There are always a bunch of library APIs (or dictionary words), edge cases, and best practices (or colloquialisms) to memorize. But understanding the basics of the language does not have to be hard.
Technically, this is a language deficiency in and of itself. I rarely find myself wishing I had more macros in Python, or even C#. I do wish there were macros in Java, but that’s because they still haven’t implemented closures correctly.
I dislike Pascal because I find its pointer syntax to be needlessly obscure. That said, I haven’t used Pascal for like 15 years, so my knowledge could be hopelessly outdated.
Sure, but you’ve got to draw the line somewhere; after all, there are as many concepts as there are language designers ! Many of them can be rolled into more general concepts (f.ex., all kinds of different type systems can be rolled into the “type system” category). Others aren’t even concepts at all, but simply useful tools, such as regular expressions or file I/O. You can’t learn everything at once, so you might as well start with the big ideas, and go from there.
All this is simple to look up—programming is not fluent speech, it is writing. The problem is that similar words have radically different combinations of meanings. And also, sometimes there are totally new concepts in the language. You see it better after you try learning a language where concepts do match your expectations.
Well, I have written significant amount of code in Python and I did have to use workarounds that would be cleaner as macros… If you consider your language a good fit to your task at any time, you are likely just not asking for the best. It can be mitigated if your requirements are understandable.
It is still the same. But C syntax is plainy malicious even in assignments, so why care about pointers. Somehow, Google managed to create a clean C-derived syntax in Go by streamlining a lot of rules.
But it is also clear that you should always know that you are not learning some magical set of all basic concepts, just the concepts that are simpliest to learn in the beginning.
Have you ever tried learning a foreign language ? Maybe it was easy for you—I know people who seem to have a natural aptitude for it—but for me, it was basically a long painful slog through dictionary-land. Yes, from a strictly algorithmic standpoint, you could look up every word you intend to read or write; but this works very poorly for most humans.
I think your demands might be a bit too strict. I am perfectly ok with using a language that is a good, though not 100% perfect, fit for my task. Sometimes, I would even settle for an inferior language, if doing so grants me access to more powerful libraries that free me from extra work. Sure, I could “ask for the best”, but I have other goals to accomplish.
How so ? Perhaps you were thinking of C++, which is indeed malicious ?
I agree with you that there’s no magical silver bullet set of concepts, but I also believe that some concepts are vastly more important than others, regardless of how easy they are to learn. For example, the basic concept you internalize when learning assembly is that (roughly speaking) the computer isn’t a magical genie with arbitrary rules—instead, it’s a bag of circuits that moves electrons around. This idea seems trivial when written down, but internalizing it is key to becoming a successful programmer. It also leads naturally to understanding pointers, on which the vast majority of other languages—yes, even Scheme—are built. I doubt that you can properly understand things like type inference without first understanding bits and pointers.
English, French (I usually forget the latter and recover it when I have any proximate use for it). My native language is Russian. It is a big relief when learning French that most words have the same translations in many contexts. This multi-translation problem is way more annoying than simply looking up words.
This actually confirms my point. You will have to choose inferior language from time to time, and its lack of tools of adapting language to your task is either local incompetence of language authors or lack of resources for development of language or lnaguage community arrogance.
“i+= i++ + ++i;” can be reliably compiled but not predicted. There are many actual everyday examples like “if(a=b);”.
Of course, it is not even close to C++, which takes malicious semantics a few levels up.
Any command-line programming environment will make you internalize that computer has some rules and that it does what you order—literally.
x86 assembly is quite arbitrary anyway. Maybe LLVM assembly (which is closer to “pointer machine” than to “random access machine) would be nicer. After all, high-level languages use specially wrapped pointers even in implementation.
You cannot properly understand some performance implications, maybe. But the actual input-output correspondence can be grokked anyway. Of course, only higher-order functions have a strict proof that they can be understood without proper understanding of imperative semantics.
It’s possible that you are much better at automatically memorizing words than I am.
Wait… what ? Are you saying that, when I have some practical task to finish, the best solution is to pick the most elegant language, disregarding all other options—and that not doing so makes me arrogant ? I am pretty sure this isn’t right. For example, my current project involves some Bluetooth communication and data visualization on Windows machines. There are libraries for Java and C# that fulfill all my Bluetooth and graphical needs; the Python library is close, but not as good. Are you saying that, instead of C#, I should just pick Scheme or Haskell or something, and implement my own Bluetooth stack and drawing APIs ? I am pretty sure that’s not what you meant...
Ok that’s a good point; I forgot about those pre-/post-increments, because I avoid them myself. They’re pretty terrible.
On the other hand, the regular assignment operator does make sense; the rules that let you say “if(a=b)” also let you say “a=b=c”. The result of an assignment operator is the RHS. I don’t see this as a bad thing, though it might’ve been better to use “eq” or some other token instead of the comparison operator “==”.
True, and that’s a good lesson too, but programming in assembly lets you get close (though not too uncomfortably so) to the actual hardware. This allows you to internalize the idea that at least some of these rules are not arbitrary. Instead, they stem from the fact that, ultimately, your computer is an electron-pushing device which is operating under real-world constraints. This is important, because arbitrary rules are something you have to memorize, whereas physical constraints are something you can understand.
You are right about x86 assembly, though, which is why I mentioned “a small microcontroller” in my original post. Their assemblies tend to make more sense.
You are right, though this depends on which problem you’re solving. If you approach the programming language completely in abstract, then yes, you can understand things like input-output correspondence from the strictly algebraic point of view. What you won’t understand, though (at least, not as readily), is why all these language features were created in the first place, and which problems they are designed to solve. But if you never intend to write practical programs that perform applied tasks, maybe that’s ok.
Or simply annoyed by different things.
Sorry for unclear phrase. I mean that language’s lack of tools is language’s arrogance.
“a=b=c;” vs “a=c; b=c;” is not much; the former syntax simplifies injection of vulnerabilities (intentionally or incidentally).
I have written in C for these microcontrollers—physical constraints visibly leak into the language, so if you are learning C anyway, you could delay learning assembly.
If you learn just Scheme and OCaml you still can understand what type system and type inference gives you.
You can appreciate steam engine without knowing nuclear physics, after all.
I’m still not sure what you mean by that. Are you suggesting that all languages should make all possible tools available ? For example, should every language, including C, Javascript, Java, C#, Ruby, Python, Dart, etc., provide a full suite of Bluetooth communication libraries ? I agree that it would be really neat if this were the case, but IMO it’s highly impractical. Languages are (so far) written by humans, and humans have a limited amount of time to spend on them.
What do you mean by “injection of vulnerabilities” ? Also, “a=b=c;” should be more correctly rendered as “b=c; a = b;”. This makes it possible to use shorthand such as “if ( (answer = confirmRequest()) == CANCEL) … ”.
Sure, you could delay it, but it’s best to learn it properly the first time. There are certain essential things that are easy to do with assembly that are harder to do with C: for example, balancing your branches so that every iteration of the main loop takes the same number of cycles.
If you were a person who only knew Scheme, how would you explain “what type inference gives you”, and why it’s useful ?
It was a clarification to some specific phrase in my previous comment. The original phrase answers both your questions. I specifically said that it can be lack of resources or competence, not only arrogance. And this is specifically about tools that allow you to tailor the language to your specific task, so that there are no problems with language that you are prohibited from solving. Somebody can always write a bluetooth library.
This is not essential for many applications, even with what is now called microcontrollers. Learning optimization on that level is something you can do while having a good grasp of other concepts already.
Type inference allows you to write with strict typechecks and catch some kinds of errors without cluttering the code with type specifications for every variable.
That makes sense, and I do wish that more languages supported more capabilities, but I think it’s unrealistic to expect all languages to support all, or even most, or even some large fraction of real-world tasks that are out there. There are vastly more tasks than there are languages: graphics (raster, vector, and 3d, on various systems), sound, desktop user interfaces, bluetooth, TCP/IP networking, bio-sequence alignment, finance, distributed computation, HTML parsing and rendering, SQL access… and that’s just the stuff I’d had to handle this month !
I think the opposite is true: performing this kind of optimization (even on a “toy” program) is exactly the kind of task that can help you internalize those concepts.
I agree with you there, but I’ll play Devil’s Advocate, in my attempt to adopt the perspective of someone who only knows Scheme. So, can you give me an example of some Scheme code where the strict typechecks you mentioned are truly helpful ? To me (or, rather, my Schemer’s Advocate persona) this sounds inelegant. In Scheme, most entities are pairs, or data structures built of pairs, anyway. Sure, there are a few primitives, but why should I worry about 5 being different from 5.0 or “5” ? That sounds like a job for the interpreter.
You didn’t understand my point correctly. Language per se should not support directly, say, bluetooth—because bluetooth will change in an incompatible way. Language could live without a bluetooth library—why not, there is always FFI for dire cases. But the question is about allowing to define a nice API if a need arises. More or less any metaprogramming tool that is not constrained in what it can create would do—those who want to use it, will wrap it in a layer that is nice to use, you can then just incorporate their work.
Common Lisp didn’t have any object system in the first edition of the standard; CLOS was prototyped using macros, documented, and then this documentation was basically included in standard. Of couse, macro use could be somewhat more clumsy or more explicit for any reason (make it easier to control overuse, for example) - this is not a problem. The problem is there when you have zero ways to do something—for example, to define a non-trivial iteration pattern.
Sorry? I was talking about things that help to catch errors. In any small snippet the errors are simple enough to find for this to be unillustrative. It only helps you when you have some kind of wrong assignment in 1K+LOC.