What's the best language to learn programming with?

In some languages, a function is a data type. You can create functions (even at run-time) and store the compiled (or semi-compiled) function in a variable of data type “function” (although there might be another name for it, like “code block”), and then manipulate it like a variable, according to the defined operations that are provided for dealing with data of type “function”. Usually, this simply means that you can pass it as a parameter to another function.

I don’t think there’s much point to picking a starter language based on ease. There’s not much actual general programming skill that will carry over into other languages. Most of the learning curve is just learning the language itself. I would focus on picking the best language for what your application is. Are you making web apps, iPhone apps, PC programs, custom science software, cross platform games?

Can anyone chime in with some suggestions for what language is best/most common for various different purposes/platforms?

Yes, but in that case the function is the code, not the result. There may not be any practical reason to do so, but there’s no reason you couldn’t add a function to a number and get some predetermined result. It may be reasonable to disallow that through code, but unnecessary to make it a basic requirement of the language.

A scripting language like python or perl is used probably the most widely. They can be used for simple shell scripts or for more advanced programming. Even if the product is being programmed in some other language (C, Java, web, etc), it’s not unusual to have perl/python somehow also used either in the configuration of the product or to implement some part of the function.

blink blink

Please, TriPolar, tell me you’re pulling my leg. You’re the one who doesn’t seem to understand types. Types were created as a mathematical structure to facilitate the analysis of computation. The fact that words like “char” and “int” were added to early portable assemblers and compilers as aliases for the generation of code to deal with differently sized integers does not at all mean that those are what define “types.” The types that you malign, that are used in modern high-level languages, have more to do with the computer science concept than with the early C compiler facility.

I think you need to check yourself. “Function mapping Num to Num” is a type just as much as “int” is. “List of FooBar pointers” is a type. “List of list of functions mapping FooBar to list of BazWibble” is also a type. If you think these things are not types, then I do not know what to tell you other than that you are incorrect. Look at the Wikipedia articles on data types or type systems, or pick up a good textbook on the subject.

:dubious: I use Haskell at work, as well as high-level languages like Python which have those “function” and “list” types that you think don’t exist. Seems close enough to reality to me.

Again, this is wrong, wrong, wrong. Types have nothing whatever to do with internal representation; they are about the semantic rules governing the applicability of operators and values. I think you’re the one who’s being blinded by custom here.

In C, “int” and “long” and so on are barely even types – they’re mnemonics for assembly instructions. There are few rules governing the way they can be combined and used; the language is rife with slicing and implicit conversions and unrestricted explicit conversions, and that’s why C is said to be weakly typed. A language with an actual type system introduces rules that generate abstractions over the data, rules that govern what things can be members of types and how members of different types are allowed to combine. Now it’s not about the internal representation anymore; that’s left to the compiler. The type system enforces the abstract rules.

But then, what do I know, I only have a degree in computer science. :rolleyes:

How is it quicker to know and easier to spot this error in a dynamically-typed system like Python’s than in a statically-typed system where the compiler(/interpreter) makes sure at compile-time that you pass in a file object to get_size, explicitly pointing out at compile-time the originating source of the violation of protocol in cases where you screw up and pass in a string instead (rather than merely pointing out at run-time some random point in the code way down the line where the violation of protocol finally become intolerable, possibly far removed from the actual error)?

Yes, one wants to develop the practice of passing to functions inputs of the appropriate type; this is so basic as to go without saying. And a statically typed language will no less force you to do so than a dynamically typed language; in fact, to the extent that it doesn’t let you remain ignorant of your type mismatch mistakes, it does even more in this regard.

Put another way: To as great an extent as possible, nothing SHOULD be left implicit and unchecked; that’s just an accident waiting to happen. If there is a protocol which is key to the proper use of some piece of code, then, by all means, that protocol should be made an explicit constraint in the code, in the appropriate sense. Having to juggle information exclusively in your head is no advantage over keeping track of that information in code (which is then read and understood in your head…).

To TriPolar: think of types not as markers of a particular data representation in memory, but as markers for semantic categories.

For one particular example: it makes sense to multiply matrices whose dimensions match up in a suitable way. It doesn’t make sense to multiply matrices whose dimensions don’t match appropriately. This has nothing to do with how those matrices are represented in memory. It’s an intrinsic constraint on the applicability of the matrix multiplication function; regardless of low-level details of digital encoding, if one were to attempt to multiply matrices whose dimensions didn’t match up, this would be considered nonsense akin to a grammatical error, and clear evidence that one had lost track of what one was doing. This is the sort of thing which a type system is meant to track. A type system needn’t have anything to do with how bits are arranged in electronic memory. A type system needn’t even have anything to do with a digital computer.

