LOOPE - Lingo Object Oriented Programming Environment by Irv Kalb

Previous Chapter

Table of Contents

Next Chapter

 

Section 1 - Parent Scripts and Objects

Chapter 2 - Naming Conventions

 

The Rosetta Stone

Even though we've just started, I want to make a quick but important diversion to lay some groundwork for the rest of the book. In all of the programming examples in this book, I will be using a convention for the naming of variables and handlers. A naming convention represents an agreement by one or more programmers to create names for variables and handlers according to a set of rules or guidelines. Although the concept of using such a naming convention seems to be foreign to most Lingo programmers, it has been widely used in other, more traditional, programming languages for years.

When writing code, the names of variables and handlers created by a programmer are always up to the programmer. A programmer can choose to make the names long or short, descriptive or useless, clear or confusing. A lot depends on the mindset of the programmer. Will this code be reused? Is this "quick and dirty" code? Is this code so clear that it is self-explanatory? The names of variables and handlers can be extremely useful in making code more readable. Choosing names of variables and handlers may seem not very important, but insisting on meaningful names helps a programmer to organize thoughts. If you can’t come up with a clear name for a variable, then that variable probably isn’t well thought out. If you can’t come up with a clear name for a handler, perhaps the functionality of that handler isn’t defined enough to code it.

By naming variables and handlers in a certain way, you can encode important information right into their names. My naming convention involves using a "prefix notation" where letters are prepended to (put in front of) the name of a variable to give information about the scope and type of a variable.

 

Scope

Scope refers to the range of code over which a variable is accessible. Accessible mean where you can set its value or where you can get its value. With respect to "scope" in Lingo, there are three different kinds of variables.

The first kind of variable is a global variable. As implied by its name, global variables are available anywhere within Director. You just have to remember to include a "global" statement defining that variable at the top of any script that uses the global variable.

The second kind of variable is a local variable. The scope of a local variable is from its first use in a handler until the end of that handler. You do not need to have any special declaration for a local variable. Lingo allocates storage for a local variable when it is first used in a handler. When the handler ends, the local variable and its value vanish.

Finally, there are property variables which are declared and used within a parent script. The scope of a property variable is defined to be anywhere within the current script. Property variables are the key to how objects remember things. When you declare a property variable within a parent script, that variable is available to all methods within that parent script. In this way, a value stored in a propery variable can be "shared" among any or all methods. If you set a property variable in one method, that property variable will maintain its value, and other methods can check and/or set the value of that property variable. As we saw in the first chapter, you declare a property variable by using the word "property" followed by the variable name. For example:

property pCount

(Note: Property variables are also used in an identical way in behavior scripts.)

In my naming convention, the first letter shows the scope of a variable. The names of all global variables start with a lower case "g". For example, gCount, gPath, etc. The names of all property variables start with a lower case "p". That is why I chose the name pCount above. The "p" indicates that this is a property variable. The name of any local variable starts with any lower case letter except "g" or "p". Examples of local variables are; i, j, temp, thisNumber, thatValue, etc.

 

Type

In computer languages, variables are used to hold data. But there are many different types of data, for example, integer, floating point, string, list, etc. In many computer languages you must explicitly give the type of the variable when you declare the variable.

int x

string y

float z

In the original FORTRAN language, variables could only represent either integer or real numbers. The compiler needed a way to distinguish between these two types of data. In the process of designing the language, it was decided that the type of a variable would be determined by the first letter of the variable name. The rule was that any variable name that started with the letters I through N would represent an integer. Any variable name starting with any other letter would represent a real (floating point) number. The range of I through N was chosen because these are the first two letters in the word "integer". So, by the definition of the language, FORTRAN made this naming convention a requirement.

In Lingo, we have no such restriction. By contrast, Lingo is not a "typed" language. The creator of Lingo (John Thompson) has said that he is very proud that any Lingo variable can contain any type of data. This makes Lingo extremely flexible. By using the assignment statement:

x = 4

Not only does the variable x get set to the value 4, but it also sets the type of the variable x to be integer. We can use the ilk() function to determine the type of any variable:

put ilk(x)

--#integer

However, if we simply assign a different value to the same variable, it also changes its type:

x = 5.0

put ilk(x)

-- #float

x = "abc"

put ilk(x)

-- #string

 

Because you can store any type of data into any variable in Lingo, it would be very helpful if we could somehow ascertain information about the type of the data from the name of the variable. And that is the next part of the naming convention. Following the designation of the "g" for global or "p" for property, we then use a letter or letters to specify the type of the data in the variable. Here is a list of prefix letters that I commonly use, and the data types that they represent in Lingo:

  f flag (TRUE or FALSE)
  l list
  ch channel
  m member
  k constant
  o object (I will go into this in much detail in a later chapter)
    (for now just notice how it fits into the naming convention)

And an extra letter which comes in handy:

  n number

These can be combined to create more complex data types:

  nm number of member
  lf list of flags
  lch list of channels

 

Base Variable Names

Following the scope and type prefix letters comes the real name of the variable. Variables can be thought of as the "nouns" of a computer language - they are used to describe the things on which the language operates. To Lingo, the name of a variable has no inherent meaning - any name works as well as any other name. A variable called "x" works just as well as one called "areWeConnectedToTheNet". However, using meaningful names make a great deal of difference to the programmer, and especially to other programmers who may have to deal with code that they did not originally write. From a human perspective, it makes sense to use names which are as descriptive as possible. This way, when you, or even more importantly someone else, attempts to read code long after you originally write it, the code will be clearer and make more sense.

