Monday, January 16, 2012

Don't Solve The Problem. Write The Language That Lets You Define The Problem.

I've often used an example to explain why so much code is big, bloated, hard to follow, and impossible to maintain.

Imagine you have a need in code to answer the question what is 2 + 2? You sit down and write the function, after counting on both your toes and your fingers to ensure the answer is portable:

int TwoPlusTwo()
{
   return 4;
}

Feeling very pleased with the speed and efficiency of the code and how remarkably exception safe it is you continue on. And you need 3 + 5.

At first you think I'll just copy and paste that other function and make it return 7. But something inside says that's not the right approach. This one function should be able to do more than that one line. So:

int TwoPlusTwo(int val)
{
   if(val == 2)
   {
      return 4;
   }
   else
   {
      return 8;
   }
}

But you look at this and inspiration strikes!

int Equation(int val)
{
   return pow(2, val);
}

I know in this trivial example it's easy to see that this is stupid, but it is very common to write this. I've seen this code from Microsoft, EA, Sony, all the big companies have plenty of this king of code.

What's missing and why do we write this way?
We are writing the answer over and over. When we learned math we did not memorize every possible outcome of every possible equation. Instead we learned a mathematical language. What's missing then is not the solution. It's the language we need to express a solution. When I write:

2 * 35 + 64 / 3 - 125^14

I don't know the result of that, but if I entered that into a calculator I would get the answer! The guy who made the calculator didn't even know that question would ever be asked and yet it gives a result.

How about another less trivial example. I won't write the language, but I want to show how a properly written language can express complex ideas in very simple terms. I'll write in psuedo code to avoid language dependency and the C++ template arguments that would make it less clear.

Here is my cross platform graphics engine in use for a simple example:

void main()
{
   System system(640, 480);
   Sprite ball("Ball.PNG");
   ball.velocity(1.0f, 1.0f);
   ball.position(10.0f, 20.0f);
   PhysicsWorld world();
   Scene scene();
   world.Add(ball);
   scene.Add(ball);
   Boundary bounds(0,0,640,480);
   world.Add(bounds);

   Timer timer;
   Keyboard keys;
   Mouse mouse;

   while(!keys.AnyKey() && !mouse.RightButton())
   {
      world.update(timer);
      scene.update(timer);
      Renderer renderer;
      renderer.Render(scene);
   }
}

There really isn't much I left out except for some template fields. And I do usually write with many comments and I'll write a post on why and how I go about writing in another post. But the thing to note here is that it's pretty easy to see what's going on and it's pretty easy to add more things if desired. The System object turns a console app into a Windows app or a Mac app or even PS3 or Wii. There is no need for a WinMain() or Objective C.

Sprite is the object least according to it's own form. It would have a template list of everything it was composed of. It's policy classes. I'll write about that, but much later. For now just know it can be a render object or a physics object, but it inherits from nothing in the classic understanding of inheritance.

The Physics world detects and dispatches collisions, but under the hood all it really does is present close by objects to each other and let them work out if they hit or not.

Scene is an object for rendering views.

Boundary is an area that restrains physics.

Timer is the local clock with some extra abilities, and keyboard and mouse are pretty self explanatory.

Renderer is an object I put in the main loop because I wanted to point out something. When you write small, simple, and lightweight objects you don't have to worry as much about efficiency. Here it makes no difference where I put it from the point of efficiency.

So what you get is a ball bouncing around the screen like if you leave your DVD player on too long. An entire graphics application start to finish in 25 lines of code. It wouldn't take much more than that to make break out, space invaders, defender, or other arcade games because I wrote the language of arcade games. All I have to do is assemble sentences.

Let me know what you'd like to understand better and I'll make that my next post. Otherwise I'll go on to how I start a new project.

2 comments:

  1. You made a typo... 3 + 5 = 7

    :P

    Anyways, I like your approach. I have found it to be true in my coding practices that by addressing what the problem is itself instead of immediately trying to solve it has given a lot less messy code. :)

    ReplyDelete
    Replies
    1. Half typo and half brain dead. I meant to point out that this approach introduces errors. and that 7 is not correct. The meta language reduces these errors.

      Delete