• Welcome to Computer Association of SIUE - Forums.
 

Bad Programming Language Features

Started by Jerry, 2006-05-17T16:01:46-05:00 (Wednesday)

Previous topic - Next topic

Jerry

Niklaus Wirth wrote an interesting article in the Jan. 2006 Computer titled "Good Ideas, through the Looking Glass". In it he discusses ideas in computing that originally looked great but turned out to be "less brillant than they first appeared." He covers a lot of areas but I found his retrospective on Programming Languages the most intersting. If you don't know of Niklaus Wirth he is the cheif designer of Pascal as well as many other languages.

He notes that a programming language is "characterized not so much by what it lets us program as by what it keeps us from expressing." In other words a programming language should prevent us from making mistakes. He goes on to list what he thinks are some of the worst features put into programming language.

Here's ones that get my vote:

1. The equivalent comparison in C/C++ being two equals signs "=="  This has launched countless hours of debugging.

2. The C/C++ increment/decrement operators "++" and "--". These create all sorts of ambiguity if used by evil programmers such as: x+++++y

3. The use of semi-colon or other delimitrs to end a statement. Instead I would recommend a specific statement such as "END". Semi-colons in C/C++ cause all sorts of problems for novice programmers - when to use them, when not. But also cause problems for experience programmers.

4. Unrestricted goto's

5. Fortran's arithmetic goto

6. Fortran's for loop syntax: 1,20 means from 1 to 20. The standard however allowed for compilers to accept 1.20 which means from 1.2 to 1.2 :-?

So, what is your favorite poor choice of programming language features?
"Make a Little Bird House in Your Soul" - TMBG...

DaleDoe

The one I hate the most is the "==" in the C languages.  Not only is it too easy for novice programmers to forget the second "=", but experienced programmers get mixed up when they frequently switch from languages that use a "==" assignment operator and ones that use a "=" assignment operator (like VB).:ranting

I tried the x+++++y in Visual Studio 6 and it wouldn't compile because it read it as ((x++)++)+y and it couldn't figure out how to increment "x++".  But x+++y did compile.

I disagree with semicolon delimiters being bad.  I like them for their speed.  See how fast you can type "END" compared to ";"
"If Tyranny and Oppression come to this land, it will be in the guise of fighting a foreign enemy." -James Madison

Justin Camerer

One thing that has ALWAYS baffled me is class and struct definitions in C++.

Functions in C/C++ go a little like so..

int addNums(int num1, int num2)
{
  return(num1+num2);
}


While classes in C++, as well as structs go like sooo...

class Number
{
  private:
    int num1;
    int num2;
 
  public:
    int addNums(int num1, int num2)
    {
      return(num1+num2);
    }
};


Notice the FREAKING SEMICOLON at the end of the class definition...

WTF is this all about?!


I haven't been using C++ lately, so I can't think of anything else I really hate off the top of my head, but I'll try.
Justin Camerer
Do yo' chain hang low?

Justin Camerer

QuoteDaleDoe wrote:
I disagree with semicolon delimiters being bad.  I like them for their speed.  See how fast you can type "END" compared to ";"

I don't think he meant using end at the end of every line like ;. Instead, using end at the end of a block of code, such as a function or class.

I have been programming in Ruby for the past 4 months or so, and it uses end instead of ; and I like it a lot.

Also, I'm not sure how many people are familiar with Ruby, but one of the first things I (re)made in it when I started learning it was a Maze program. I thought I would share one function used in this program that I thought was pretty cool because of one, maybe two, lines of code.

(values is an array of objects)

1.  def shuffle(values)
2.    values.length.times do |i|
3.      r = rand(values.length)
4.      values[i], values[r] = values[r], values[i]
5.    end
6.    
7.    values
8.  end


You can loop through these with Ruby by saying values.length.times, which i think is cool. Also, on line 4, I am doing a double assignment with one line of code. C++ would take at least 3, right? Yeah...

Ruby is cool stuff....
Justin Camerer
Do yo' chain hang low?

William Grim

In just C/C++ I could do line 4 in a single line if I already had a small tuple struct defined.


typedef struct
tuple {
  void *x, *y;
} tuple_t;

int main()
{
  // assume tuple_t t1, t2 are already defined and
  // have their memory set correctly.

  t1 = t2; // done :)
  return 0;
}


Granted, that took a few more lines of code to setup the struct and doesn't make copies of Immutable Data (i.e. basic data types), but as the program gets larger, that overhead shrinks.  It will, however, create references to Mutable Data the same way Ruby/Python does (i.e. data structures, arrays, dictionaries, etc.).

