Also, Python is a descendant of ABC. ABC was designed to be a simple, easy to use and learn language. The indendation based grouping comes derives from it.
Directly derives, since ABC probably derived it from Miranda, and Miranda from ISWIM.
One should not overlook the fact lanugage features do have readability implications. For example, keyword assoication for parameters helps readability but must be done on the language level.
In Scheme, keyword parameters can be implemented with macros.
Whether or not a feature can be implemented within a language, rather than added as a primitive, depends on how expressive the language is. When a language is expressive enough to implement what must be primitive in another language, it is almost always better to do so, because 1) it makes the specification smaller, 2) it lets you manipulate that feature use the other parts of the language, 3) if you have a module system you can decide whether or not to import it. The arguments against doing so are usually that, when something is primitive, you can sometimes implement it more efficiently, and provide better error-checking.
You can program without generators, but then you may have to roll your own when you need
this kind of functionality. Since this is far from trivial you are going to end up with quite a lot
of complicated code
That's what libraries, modules and other abstraction features are for!
This amy also be seen as an argument about Scheme readability: sure continuations are
cool, but code that uses them to achieve what Icon does with the keyword suspend is going
to be harder to read. This is exactly the same argument favoring for loops to goto
statements.
That is no argument at all. Just bind it to a name. suspend gets implemented as a procedure. In Scheme, the primitive would be implemented as a syntactic keyword, so they wouldn't even look any different. And as a procedure you could manipulate within the language, e.g., you could pass it to another procedure, which is impossible with a primitive.
However we have to
take into the principle that Stroustrup says guided the evolution of C++: you shouldn't have
to pay for what you don't use.
Wasn't he referring to efficiency? I agree with this on both counts, though.
So whenever people suggest adding a do...while
loop syntax to the language the standard response is that people should use the following
idiom instead.
while 1: break
This is a bit different from the situation where you implement control structures with continuations. In fact, it's a bit funny because there are two instances of this phenomenon here. First, you implemented do-while with while-do. Second, you implemented booleans with integers! :) I'll get back to that later; first let me explain why I think this kind of unification is different from the continuation case.
First, using continuations, I can implement for-do loops, while-do loops and do-while loops, generators, non-pre-emptive multitasking and, in the presence of dynamic binding, exceptions and resumptions, and, I think, in the presence of asynchronous signals, even pre-emptive multitasking, and many other wondrous things. So there is a very high value-to-cost ratio for continuations. But if all you can implement with while-do is do-while, then that's not very impressive.
Second, in the case of continuations, I can package up all these features into a set of procedures, which can then be used in a fashion which is more-or-less indistinguishable from the versions implemented as primitives. For example, if I implement exceptions, I get procedures handle (or try, if you will) and raise, and the usage is not much different from having them as keywords. In other words, since they are first-class citizens, I can bundle them up and assign them names. But while-do is not a first-class citizen, it is a piece of syntax, and I can never name it "do-while".
Third, there is a small possibility for inefficiency. A really naive optimizer might not recognize the obvious optimization. Or, suppose you have a program generator which for some strange reason (and, believe me, it can happen) replaces 1 with some arithmetic expression which evaluates to 1. Then the optimizer will definitely miss the optimization.
Now let's get back to your representation of the booleans as the integers. This is also a sort of language unification. It makes your language more orthogonal, right? But why stop there? After all, you can implement strings with integers, so we don't need those either. And characters too. And, wait a minute, we can actually implement integers with booleans by encoding them in binary! Taken to an extreme, this approach will suggest you go back to programming in machine code!
Obviously you wouldn't go that far, but it raises the question of where to draw the line. If you use base your decision on just some fuzzy subjective opinion, then someone is bound to disagree with you and call you to task, and probably you will waffle after thinking about it for a while too. Ask yourself: why am I identifying true with 1 and not 6712319? It's an arbitrary choice, and there's going to be somebody in this world for whom a different representation is more convenient. The number of possible embeddings increases dramatically as the datatypes involved increase in complexity, so the arbitrariness of the choice does too.
So (quite apart from the fact that you are giving your programmer more ways to trip himself up), while this sort of unification makes sense for simple types like bools and ints, it doesn't scale well.
Also, most languages will have user-defined datatypes which it wants to treat as well as primitives, so this little infelicity for the sake of convenience will look like a wart. For example, automatic coercions between ints and reals are nice at first, but if the user defines his own numerical system, then it will always be inferior to the built-ins for lack of coercions (except in C++, but the coercions there cause endless problems too).
OTOH, if your approach is to distinguish booleans and integers, but also provide explicit coercing functions between them, you are not making an arbitrary choice, because you can have as many coercions as you want. Furthermore, you can give each representation of bools-as-ints a name. Sounds lame for such a simple case, but consider representing complex numbers as real tuples: there are polar and cartesian representations.
Well, enough rambling... I was going to talk about type safety, but I'm all rambled out.
|