For another familiar example, when physicists use “dimensional analysis” to make sure their equations are reasonable, they are making use of a type system. The rules “You can multiply a 2 x 3 matrix by a 3 x 4 matrix to get a 2 x 4 matrix, but you can’t sensibly multiply a 2 x 3 matrix by a 4 x 5 matrix” and “You can multiply a force by a distance to get a quantity of work, but you can’t sensibly ask whether a force and a distance are equal” are type system rules that have nothing to do with how data is represented in digital memory.

Had? Feh. I HAVE a toggle-switch EPROM programmer.
It’s right here by my slide rule. I’d post pics, but, well, you know.

I agree with this. Tripolar, would you mind if high level languages exposed the “types” integer, rational, real, and complex? Sure, you can implicitly invoke an isomorphism between them, but there are times where you don’t want your domain switching on you (there have certainly been times where I wanted integer division and I wanted to constrain my domain to ints; and it wasn’t even a particularly low level program). What about optional typing? Say it would be treated as an auto-convert variable, but one had the option to say “x has the domain <real>” and it would be bound to the domain “real numbers” unless an explicit conversion/isomorphism was invoked?

I’ve had a doozy with Python when working with a bad library framework I had to work in for a class. Specifically, the tuple format that a GetSuccessors function returned wasn’t well documented, and since Python allows you to compare arbitrary types, it wouldn’t crash or warn me when I was doing something weird.

The format ended up being something like ((current state tuple), [possible actions list], cost to get to this state from last state), but I only figured that out after a lot of debugging. But the thing was, I was programming pathfinding algorithms, so there’s steps like

if current state == goal {
return path and cost
}

Or

if current state in closed set {
continue
}

But if you extracted the state incorrectly, instead of throwing an error they’d simply fail to compare correctly – giving false positives and negatives all over the place. Thus you’d get really bizarre paths, no paths, etc. For the longest time I was trying to find the bugs in my heuristics and algorithms – because for some problems it was working correctly because the erroneous way I was extracting states worked correctly for some state representations but not others. So that pointed to an algorithmic or heuristic problem, to me.

It was only after tons of torture testing, print statements, and mathematically proving heuristics admissible that I found out that, no, both my algorithm and heuristic were solid and I wasn’t just blindly skipping over a bug. It’s just that the state representation documentation was unclear and my extraction was silently returning the incorrect thing for some problems. Something that would have at least thrown an error with a language anal about typing.

That was, in my opinion, certainly comparable to some of the “why is valgrind yelling at me now?” bugs I’ve had.

(Obviously since that program was in Python there were no curly braces, that’s not the point :p)

Stealth Potato, I want to apologize. I was in a pissy mood yesterday, fighting a sinus headache, looking for a distraction, and I ended up in a stupid argument, which was my fault because I wasn’t very clear.

I think you and I agree, I was only talking about primitive data types initially. I don’t think they should be tied to internal data representation limits at all. Of course higher level typing is needed for a variety of purposes, and there are many ways to handle that within a language.

Please accept the apology of a grumpy old bastard.

Interesting conversation. To the OP I guess I’d start with Java. It’s certainly not perfect but it introduces the student to most common programming problems without being overly difficult. I concur that C is a great second language to learn.

Engineering circles are beginning to realize the dangers of a loosely typed language for large applications. The most recently developed language that is gaining traction is Scala and it is statically typed.

Obviously it’s easier in a language with a compiler as the compiler will find these type mismatches. But the compiler won’t find problems with a function takes two strings X,Y but the caller passes in Y,X. So the programmer always has to be aware of the inputs and outputs even when the compiler helps out. The newbie programmer develops that skill. If they make an error, usually the problem will be very close to the source of the bug and it’s more straightforward to understand.

The problem with C is the memory errors. If there wasn’t the memory issue, then C would be fine. It’s just that memory bugs are so easy to introduce and so hard to debug. I’ve been a professional programmer since '87 and memory bugs are still difficult for me to track down and may take several days.

I think if your beginner programming class was made up of engineers, then C, with it’s memory problems, would be fine. If your audience is made up of problem solvers, then they would likely be able to work through memory bugs. But if this is a general programming class that people take as an elective, it would be better to use a language which is easier to debug problems. One or two memory bugs could cause people to quit in frustration before giving it a chance.