Now, I'm not arguing that Ruby couldn't be used to code faster (I'm sure it could just like Python can), but it should be noted that even "arcane languages" offer a lot of flexibility (especially the C99 standard).

I was mainly being facetious with my example.
William Grim
IT Associate, Morgan Stanley

William Grim

The reason you need the semicolon at the end of structs/classes in C++ is to be compatible with C (as if it's compatible these days, HA!).

In C, you often typedef a struct so you don't have to type "struct blah" to declare a structure instance.  This is how you do it without a typedef:


struct
data
{
  int x, y, z;
};

struct data d1, d2; // two instances of the data structure


However, to be able to typedef a struct in C, you need to have a semicolon somewhere.  I'll bet it's way easier to create a grammar that works for both typedef structs and non-typedef structs if both versions have semicolons at the end.

Here is how you do it with a typedef:


typedef struct
data
{
  int x, y, z;
} data_t;

data_t d1, d2; // two instances of the data structure
William Grim
IT Associate, Morgan Stanley

William Grim

I disagree with Nicklaus Wirth's idea about the unrestricted use of the goto statement.  I do agree it is a feature that has caused some horrific code, but it really does make life easier when programming code that must run as optimal as possible or where a standard "destructor" is needed for languages that don't support RAII, such as C, assembly, and various others I'm sure.

This would be an example of it's use:


void f()
{
  data_t *s1=NULL, *s2=NULL;

  if (!data_alloc(&s1) || -1==data_init(s1)) {
    goto err_exit;
  }
  if (!data_alloc(&s2) || -1==data_init(s2)) {
    // Imagine if HOLY CRAP SOMETHING BAD HAPPENED
    // AND WE GOT TO THIS STATEMENT!!!!
    goto err_exit;
  }

  // .... more code here ....

err_exit:
  if (s1) {
    data_free(s1);
  }
  if (s2) {
    data_free(s2);
  }
}


What are you going to do?  Put the proper free code between all the if-else statements?  That's just retarded.  That'd be like copy-pasting functions all over the place.

In C89 and assembly, the above solution would be necessary.  However, I would like to note that there is one other thing that could be done in C99 so you don't have to use goto for "destructors", and it's what I'd recommend doing.  It's called function nesting (works in C but not C++... told you they weren't even remotely close to compatible):


void f()
{
  data_t *s1=NULL, *s2=NULL;

  void f_exit()
  {
    if (s1) {
      data_free(s1);
    }
    if (s2) {
      data_free(s2);
    }
  }

  if (!data_alloc(&s1) || -1==data_init(s1)) {
    f_exit();
    return;
  }
  if (!data_alloc(&s2) || -1==data_init(s2)) {
    // Imagine if HOLY CRAP SOMETHING BAD HAPPENED
    // AND WE GOT TO THIS STATEMENT!!!!
    f_exit();
    return;
  }

  // .... more code here ....
}


My point about optimization still stands.  Not that you should optimize prematurely, but the thing about compiler optimizations is that they're hardly optimal... they're more like guesses that usually work pretty well but sometimes are completely wrong.  Those times when the compiler is wrong is when goto could potentially be handy.
William Grim
IT Associate, Morgan Stanley

William Grim

I agree with Nicklaus Wirth's notion of "==" being bad; however, I do know of one tip that can reconcile a lot of problems people have with it.  Put the constant on the left-hand-side of the equality test.  If a single "=" is used on accident, the compiler will catch the error:


if (20 = x) // caught
  ...


const unsigned C = 3.14;
if (C = 3) // caught
  ...

if (C = x) // caught
  ...


I really wish people would do that more; it makes me feel more confident about their code.
William Grim
IT Associate, Morgan Stanley

Justin Camerer

Justin Camerer
Do yo' chain hang low?

Jerry

First, to be fair to Wirth, I threw in a couple of my own pet peeves with his.

IAMJWC is correct about the use of END - not every statement, just at the end of blocks of code.

As for WGRIM's support of unrestricted gotos - keep in mind that all language constructs are design decisions. Wirth isn't saying that there might not be some good uses for a particular construct, what he is saying is they could be designed better.

With the exception of sequential statements, all control statements are a form of restricted goto - loops, conditionals, functional calls. So what he is saying is to design construts that capture the useful behavior of how you would use an unrestricted goto and not allow for the bad programming behaviors. The classic example used in favor of unrestricted gotos is exiting from inside a set of deeply nested loops. Well, ok, if you had to put all the conditions in each loop to exit each one it would not be optimal - so introduce something like BREAK and EXIT statements that breaks control out of a code block and puts control in a predictable (structured) place.


 
"Make a Little Bird House in Your Soul" - TMBG...

blacklee

I can't wait to take 330, always wanted to figure out - why do we need so many different programming languages? After all, all the code is translated in 0's and 1's, right?
I'm sure there is a reason, but in this case, if code is translated in 0's and 1's, why it can't be translated back into any language? Why programmers have to learn different languages and different syntax? It would be nice if there was just one, universal language.
I know, this is probably a very stupid post, sorry.  :oops:

Jerry

Quoteblacklee wrote:
I know, this is probably a very stupid post, sorry.  :oops:

Actually this is an excellent question - why so many different programming languages?

Programming languages fall into different paradigms: procedural programming (eg., C), functional programming (eg., Lisp), logic programming (eg., Prolog), object oriented programming (eg., Java). Each different programming paradigm provides (forces) a different problems solving approach. For example, procedural programming (my personal favorite) focuses on data storage and processes (i.e., data structures and global program state); object oriented programming focuses on partitioning the program state into individual objects with seperate state and procedures (i.e., methods).

Further, within each programming paradigm different languages take different design stances to provide programmers with different abstractions for representing a problem. For example, Snobol and C are both procedural languages, but Snobol is designed specifically for string processing applications. Check out "Hello World" in Snobol.
"Make a Little Bird House in Your Soul" - TMBG...

blacklee

Thanks Jerry.
I've heard of Cobol, but never heard of Snobol, it's a funny name.  :-)
Speaking of my "favorite poor choice of programming language features" - I could never understand why object-oriented approach has to be forced in Java. Why "Hello World" in Java requires a use of a class, instead of being just as easy as in Snobol?

I still like the idea of universal programming language, but I'm pretty sure this is because of my lack of understanding and lack of technical knowledge. It sounds good in theory, but probably is not applicable in real world. Even if it was realistic, I don't expect the companies to forget about the stockholders and start all working on one language to benefit humanity.  

I realize that a computer scientist must understand different programming paradigms and problem-solving techniques. Still, somehow, whenever I think of bunch of people coming up with new languages; and bunch of other people learning how to use them to keep up with the job market; this quote always comes to my mind:

QuoteYou can know the name of a bird in all the languages of the world, but when you're finished, you'll know absolutely nothing whatever about the bird... So let's look at the bird and see what it's doing -- that's what counts. I learned very early the difference between knowing the name of something and knowing something. - Richard Feynman (1918 - 1988)

William Grim

The reason that Java forces you to do OOP is because it's proven to be a good design principle.  I would not want to manage a huge project in a procedural manner.  Even operating system kernels written in C are object-oriented... at least any that come to the top of my head:
Linux, BSD, Coyotos, NT, SunOS, and probably many others.
William Grim
IT Associate, Morgan Stanley

sparks

While I concur that placing the constant on the left side of the '==' operator will fix the problem of introducing inadvertent bugs, I would have to argue that it sacrifices intent.

The standard thought behind the '==' operator is:


if(ACTUAL_VALUE == CONSTANT_VALUE)
{
     [i]do something...[/i]
}


Because this is normal procedure, people (those who must maintain your code) may become confused about your intent.

To avoid both problems, but perhaps introducing some overhead, we could devise a solution by writing a function to compare two values.  The overhead that may be introduced lies in the fact that multiple functions may need to be written to cover the different types (int, char, double, etc.).

Example:


void main()
{
     ...
     if(areEqualInts(CONSTANT_VALUE1, ACTUAL_VALUE1))
     {
          [i]do something...[/i]
     }

     if(areEqualInts(CONSTANT_VALUE2, ACTUAL_VALUE2))
     {
     [i]do something else...[/i]
     }
     ...
}

bool areEqualInts(int expected, int actual)
{
     return actual == expected;
}


This solution resolves the '==' operator usage for each value comparison to its corresponding function--reducing the chance for bugs due to incorrect syntax.  At the same time, there is no confusion around the usage of said operator.  

This does introduce the overhead associated with creating these comparison functions, but I will gladly take a little more time now writing these functions to save a lot of time later.  This also creates a performance hit due to function call overhead, but I'll believe it's overkill when my profiler tells me its overkill.
Mark Sparks
Development Lead
Monsanto

DaleDoe

Jerry wrote:

QuoteIAMJWC is correct about the use of END - not every statement, just at the end of blocks of code.

OK.  That makes a bit more sense.  Unless I'm misunderstanding, C++ uses a '}' at the end of a block of code, not a ';'.  And I still disagree; I think '}'s are quicker to type and more readable than "end".
"If Tyranny and Oppression come to this land, it will be in the guise of fighting a foreign enemy." -James Madison

