Wednesday, November 30, 2005

Language Inefficiencies

   I spent some time yesterday trying to learn Perl.  I'd looked at it some a few years back but never had a use for it.  I now have a need to write a tool for our build environment and so, based on what is available, I am required to use Perl.  The first thing I noticed about Perl is that it is very powerful.  The second thing I noticed is that it is really ugly.  There are too many ways to accomplish the same result.  Many new languages suffer from this same fate, only to a lesser extent.  What I want to talk about is keeping a language small and compact.  If there is a way to accomplish a task, please don't invent another one with slightly different syntax just to save a few keystrokes.  C++ is pretty good about this.  Other than the multiple ways to cast, there is not much redundancy in the language.  The namespace is fairly unencumbered by keywords.  Modern languages like C#, Perl, Ruby, and Python, however, seem to burn namespace like it is going out of style.  They invent new keywords and operators that add nothing to the language.  One of my favorite examples comes from C#.  The operator 'as' strikes me as wholly unnecessary.  The following code accomplishes the same result:


CFoo cf = myObj as CFoo;


- and -


CFoo cf;


if (myObj is CFoo) {


   cf = (CFoo) myObj;


}


In both cases, we are checking if myObj is of type CFoo and if so, setting the variable cf to point to it.  Why the need for as?  What does it add to the language? 


Perl is much, much worse.  A glaring example is the 'unless' operator.  Instead of typing if(!foo), you can type unless(foo).  Much better, right?  No.  There are many other redundancies.  && and 'and' do the same thing.  I can choose whether or not to use parentheses around my subroutine calls.  You can put if before or after the code you want executed.  The list could go on almost indefinitely.  The worst part about Perl is that the advocates are proud of all of this redundancy.  Even newer, more compact languages such as Ruby have redundant operators.


Here is my rule for adding a new operator or keyword to a language:  Does this operator give me the ability to do something I couldn't do before?  If yes, consider adding it.  If no, reject it. 


I'm not here to bash Perl or any of the other languages.  They all have their place and are powerful.  They also all have a large following.  However, it seems like they could be even better if they were a bit more careful.  Making something already possible merely syntactically easier has the effect of making the language more complex.  While making something a bit simpler to express, you have made the language harder to learn and to retain in memory.  If only language authors would think a second time before making an addition to their language.  Having hundreds of keywords and multiple ways to do everything makes the language harder, not easier to use.

5 comments:

  1. We can also get rid of other redundant language elements: "using", "foreach", all catches other than "catch (Exception*)", operator overloading, constructors, the "switch" statement, the "for" loop, the "do {} while" loop, the "string", "int" and "bool" keywords. Even "if" is redundant -- "if (a) b" is the same as "a && b". Then we could require that "*" be short-circuiting, then we can get rid of "&&" too and just use "*", so "if (a) b" is now just "a*b".


    [BTW, "as" is faster than "is+cast" since "is+cast" does the type check twice. If anything, it is the cast operator that is unnecessary - it's the same as "as" + throw.]

    ReplyDelete
  2. It's as oldnewthing says above. the CIL for the second statement is longer than using the "as" keyword.


    The second example is actually expanded into:


    if((myObj as CFoo) != null)

    {

    cf = (CFoo)myObj;

    }


    The lesser expensive alternative (and the recommended practice) is:


    cf = myObj as CFoo;

    if(cf != null)

    {

    ... do something

    }

    ReplyDelete
  3. Would we have moved past assembly code if your rules had been rigidly applied in the past?

    I can see your point in general about redundancy, but in some cases surely it can be worthwhile. Isn't it possible and worthwhile to promote a desirable programming practice by providing a succinct, yet redundant, language construct or keyword?

    ReplyDelete
  4. Steve, I agree with you - Perl has too many ways to do the same thing. This makes reading someone else's Perl code very difficult. The length of the learning curve on programming languages is really critical these days, because a typical developer may have to program in one of 'n' languages. For example, I regularly work in these languages: C, C++, C#, VB, Perl, HTML, XML, XSD, XSL, VHDL, and probably some others I've forgotten. So it is important for me to be able to quickly read existing code and code examples and apply concepts to my problem being solved.


    Expressiveness in a programming language is a good thing, but taken to the extreme, it can be a problem, as is the case with Perl. I think sometimes people confuse expressiveness with powerful, but they really are two different dimensions.

    ReplyDelete
  5. Okay. Obviously you can take my "rule" too far. Taking it to the extreme would leave us with assembly. That isn't what I intended. There is a place for new operators that make things substantially easier to express. A good example is the class in C++. You can get pretty close in C but it is a real pain. Adding class functionality extends the language to allow you to do things too painful to do before.


    So, let's modify the rule:

    Does this operator give me the ability to do something I couldn't do before or does it make expressing some functionality substantially easier? If yes, consider adding it. If no, reject it.


    This would allow for most of what OldNewThing says would be disallowed. Some things like do...while still probably fail but that might be the right thing.

    ReplyDelete