This’ll work:

IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO-WORLD.
PROCEDURE DIVISION.
DISPLAY ‘Hello, world’.
STOP RUN.

No worries, TriPolar. I apologize for getting a bit warm and sarcastic myself; I probably could have done better to clear things up too. :o

To that extent I see what you mean, and having different number types for different representations is definitely a leaky abstraction that creates a lot of headaches. it might be nice, for example, to have a language where the only number type is an infinite-precision decimal. Slow as heck, naturally, but there would be no more need to worry about things like integer overflow or floating-point denormals and the billion ways you can disastrously lose precision.

I’m more leery of implicit conversion between numbers and things that are not numbers, however, e.g., booleans and strings. “123” is not the same thing as 123, not because the underlying binary representation is different, but for the same reason that water is different from the letters that make up the word “water”: a sort of use-mention distinction. That, and string and integer comparisons overlap in unpleasant ways.

Specifically, there is the fact that string comparison is lexicographic, whereas number comparison is based on numeric value. That is, strings that contain different characters are considered unequal, but there are many different representations of numbers that may refer to the same value. E.g., 15 == 0x0F == 017 == 1.5e1 == 1500e-2. Should 15 == “0x0F” evaluate to TRUE, then? Or, more perversely, should “three” be the same as 3?

You probably also want equality to be transitive. In that case, if 15 == “15”, and 15 == “0x0F”, it stands to reason that “15” == “0x0F”. This is necessary to keep that “consistency” of erasing primitive type distinctions, but it has suddenly made string comparison next to useless, since now strings with entirely different contents can compare equal. Just try writing a password hash checker or a username uniqueness constraint in such a language!

This kind of thing is more or less what PHP tried to do, and while PHP’s handling of it was characteristically disastrous, I’m not sure it’s possible to do it consistently and remain useful.

The problem with strings is that there’s simply too many ways to define equality. Character-by-character equality is the simplest, but then there’s case-insensitive equality, and even worse: semantic equality. “0x02” == “2” seems like a good idea, but it really opens up a terrifying rabbit hole. Are we now going to argue if “cat” == “gato” == “猫”? Maybe “cat” == file.Open(“pic.jpg”) if some computer vision backend program can determine that the picture is primarily a picture of a cat or the word “cat”?

Obviously these are deliberately ridiculous assertions, but I feel like when you assert that “0x02” == 2 (the integer) that you’re taking steps in the direction of expecting the language to do bizarre things for you that it has no business trying to do. It seems better to me to have strict character-by-character equality for string ==*, and have a library functions for everything else. It leads to more sane semantics that way.

  • Yes, I said it, I believe that == on strings should compare the characters, not the pointers. Because == on string pointers is almost useless (see: Java).

I have worked in languages where strings and number were the same type. You wouldn’t have “0x02” == 2, it would be 0x02 == 2. Contextual syntax would distinquish them. The reason it has limited utility would be the questions of format for numbers. “02” could be equal to 2, but then you need further rules to deal with " 2". And of course you need different operators to perform string comparisons because they aren’t numeric comparisons, and multiple operators where there are different collating orders, and the old question of does “s” follow “T”. However, except for the most primitive expressions these things should be buried within an class definition. I would be perfectly happy if a primitive were an XML document (assuming some of the imperfections of XML were worked out), and the primitive is actually an object. It could work whether or not it included a specific class definition or schema. The point however is not to bog the beginner down in the representation details. Beginners need to learn about control structure and basic expression syntax without getting enmired in these arcane details. And the ideal language should be forgiving with undeclared data. In the end a well defined application will define it’s own data constraints. That is one of the advantages of the object model.

To clear up another point, aggregates themselves should always have both references to their members and an aggregate as whole, where the whole is considered scalar. As an alternative, there could be no scalars and instead only aggregates of a single item. My thoughts on this arise from the concept of a high level language which isolates itself from the physical machine, but never-the-less is creating a virtual machine, and that machine should have the simplest, most consistent definition until the application applies constraints. That doesn’t have to limit itself to the most general purpose case though, specialized languages even though complex can be very efficient when developing code for specialized applications.

ETA: "* Yes, I said it, I believe that == on strings should compare the characters, not the pointers. Because == on string pointers is almost useless (see: Java). "
Absolutely agree.