Jerry

QuoteDaleDoe wrote:

OK.  That makes a bit more sense.  Unless I'm misunderstanding, C++ uses a '}' at the end of a block of code, not a ';'.  And I still disagree; I think '}'s are quicker to type and more readable than "end".

Yep, but part of the problem that programmers, particularly new programmers, have are disambiguating the use of the statement terminator ';'.

For example, here is a typical piece of code I see from new programmers working in IC6:

   if (! a_button());
           motor(1,50);


Means something different than
if (! a_button())
           motor(1,50);


But they do not understand or simple miss the fact that they put a ';' after the if condition.

Instead, this could make it more clear:

if (! a_button())
           motor(1,50);
       endIf


A typical activity in robotics is to have the robot wait until a particular button is hit. This would make it clear that the meaning is "While the a_button is not touched do nothing"

while (! a_button) endWhile
      motor(1,50);


Even if you teach programming habits of always using code block deliminters '{}', programmers can spend time having to match these up properly. Specific "endWhile, endIf, endFunction, ..." would make these clearer. Not to mention the problems that can happen when they leave them off of a loop or conditional construct when it contains only a single executable statement.

As for saving time typing - well context specific editors can reduce typing. It would be interesting to know if they reduce the number of programming errors too.

Though I have to wonder if DaleDoe is naming all of his variables with a single character to save time typing  ;-)
"Make a Little Bird House in Your Soul" - TMBG...

