Wonderful! -- a chance for me to advance my agenda!
If you want something done in a quick and dirty fashion, and you simply do not care how things work, try Python. A lot of stuff is simply done for you, whether through syntax or libraries. It may be unsatisfying. Comes with an interactive interpreter.
If you want something done in a quick and dirty fashion, but do not want to install too many new programs on your clean UNIX system, try sh/bash/zsh. Embrace the pipe! Also, you may know some already. As a shell, is interactive, of course.
If you do not really care how things work in reality, but do want to actually learn something, try Scheme (perhaps with the SICP). Especially good for those who like (superficial) simplicity. For more industrial/less pure an environment, try Common Lisp or Clojure. You shall learn the value of everything and the cost of nothing. There are also other LISP-type languages out there, do you really want to spend months reading about them all? Most LISPS give the ability to generate more LISP code with other LISP code, called macros -- the saying is code is data. This allows the production of little domain-specific languages (DSLs) to be produced for the problem, though usually the DSLs must (be (composed of (parentheses just as (LISP is)))). Detractors say that LISP stands for Lots of Irritating Superfluous Parentheses (it actually stands for LISt Processing, as it built from a datastructure known as a list). Comes with an interactive interpreter (Read-Eval-Print-Loop, REPL).
If you want to understand how your UNIX system (you have a Pandora or Pyra, at least, right?) works on a lower level, try C. It is very much a mainstream language, with little in the way of exotic/interesting features, but it does allow you the sensation of touching a real computer, with pointers, manual memory management (all of the above are (effectively) garbage collected) and so on. It is also relatively simple, describing a lowest-common-denominator-type virtual computer. Unlike with (most of, the LISPs may be an exception) the other languages above, if you can demonstrate decent knowledge of C, then people will generally assume that you know something about computers as one must learn quite a few other things to make anything serious (make, gcc flags, static vs. dynamic linking etc.). This can be both good and bad. Usually no interactive interpreter, compiler only.
If you really want to understand what you are doing (kudos!), rather than to get the right answer (Tom Lehrer -- search for New Math) then try the Assembly language for your computer. It can be a very educational experience -- I regret that I delayed for so long before I taught it to myself (on x86_64) -- as it strips away most of the abstractions in use elsewhere: there are no built-in loops or if-statements (at least, on x86, none you are expected to use), no variables, no built-in memory manager (C provides malloc and free) and no functions (at least, not in the way you may know). Your assembler will provide you with some registers (very small, fast memory locations -- most operations use at least one register), mnemonics (short names that translate into machine-code instructions) and probably a text-substitution-macro language (which is so useful that you will probably find yourself using it). The mnemonics are the statements (better: orders or imperatives as in imperative programming) given to the machine to direct its behaviour, such as GOTO/JMP, CMP, AND, NOT etc. (you can do pretty much anything given those mnemonics and enough effort -- I believe only those, or fewer, are required for Turing completeness; of course, most computers will give you more instructions for the sake of both speed and ease-of-use). You will also, likely, learn how most programs depend on the operating system (you do want an operating system, don't you? Or would you rather write your own I/O routines?) and learn what a stack (there are probably built-in instructions for working with at least one). Also, remember, one man's constant is another man's variable -- there is very little you cannot do with just assembly language, including unwise things, and what little you cannot do (self-modifying code, for example) can be done with a smattering of machine-code knowledge, in addition. (If someone knows of something that can only be done with pure machine-code -- operating on the numerical values of the instructions to do... what? -- please tell me what it is. I only really have x86 in mind, and perhaps also ARM). Assembly usually has no interactive interpreter, though assembly (the process) is usually quite quick, so it can approach the effects of having one; machine-code DOES have an interpreter: the machine! In theory one could input machine-code directly as hexadecimal numbers using tools such as xxd, but it is usually not practical.
You may have heard of Object-Oriented Programming. For most of the last 30 years it has been the prime programming fad (there are uses for it, but it has been promoted as the solution to everything, inappropriately -- also, there is no agreement on what it really means), fading a bit now though (in favour of functional programming). Two OOP languages you may have heard of are C++ and Java. I recommend leaving C++ for later: it may be good if you are already knowledgeable, but for now it is very large, (almost) as unsafe as C (in terms of crashing and unexpected behaviour from manual memory manipulation, with assembly more so) but more complex (both the language itself and the code, written by other people, that you may want to use, though that may just be my own experience; if you want to learn about lower level programming, C is probably a better place to start). Notwithstanding that it is considered rather uncool at the moment, Java does, mostly (I hear there is NullPointerException), save you from C++'s troubles regarding manual memory management and "safety", though less when it comes to complex code libraries. Java also, supposedly, works on multiple platforms without requiring you to change your code -- write once, run anywhere -- and is (sort of) the standard language for programming on Android (assuming that interests you). There are other OOP languages, but I do not have experience with them. Neither C++ nor Java are designed with interactive interpreters in mind; I have never seen one.
Increasingly popular now are the functional languages (also known as applicative languages, as opposed to concatenative languages), which have a reputation as being academic and mathematical -- few more so than Haskell (named after a mathematician!). I have cheated a bit: most LISPs are designed to make functional programming easy, classically Scheme, and in many imperative or procedural languages it is possible to program in a somewhat functional style -- i.e. without changing many global (or static) variables in most of the functions/procedures, so that they always return the same result with the same inputs. Haskell, though, enforces functional programming. Functional programming is generally regarded as good for reasoning about (and testing) small procedures and, particularly, for concurrent programs, though I doubt that you should worry too much about that, right now. I know little about Haskell, though, so here some other functional languages that I have on my TODO list: ML, Erlang. I do not know about interactivity for Haskell, but I believe it is mainly used with the Glasgow Haskell Compiler, which suggests to me that it is not normally used interactively.
Finally, I am currently playing with a concatenative language called Forth or FORTH(by the way, if something is spelt in ALL-CAPS, that frequently means that the language is old-enough that it existed before systems with lower-case letters were common/universal: some of the oldest systems had only UPPER-CASE -- after all, if the input is a punch-card, why care what the text (you cannot see) looks like? -- and, as a result, both Forth and Common Lisp are case insensitive, while most more modern languages are the opposite). Concatenative means that any program can be placed before or after another program and it will run, even if only to crash. Forth is quite an odd language: while Python and C mostly use prefix notation (for functions, f(a,b,c)) with some infix (x+y) and LISP only prefix ((+ x y)), Forth uses (mostly) postfix (x y +), like some old calculators. As a result, it kind-of reflects how the computer really works, as the computer needs to be given the values before they can be added together; also, postfix allows Forth to be concatenative, with the addition of an implicit stack, so the program x y +, which leaves x+y on the stack, can prepended to the program z *, which accepts a value on the stack and multiplies it by z, to give a new program x y + z *, which leaves z*(x+y) on the stack. This new program can also be prepended or appended to any other valid program to give a valid program, which is important as function calling in Forth is dependent on this behaviour. Forth also has some other fun features: direct return-stack (yes, there are two stacks built-in!) manipulation (allowing tricks like copying the return address of a calling function multiple times so that the latter part of that function will return to itself multiple times B) ), compile-time functions (which are a bit like LISP macros, allowing the creation of DSLs in a similar way, but without the parentheses of LISP; here the saying is data is code) and use of the Forth interpreter in one's own programs (I wrote a simple stack-based virtual machine language translator by just treating every instruction in that language as a Forth function and getting the Forth interpreter to read in the non-Forth source code B) ). As such, using Forth is a bit like coding the inside of a very simple compiler, especially as it also starts at a very low level (like C) with access to memory address, bitwise operations and manual memory management; but since, in Forth, everything is just a function (or word) except for numbers, one can quickly build up one's own high level language inside the program. I should also note that Forth is a very simple language, in terms of implementation (not so much in terms of use, as, eventually, one needs to have some idea of how the compiler works to do the most interesting things), and has been used as the machine-code/assembler for some processors with two built-in stacks (Intel and AMD x86 processors have only one). Also, in Forth it is general practice for functions to be very small, as that reduces the need to remember stack state mid-function, which has the effect of greatly encouraging code reuse on a very fine scale (very important!). TL;DR: Forth is great fun. At least, it is at the moment, as I play with it. Almost always comes with an interpreter, usually a very simple one -- this allows the interpreter to be used on very small embedded systems, allowing interactive debugging on them, including hardware debugging, so Forth has a niche there.
Some possible paths:
1. Quick-results: Python/bash --> Java --> C/C++
2. Bottom-up (why does all this work?): Assembly --> Forth/C --> Scheme/Common Lisp/Clojure --> Haskell
3. Software Engineering undergraduate: Python/Java --> C++
4. UNIX-hacker: sh/bash/zsh --> (Python, optional) --> C --> Assembly
5. Little-computers: Arduino C (or is it C++?) --> Forth/Assembly
6. I don't care how real computers work!: Scheme/Haskell --> Haskell/Clojure/Common Lisp
7. I want to build a computer: VHDL/Verilog (I assume this does not apply to you, so I shall not expand)
8. My path: a little Pascal --> Java (university) --> C++ --> Python (work) --> C --> sh/bash/zsh (a smattering earlier, but got serious here) --> Assembly --> Forth --> ? (I have read a lot about the LISPs, and plan to do them next. The question is, Scheme (which one?), Common Lisp (which one?) or Clojure? Help!)
I like paths 2 and 4 the most. A little bit of path 7 can be explored by using the material at nand2tetris.org, which, in itself, is quite educational.
If you want something done in a quick and dirty fashion, and you simply do not care how things work, try Python. A lot of stuff is simply done for you, whether through syntax or libraries. It may be unsatisfying. Comes with an interactive interpreter.
If you want something done in a quick and dirty fashion, but do not want to install too many new programs on your clean UNIX system, try sh/bash/zsh. Embrace the pipe! Also, you may know some already. As a shell, is interactive, of course.
If you do not really care how things work in reality, but do want to actually learn something, try Scheme (perhaps with the SICP). Especially good for those who like (superficial) simplicity. For more industrial/less pure an environment, try Common Lisp or Clojure. You shall learn the value of everything and the cost of nothing. There are also other LISP-type languages out there, do you really want to spend months reading about them all? Most LISPS give the ability to generate more LISP code with other LISP code, called macros -- the saying is code is data. This allows the production of little domain-specific languages (DSLs) to be produced for the problem, though usually the DSLs must (be (composed of (parentheses just as (LISP is)))). Detractors say that LISP stands for Lots of Irritating Superfluous Parentheses (it actually stands for LISt Processing, as it built from a datastructure known as a list). Comes with an interactive interpreter (Read-Eval-Print-Loop, REPL).
If you want to understand how your UNIX system (you have a Pandora or Pyra, at least, right?) works on a lower level, try C. It is very much a mainstream language, with little in the way of exotic/interesting features, but it does allow you the sensation of touching a real computer, with pointers, manual memory management (all of the above are (effectively) garbage collected) and so on. It is also relatively simple, describing a lowest-common-denominator-type virtual computer. Unlike with (most of, the LISPs may be an exception) the other languages above, if you can demonstrate decent knowledge of C, then people will generally assume that you know something about computers as one must learn quite a few other things to make anything serious (make, gcc flags, static vs. dynamic linking etc.). This can be both good and bad. Usually no interactive interpreter, compiler only.
If you really want to understand what you are doing (kudos!), rather than to get the right answer (Tom Lehrer -- search for New Math) then try the Assembly language for your computer. It can be a very educational experience -- I regret that I delayed for so long before I taught it to myself (on x86_64) -- as it strips away most of the abstractions in use elsewhere: there are no built-in loops or if-statements (at least, on x86, none you are expected to use), no variables, no built-in memory manager (C provides malloc and free) and no functions (at least, not in the way you may know). Your assembler will provide you with some registers (very small, fast memory locations -- most operations use at least one register), mnemonics (short names that translate into machine-code instructions) and probably a text-substitution-macro language (which is so useful that you will probably find yourself using it). The mnemonics are the statements (better: orders or imperatives as in imperative programming) given to the machine to direct its behaviour, such as GOTO/JMP, CMP, AND, NOT etc. (you can do pretty much anything given those mnemonics and enough effort -- I believe only those, or fewer, are required for Turing completeness; of course, most computers will give you more instructions for the sake of both speed and ease-of-use). You will also, likely, learn how most programs depend on the operating system (you do want an operating system, don't you? Or would you rather write your own I/O routines?) and learn what a stack (there are probably built-in instructions for working with at least one). Also, remember, one man's constant is another man's variable -- there is very little you cannot do with just assembly language, including unwise things, and what little you cannot do (self-modifying code, for example) can be done with a smattering of machine-code knowledge, in addition. (If someone knows of something that can only be done with pure machine-code -- operating on the numerical values of the instructions to do... what? -- please tell me what it is. I only really have x86 in mind, and perhaps also ARM). Assembly usually has no interactive interpreter, though assembly (the process) is usually quite quick, so it can approach the effects of having one; machine-code DOES have an interpreter: the machine! In theory one could input machine-code directly as hexadecimal numbers using tools such as xxd, but it is usually not practical.
You may have heard of Object-Oriented Programming. For most of the last 30 years it has been the prime programming fad (there are uses for it, but it has been promoted as the solution to everything, inappropriately -- also, there is no agreement on what it really means), fading a bit now though (in favour of functional programming). Two OOP languages you may have heard of are C++ and Java. I recommend leaving C++ for later: it may be good if you are already knowledgeable, but for now it is very large, (almost) as unsafe as C (in terms of crashing and unexpected behaviour from manual memory manipulation, with assembly more so) but more complex (both the language itself and the code, written by other people, that you may want to use, though that may just be my own experience; if you want to learn about lower level programming, C is probably a better place to start). Notwithstanding that it is considered rather uncool at the moment, Java does, mostly (I hear there is NullPointerException), save you from C++'s troubles regarding manual memory management and "safety", though less when it comes to complex code libraries. Java also, supposedly, works on multiple platforms without requiring you to change your code -- write once, run anywhere -- and is (sort of) the standard language for programming on Android (assuming that interests you). There are other OOP languages, but I do not have experience with them. Neither C++ nor Java are designed with interactive interpreters in mind; I have never seen one.
Increasingly popular now are the functional languages (also known as applicative languages, as opposed to concatenative languages), which have a reputation as being academic and mathematical -- few more so than Haskell (named after a mathematician!). I have cheated a bit: most LISPs are designed to make functional programming easy, classically Scheme, and in many imperative or procedural languages it is possible to program in a somewhat functional style -- i.e. without changing many global (or static) variables in most of the functions/procedures, so that they always return the same result with the same inputs. Haskell, though, enforces functional programming. Functional programming is generally regarded as good for reasoning about (and testing) small procedures and, particularly, for concurrent programs, though I doubt that you should worry too much about that, right now. I know little about Haskell, though, so here some other functional languages that I have on my TODO list: ML, Erlang. I do not know about interactivity for Haskell, but I believe it is mainly used with the Glasgow Haskell Compiler, which suggests to me that it is not normally used interactively.
Finally, I am currently playing with a concatenative language called Forth or FORTH(by the way, if something is spelt in ALL-CAPS, that frequently means that the language is old-enough that it existed before systems with lower-case letters were common/universal: some of the oldest systems had only UPPER-CASE -- after all, if the input is a punch-card, why care what the text (you cannot see) looks like? -- and, as a result, both Forth and Common Lisp are case insensitive, while most more modern languages are the opposite). Concatenative means that any program can be placed before or after another program and it will run, even if only to crash. Forth is quite an odd language: while Python and C mostly use prefix notation (for functions, f(a,b,c)) with some infix (x+y) and LISP only prefix ((+ x y)), Forth uses (mostly) postfix (x y +), like some old calculators. As a result, it kind-of reflects how the computer really works, as the computer needs to be given the values before they can be added together; also, postfix allows Forth to be concatenative, with the addition of an implicit stack, so the program x y +, which leaves x+y on the stack, can prepended to the program z *, which accepts a value on the stack and multiplies it by z, to give a new program x y + z *, which leaves z*(x+y) on the stack. This new program can also be prepended or appended to any other valid program to give a valid program, which is important as function calling in Forth is dependent on this behaviour. Forth also has some other fun features: direct return-stack (yes, there are two stacks built-in!) manipulation (allowing tricks like copying the return address of a calling function multiple times so that the latter part of that function will return to itself multiple times B) ), compile-time functions (which are a bit like LISP macros, allowing the creation of DSLs in a similar way, but without the parentheses of LISP; here the saying is data is code) and use of the Forth interpreter in one's own programs (I wrote a simple stack-based virtual machine language translator by just treating every instruction in that language as a Forth function and getting the Forth interpreter to read in the non-Forth source code B) ). As such, using Forth is a bit like coding the inside of a very simple compiler, especially as it also starts at a very low level (like C) with access to memory address, bitwise operations and manual memory management; but since, in Forth, everything is just a function (or word) except for numbers, one can quickly build up one's own high level language inside the program. I should also note that Forth is a very simple language, in terms of implementation (not so much in terms of use, as, eventually, one needs to have some idea of how the compiler works to do the most interesting things), and has been used as the machine-code/assembler for some processors with two built-in stacks (Intel and AMD x86 processors have only one). Also, in Forth it is general practice for functions to be very small, as that reduces the need to remember stack state mid-function, which has the effect of greatly encouraging code reuse on a very fine scale (very important!). TL;DR: Forth is great fun. At least, it is at the moment, as I play with it. Almost always comes with an interpreter, usually a very simple one -- this allows the interpreter to be used on very small embedded systems, allowing interactive debugging on them, including hardware debugging, so Forth has a niche there.
Some possible paths:
1. Quick-results: Python/bash --> Java --> C/C++
2. Bottom-up (why does all this work?): Assembly --> Forth/C --> Scheme/Common Lisp/Clojure --> Haskell
3. Software Engineering undergraduate: Python/Java --> C++
4. UNIX-hacker: sh/bash/zsh --> (Python, optional) --> C --> Assembly
5. Little-computers: Arduino C (or is it C++?) --> Forth/Assembly
6. I don't care how real computers work!: Scheme/Haskell --> Haskell/Clojure/Common Lisp
7. I want to build a computer: VHDL/Verilog (I assume this does not apply to you, so I shall not expand)
8. My path: a little Pascal --> Java (university) --> C++ --> Python (work) --> C --> sh/bash/zsh (a smattering earlier, but got serious here) --> Assembly --> Forth --> ? (I have read a lot about the LISPs, and plan to do them next. The question is, Scheme (which one?), Common Lisp (which one?) or Clojure? Help!)
I like paths 2 and 4 the most. A little bit of path 7 can be explored by using the material at nand2tetris.org, which, in itself, is quite educational.