2011 February

 

 Just when I thought I could bear Ruby

February 24, 2011

After my previous diatribe, I had no option but do some further research, just as a sanity check.

So, along comes Ruby.

(from wikipedia)
According to the Ruby FAQ,[22] “If you like Perl, you will like Ruby and be right at home with its syntax. If you like Smalltalk, you will like Ruby and be right at home with its semantics. If you like Python, you may or may not be put off by the huge difference in design philosophy between Python and Ruby/Perl.”

Sounds good actually!


ashpool:~# apt-get install ruby
ashpool:~# cat >test.rb
#!/usr/bin/ruby
print "hello world\";
^D
ashpool:~# chmod 755 ./test.rb
ashpool:~# ./test.rb
hello world

Hmm, that was actually pleasant! And I used a semicolon successfully!

So, then I do some further reading:

(from wikipedia)
Boolean evaluation of non-boolean data is strict: 0, “” and [] are all evaluated to true. In C, the expression 0 ? 1 : 0 evaluates to 0 (i.e. false). In Ruby, however, it yields 1, as all numbers evaluate to true; only nil and false evaluate to false.

What? You just broke binary! A fundamental computing principle. And also, by the way, the “principle of least astonishment”. Yes, I understand that it’s not supposed to be entirely true according to Matsumoto, but quite frankly I’m astonished.

To soften the blow:

A corollary to this rule is that Ruby methods by convention — for example, regular-expression searches — return numbers, strings, lists, or other non-false values on success, but nil on failure.

That’s fine, I don’t mind your convention, and it makes neat sense, since zero isn’t always a good indication of failure and a nil, tells me something more than zero.

But for fucks sake.

0 is false, 1 is true. And it doesn’t matter what the radix is… I see no reason to mess with that basic computing principle for the sake of a convention involving nil.

Once again — please, Mr. Interpreter, get out of my way, and let me get on with the job of programming. I’ll decide what is true and false, thank you very much. And 1 is true and 0 is false.

So, let’s try something simple. A loop!

cat >./test.rg
#!/usr/bin/ruby
for i in (1..2)
print "hello world\n";
end
ashpool:~# ./test.rb
hello world
hello world

That wasn’t so bad. The loop construct seems a bit simple (or maybe just nonclassical) but can be neatly used to iterate data structures such as one would expect from a dynamically typed language.

But srsly — “end” ?

I feel like I’m back in Pascal hell already. But at least the whitespace wasn’t significant. And “end” at least made things quite clear.

Being able to use a semicolon (without complaint) was also quite nice, and refreshing, for a change. But honestly, would it really have killed the language to allow {} as shortcuts ?

Aaah, wait, I see the problem here…

{} is also syntax for blocks, but only when passed to a method OUTSIDE the arguments parens.
When you invoke methods without parens, ruby looks at where you put the commas to figure out where the arguments end (where the parens would have been, had you typed them)

example:

1.upto(2) { puts 'hello' } # it's a block
1.upto 2 { puts 'hello' } # syntax error, ruby can't figure out where the function args end
1.upto 2, { puts 'hello' } # the comma means "argument", so ruby sees it as a hash - this won't work because puts 'hello' isn't a valid hash

Holy shit.

This is why we have “()” and family in a decent language. Look! Here are the args — “()”. Here is the code block — “{}”. Here’s an array — “[]”. Here’s the end of the statement — “;” Zero ambiguity.

It appears that every time someone invents a new language to address the supposed inadequacies in other languages something goes awry.

I’m still willing to give Ruby a bit more of a go, since there’s seems more sensibility and flexibility than them Python dragons enforce.

For example, my simple little string “acid test”.

ashpool:~# cat >test.rb
#!/usr/bin/ruby
test="123";
test[1]="b";
print test;
^D
ashpool:~# ./test.rb
1b3

I’m going to have a tough time dealing with the stupidity of 0 and 1 though…

Perhaps I’m too much of a traditionalist, and and old fart, but quite frankly, I’d rather be coding Perl, PHP, or C.



 

 The Zen of Python?

February 22, 2011


ashpool:~# python -m this
The Zen of Python, by Tim Peters

Tim Me
Beautiful is better than ugly. Yep, and python is ugly

if x < 0:
...      x = 0
...      print 'Negative changed to zero'
... elif x == 0:
...      print 'Zero'
... elif x == 1:
...      print 'Single'
... else:
...      print 'More'

“elif” ??? What the fuck is wrong with “else if”, or even “elseif” ?

Who is this “elif”? Something from Lord of the Rings mayhap ?

Explicit is better than implicit. Yeah, because everyone likes shit like “ext://sys.stdout” and sys.stdout instead of let’s say “stdout” ?

There is such a thing as too fucking explicit.

Simple is better than complex. Oh, you mean like PEP 3149. Yeah, let’s invent yet another shared object naming convention! and, let’s distinguish between varying interpreters such as “CPython, PyPy, Jython, etc” as well. That sounds terribly simple.
Complex is better than complicated. I missed the point of this one, especially, after investigating the prior.
Flat is better than nested. That’s just because you don’t have curly braces in python and you have to use whitespace to nest 🙄

For structures, nesting with proper delimiters such as {}, and () makes very good sense and is easily readable and “findable” with a decent editor. It appeals to the eye and makes structures easy to delineate and find.

Never mind the fact that shit like ctags loves it. Where’s pytags ?

In a language without these tokens, you’re fucked.