William Grim

I don't think placing the constant on the left-hand side makes it any more difficult to read code; I think the equality operator should make the intent perfectly clear.  However, that's a style issue and always up for debate; I just like it because the benefit far outweighs any cost of spending an extra split-second thinking about it.

As for your function, I don't know if I'd do that.  It adds more confusion than a simple reordering of the values to compare, but if you are going to do it, a macro would be best:


#ifndef EQUALITY
#define EQUALITY(x,y) x == y
#else
#error EQUALITY already defined.
#endif


With the above macro, you've eliminated any performance hit, and it works with any data type.
William Grim
IT Associate, Morgan Stanley

sparks

QuoteAs for your function, I don't know if I'd do that. It adds more confusion than a simple reordering of the values to compare, but if you are going to do it, a macro would be best:

I disagree strongly that the function adds confusion.  That is the beauty of choosing meaningful names.  When you read the if statement, it makes sense logically.  In fact, someone not familiar with the syntax of C could read that if statement and perhaps figure out the intent.

Quote#ifndef EQUALITY
#define EQUALITY(x,y) x == y
#else
#error EQUALITY already defined.
#endif

I like the benefit of this macro--works on any type that can be compared; no performance hit.  However, because it works on any type, it's not as error-proof as the functions I discussed.

For example:


if(EQUALITY(1, 'a')) [i]do something...[/i]


wont complain but...


if(areEqualInts(1, 'a')) [i]do something...[/i]


will most certainly complain, so it takes away the chance of making a mistake of this nature.  And performance is certainly a consideration, but I'll let my performance profiler tell me if there is an issue.

Again, I guess it comes down to personal style, taste, and preference.
Mark Sparks
Development Lead
Monsanto

William Grim


if(areEqualInts(1, 'a')) do something...


will not complain except for a warning if full warnings are enabled (and they should be).  The compiler will just promote the character data type to an integer and pad the upper N-8 bits with zeroes.  Likewise, if I pass a float or a double, a similar thing will happen (they will be truncated).

So, again, the functions don't make much sense.  I see what you're trying to do (and it's a good idea), but adding X functions for what you want adds much more confusion than reversing the order of comparisons in an equality test.  The brain gets used to seeing patterns, and "==" is a pattern that is quickly recognized when it's the only pattern used for an equality.  The extra functions are going to incur extra overhead for no added benefit that I can see.

As a side note, I think -pedantic on GCC compilers will cause GCC to stop with an error if it reaches something like

if ('a' == 1)

but allow it to continue if it encounters

if ('a' == (const char)1)

The reason for this is because -pedantic (I am pretty sure that's the option) will make GCC follow type checking rules religiously.
William Grim
IT Associate, Morgan Stanley