The rule for the base name of the variable is: use one or more words using upper case letters to start words.

The only exception to this rule is the case where you have a simple very temporary local variable which does not have any type information. For example, I will often use the variable "i" as an iterator in a loop such as: repeat with i = 1 to 10. Or in a simple assignment statement such as: totalCost = nItems * price. Notice, however, that all variable names begin with a lower case letter.

 

Examples of full variable names

Here are some examples of variable names following this naming convention and what they mean:

  gkSliderTop global constant of the top of a slider
  goNavigation global navigation object
  pchButton property, channel in which a button is found
  plNames property which is a list of names
  pnSceneMarkers property which contains the number of scene markers.
  ploAnimations property which is a list of animation objects
  fOK local flag variable
  oScroller local scroller object
  lchButtons local list of channels of buttons
  nmRoll local number of member Roll

 

I was first introduced to the concept of naming conventions many years ago when I worked for Convergent Technologies. There were a large number of programmers writing in a language called PL/M. As tasks changed, it was important to be able to hand off code to other programmers and be able to have that code understood quickly. Some of the early programmers there had worked with a man named Charles Simonyi at Xerox PARC (Palo Alto Research Center). Mr. Simonyi, who was of Hungarian descent, developed a style of naming using a prefix notation. This style later became known as "Hungarian notation".

 

Handler Names

If variables are the nouns of a computer language, handlers are the "verbs" of the language - things which specify actions. Again it makes sense to try to use names that are as descriptive as possible, ones that imply actions. As far as handler names are concerned, I use some very simple rules. All globally available handlers (that live in movie scripts) start with a capital letter. If a name consists of more than one word, start separate words with capital letters. All handlers which are methods of an object start with a lower case "m". Here are some examples:

StartSound("SoundName") -- call a global Sound handler

PrintPages(lPagesToPrint, fUseHeaders) -- call a global printing handler

nextPageNum = oPage.mFindNextPage(currPageNum) -- call the mFindNextPage method of the oPage object

me.mSetPuppets(TRUE) -- call a method inside the current object

 

The usefulness of a naming convention

Here is a snippet of code from a parent script which will serve to demonstrate the power of such a naming convention. I will show three variations of the same code. All three examples would execute identically. The only difference is in the names of the variables and handlers.

You may find this first code listing nearly unreadable (I certainly do). The variable and handler names don’t give any clue as to what they represent. (It’s shocking but true -there is a lot of code in the real world that actually looks like this.)

 

global d
global r
global c

on whatever
  if count(r) = 0 then
    s()
  end if
  x = FALSE
  b = count(r)
  repeat while NOT(x)
    i = random(b)
    n = getAt(r, i)
    if n <> c then
      x = TRUE
      c = n
    end if
  end repeat
  
  deleteAt(r, I)
  if d then
    put ("Answer is:" && n)
  end if
  return n
end

 

The second version of the code provides more meaningful variable names and handler names. However, you will notice that the variable names don’t give you information about what type of data they represent or their scope. Further, it is customary to place global declarations at the top of the script. If there were many handlers in this script and you were looking at the code of just this handler, you could easily miss the global declarations. Therefore, when reading the code, it would not be clear as to which variables are global and which are local.

global debug
global available
global previousnum

on getnextrandom
  if count(available) = 0 then
    initlist()
  end if
  done = FALSE
  n = count(available)
  repeat while NOT(done)
    randomindex = random(n)
    randomnum = getAt(available, randomindex)
    if randomnum <> previousnum then
      done = TRUE
      previousnum = randomnum
    end if
  end repeat
  
  deleteAt(available, randomindex)
  if debug then
    put ("Answer is:" && randomnum)
  end if
  return randomnum
end getnextrandom

 

Finally, here is the same code using my naming conventions. From looking at the code, it becomes immediately clear as to which variables are global and which are locals. It also becomes obvious that some of the variables are lists, some are values, and some are flags.

 

global gfDebug
global glAvailable
global gPreviousNum

on GetNextRandom
  if count(glAvailable) = 0 then
    InitList()
  end if
  fDone = FALSE
  nAvailable = count(glAvailable)
  repeat while NOT(fDone)
    randomIndex = random(nAvailable)
    randomNum = getAt(glAvailable, randomIndex)
    if randomNum <> gPreviousNum then
      fDone = TRUE
      gPreviousNum = randomNum
    end if
  end repeat
  
  deleteAt(glAvailable, randomIndex)
  if gfDebug then
    put ("Answer is:" && randomNum)
  end if
  return randomNum
end mGetNextRandom

 

It may seem that using a naming convention such as this is a lot of work, but it is something that is well worth the effort. After a small amount of time it becomes second nature. In fact, it forces you to stop and think about what the "correct" name of a variable or handler should be. The payoff comes when you try to read code that you wrote a while back. If you have forgotten how some piece of code works, you will be surprised as to how much easier it is to read code that follows a naming convention such as this. It is even more helpful when you are working as a member of a team that has adopted such a convention. You find that using a naming convention helps you understand the meaning of other people's code more quickly than if there were no such convention in place.

Even if you choose not to use a full naming convention as I have laid out here, at a mininum, using "g" for all global variables "p" for all property variable will go a long way to making your Lingo code more readable.

 

Previous Chapter

Table of Contents

Next Chapter