Of course, flat procedural code and “bailing early” is better, but this is true regardless of the language.

Sparse is better than dense. Yup. And the existence or nonexistence of curly braces does not affect this. In fact pep-0008 recommends all the general whitespace rules used by decent programmers, regardless of curlies.
Readability counts. All of the python views on readability viz function names or members are indeed useful, and common practice. Well done.

However let’s compare

while True:
    prelim_one()
    X = prelim_two()
    Y = another_thing(X)
    if X > Y:
        break
    yet_another(X, Y)
    prepare_next(X+Y)
and_another_thing()

to

while (1) {
    prelim_one();
    X = prelim_two();
    Y = another_thing(X);
    if ( X>Y )
        break;
    yet_another(X, Y);
    prepare_next(X+Y);
}
and_another_thing();

A single extra line and judicious use of semicolons, brackets and braces has just separated all code neatly, and delineated everything to editor, and eye as to where they belong.
Sorry, but C syntax wins as far as readability is concerned.

Special cases aren’t special enough to break the rules. Cannot argue with this one. And again, should be standard practice for a decent language.
Although practicality beats purity. Oh wait, Indeed. Let’s consider the very “special” case of dealing with strings and embedded variables.

Which is why most statically typed languages have a printf() style formatting function, and dynamically typed languages offers a plain $ dynamic substitution within strings, or printf() style functionality. Let’s let the programmer decide on his choice of poison…

So, Python would like you address a string as an object using string methods and all of the syntactical “special cases” that has been added on top of strings.

And as a result, something basic, something that nearly every language can do, such as addressing a string as an array Python does not allow!

Python doesn’t allow common string manipulation such as word[0] = ‘x’, as can be done in C, Perl or PHP.

Python does not allow the common mechanism of variable substitution in a strings such as $var=”$bar”;

So Python saves you on the curlies, and braces and fucks you on strings. The most primitive fucking programming construct known to man!

Give me more practicality please.

Errors should never pass silently. No. Errors should never cause an exception, due to for example an integer that could not be parsed.

Please don’t toss a fucking exception thank you very much, because I, the programmer have probably got more of an idea about what I care about than you do thank you very much Mr Interpreter.

You get along with running as much of the code as you possibly can and I’ll get along with the business of deciding what’s wrong and right. Don’t get in my fucking way. I’ll throw the exceptions when I want to, and of the kind I want to, and when I feel like catching them. System level stuff should never get in my way because otherwise we end up in Java-land.

And sprinkling the parsing of shit like integers and real numbers with try()’s and finally() isn’t useful thanks, I thought we were trying do more with less. I’ll check if I like the integer or not.

Unless explicitly silenced. Yeah, I can sort of manage try() and catch() by myself here. And my view is “shut the fuck up until spoken to”.
In the face of ambiguity, refuse the temptation to guess. Agreed. A rule is a rule, stick with it.
There should be one– and preferably only one –obvious way to do it. No thanks. Give me options, if I’m a printf() fanatic let me use it. If I’m a string substitution junkie let met do it. In fact, allow me to do it as many ways as I like.

As a programmer I kind of like not being in a fucking sandbox. If I did, I’d be coding Java.

EXCEPT if it’s basic syntax and language constructs.

Although that way may not be obvious at first unless you’re Dutch. Guess that’s why we cannot classic ternary operators such as the classic “result = (a > b) ? x : y;”

So, because of dutch we had to get “{True: x, False: y}[a > b]”

Yeah, that was the really fucking obvious way to do it 🙄 It’s nice to be different and all, but not to the point of fucking stupidity. Obvious stupidity.

Now is better than never. Yeah, if Python had curly braces and decent string manipulation I’d be using it now.
Although never is often better than *right* now. Yup, I’ll never use Python *right* now.
If the implementation is hard to explain, it’s a bad idea. Yeah, still trying to figure out how to dynamically change strings like they’re an array.

“Unlike a C string, Python strings cannot be changed. Assigning to an indexed position in the string results in an error:”

>>> word[0] = 'x'
Traceback (most recent call last):
  File "", line 1, in ?

However, creating a new string with the combined content is easy and efficient:

>>> 'x' + word[1:]
'xelpA'
>>> 'Splat' + word[4]
'SplatA'

Wait, what the fuck? Easy? Efficient? Sorry I missed that fucking explanation. Or maybe, it was just too hard to explain away that problem?

If the implementation is easy to explain, it may be a good idea. Sorry, but I’m still fucked on the previous one. Strings. Basic. Easy. Obvious?
Namespaces are one honking great idea — let’s do more of those!
>>>import honk.great.idea.and.however.the.fuck.deeply.you.want.to.nest.it.
>>>   print "hello"
Traceback (most recent call last):
  File "", line 1, in 
>>>> Exception "print" does not do what you think it does. Unless you're Dutch.

Namespaces are dangerous. Trust me, I once gave a TCL project to a bunch of girls that figured out what namespaces was. They thought it was all “neat” and “tidy”. I thought that I couldn’t read identifiers nested twelve levels deep. Namespaces pollute the basics of language.

Don’t do it. Create an object instance. Tada. Instant namespace.

Namespaces are useful, but for fucks sakes, don’t “honk it as a great idea”.

Python is like the bastard child of Java. I will never write serious code in it. Comments, corrections and diatribes welcome.

Footnote: If python had curlies, I’d probably be it’s biggest fan. Aside from the string fucked-ness.