Fourth World Logo Fourth World Media Corporation
  Embassy Services Products Resources   About Fourth World Contact  
logo

xTalk FAQ

Frequently Asked Questions about
HyperTalk and Related Modern Diaects

Richard Gaskin
Fourth World Media Corporation
ambassador@fourthworld.com

First published 4 April, 2003.

Copyright © 2003 Fourth World Media Corporation

This document may be distributed freely only in its complete, unmodified form, including this header and copyright information.


Abstract
xTalk refers here to HyperTalk and subsequent dialects. Looking at how application development has changed since HyperTalk's premier in 1987, one could argue that xTalk not only legitimized GUI scripting, but in many respects has affected nearly all aspects of application development. Yet for all these accomplishments and historic popularity, xTalk is among the least understood language families, and possibly the least appreciated in terms of bag for the buck. This FAQ aims to help correct that.

Keywords: xTalk, HyperTalk, SuperTalk, MetaTalk, Transcript, OpenScript, scripting, fourth-generation languages, programming, rapid development.


Introduction

Binding the language design to an object model that's

At some point in everyone's career, we all write crappy code (except my friend Mark Lucas, but he's in a class by himself). You know the kind: You go back to it six months later to add new features or better error-checking, and you have no idea what you were doing. It's "spaghetti code", and it can eat a lot of time as you try to figure things out all over again. That's where this document comes in.

A few healthy habits can make your code more robust and your workday shorter. While they may require a little self-discipline at first, if you find these practices useful and adopt them in your daily work they'll become second-nature and eventually require no extra effort at all.

There are many reasons to use good style practice when writing code, including:

  • More easily read and understood code
  • Consistency in a multiple-author environment
  • Make future enhancement more easily
  • Turn out code you can be proud to share
  • Impress the chicks (or dudes, as the case may be)

The tips presented here reflect some of the best practices of many world-class scripters, and we should take a moment to acknowledge them here:

Michael Silver, Ken Ray, Mark Lucas, and Mark Hanrek are some of the best SuperCard scripters I've known, and it was while we were sharing code that we discovered that we had each independently developed pretty much the same code style. The first draft of this document reflected our common interest in SuperTalk, and was accordingly more limited in scope. It was distributed as part of a panel discussion on scripting techniques at the SuperCard Developer Conference in 1996, the year SuperCard 2.5 won the Mac Eddy award for Best New Multimedia Authoring Tool.

Since then this document has been revised many times, expanding its scope to include new tips for other similar scripting languages. Along the way I've collected valuable tips from a great many programmers, including Scott Raney, Kevin Miller, Steven McConnell, Jad Santos, and Alex Bronstein. With the things these folks have taught me, this document should hopefully be useful for scripters who prefer just about any HyperTalk dialect, and a few others besides.

As you read these tips, you may feel at times that where you code style differs you kinda like it that way. That's okay. If it works for you, it don't need fixin'.

These are only guidelines, and the more people use them the more easily people will exchange code happily, solve problems more quickly, spend more time with their families, and create an ever better world.

Having said all that, it is never worthwhile to rewrite existing code simply to conform to these suggestions. Nor is it worthwhile to stick to a guideline where the function or efficiency of the code is compromised. There is a fine line between making more maintainable code and more efficient code. It is up to you to decide where that line is in your own code.

This document is a work in progress. We use it here in the course of teaching and working with contractors, and accordingly it is always being ammended and enhanced as experience suggests and time permits. If you have ideas for tips and techniques which you would like to see included here, please email them to me at scriptstyle@fourthworld.com.

With all that out of the way, here's the beef:


General Scripting Techniques

A few techniques for getting the spaghetti out of your code....

Clarify Program Flow

One way to fight spaghetti code is to make sure your code's logic is self-evident. For example, if you have one long handler which takes care of a variety of startup stuff, you could break that down into multiple handlers which handle only specific related tasks. If you use descriptive names, you'll have no trouble grasping how the first handler works:

on startup
  InitMenus
  InitWindows
  InitPrefs
end startup

The extra time required by the interpreter to handle these separate calls is trivial, and isolating specific sets of related routines in this way can help you track down the source of errors during debugging.

One good technique for making sure your code's flow is evident is to write the comments first. Just make an outline of what the code needs to do in comments, then go back and fill in between them with the actual executable lines. This technique can also be useful in the early stages of tackling a difficult routine, helping you think through the stages needed to complete it.

Many words in scripting languages can be abbreviated. Where available, use 'em. They save space, and by being visually less significant they help your variables and handler names stand out. Plus you'll save a little typing.

You can use parentheses to group logical or arithmetic expressions or to clarify code:

if (the vis of wd "myWindow") then

instead of:

if the vis of wd "myWindow" = true

Also, always using "=" for comparisons instead of "is" will help make such comparisons stand out visually.

Comment Effectively

As a general rule, it's useful to comment any block of code whose functionality is not immediately self-evident. For example, it's merely distracting to see:

-- Set the cursor to watch:
set the cursor to watch

But it's very helpful to have a brief note like this for more complex blocks:

-- Update each card with the current date:
set the cursor to watch
put the short date into tDate
repeat with i = 1 to the number of cds
  put tDate into fld "Date" of cd i
end repeat

Comments can also be used as visual guides, separating different sections of code. Here's an example of how sets of related handlers can be grouped visually using commented lines:

--=============================================--
-- WINDOW ROUTINES
--

--
-- DocWindowRect()
-- 
-- Returns the default size for new document windows
--
function DocWindowRect
  put the screenrect into r
  add 4 to item 1 of r
  add 4 to item 2 of r
  subtract 4 from item 3 of r
  subtract 4 from item 4 of r
  return r
end DocWindowRect

--
-- UpdateAllWindows
--
-- Allows each open window to refresh itself
--
on UpdateAllWindows
  put windows() into tWdList
  repeat for each line tWd in tWdList
     send "UpdateThisWindow" to tWd
  end repeat
end UpdateAllWindows

--=================================================--

Note that the example above also includes a brief description of each handler. This makes handlers stand out more clearly when skimming, and provides a useful overview as to its purpose.

You can also use comments to separate blocks of code within a handler, like this

on MyHandler
  global gMyGlobal
  --
  SomeStatementHere
  AnotherStatement
  --
  StatmentForSomethingElse
  MoreOfThat
end MyHandler

By breaking code into blocks with comments you can make groups of related statements stand out from the rest of the code. And by using blank lines only between handlers but not within then, you can skim through the code more easily.

You'll want to use comments liberally if you're not concerned about script size. SuperCard, HyperCard, and a few other scripting environments have a 32k limit on the size of a script, so if you run into this limit you make want to use only important descriptive ones and ditch those used as visual separators to save space.

Code for Reuse

As you write, ask yourself if you'll be using this again. If so, break it out into a separate routine so you can call it from anywhere. If you see yourself typing the same lines into a new handler that you used earlier in another, maybe that's a good opportunity to break it out.

When you write your handlers, try to make them as generalized as possible. If you can avoid using global variables do so, since anything dependent on other routines will reduce the chances of being able to use this routine again in the future. If you only need to read a value in a global, consider passing it into the handler as an argument instead.

Another way to keep code portable is to remove references to specific objects. If you have a function which calculates the sum of two fields for example, a non-portable version might look like this:

put SumFields()

function SumFields
  return fld "Num1" + fld "Num2" + fld "Num3"
end SumFields

This handler only works if you have three fields using those specific names. You could make it more portable like this:

put SumFields(fld "Num1", fld "Num2", fld "Num3")

function SumFields
  put 0 into tSum
  repeat with i = 1 to paramcount()
    add param(i) to tSum
  end repeat
  return tSum
end SumFields

Not only can this handler be used anywhere, by using the param and paramCount functions we can now return the sum of any number of containers, making it applicable for a great many more uses.

When referring to objects, use names whenever possible. If the name cannot be known or may change, use the object's ID number. Try to avoid using the ordinal number of the object (e.g., "button 4"), as you may make changes to the layout which will cause the script to break. Descriptive names also help you understand the purpose of the object.

Name all objects (cd flds, grcs, wds, &c.). Stay away from using numbers unless your script depends upon that technique (and even so, you should still name them. I often use names like "Bookmark 1," "Bookmark 2", &c.).

The Zen of Runtime Editing

Because interpreted languages are generally slowed than compiled ones, speed is a more critical consideration. Here's a few tips for helping the interpeter do its job faster:

  • Use spelled-out words for literals such as "comma" and "colon" where the language provides them. These are parsed more quickly by the interpreter.

  • Always put string literals in quotes, even one-word literals. Never put numeric literals in quotes (unless the language requires it). This improves parsing speed, since otherwise the interpreter must check its list of variables for a match before determining that the string is to be used literally.

  • Use numbers instead of textual numeric constants. (e.g. "put myVar - 10 into cd fld "myField"", not "put myVar - ten into cd fld "myField"")

  • Perform repeated operations on variables, not fields. When accessing a field, the interpreter needs to do a great many additional things to set the text up, modify it, and display it. Putting the data into a variable first lets you work on it much faster, often by several orders of magnitude.

Naming Conventions

In this section we advocate liberal use of what is commonly referred to as Hungarian notation, in honor of the famous Microsoft programmer Charles Simonyi, who is said to follow this practice obsessively. The value of Hungarian notation is that it provides a consistent method for determining the use and nature of a given container by its name alone, without having to look elsewhere in the code to find out where it came from, such as whether it's global or was passed in as an argument. To varying degrees, this style has been adopted by many scripters in recent years, as is reflected in the product documentation for Revolution, SuperCard, and others.

Variables and Arguments

You can quickly identify whether a variable is local or global, or whether it was passed in as an argument, if you preceed the descriptive name with a lower-case letter to determine its type.

Char Meaning Example
g
Global variable gMyGlobal
t
Local ("temporary") variable tMyVar
s
Script-local var* (sometimes called "static") sMyVar
p
Parameter (also called an argument) pMyParam
k
Constant* kMyNumber
u
User-defined (or custom) properties uMyProp

* Runtime Revolution, MetaCard, and Visual Basic only.

Note about "p": I've had a few readers suggest that "p" should not be used for parameters, and that scripters using Lingo and AppleScript more commonly use it to designate properties. I've had a tough time finding published code libraries that use this convention, so if you find any drop me a note at scriptstyle@fourthworld.com. This document is an ongoing work, and I welcome the opportunity to keep it up to date as new trends in scripting become evident.

Associative arrays can be local, script-local, global, or passed as a parameter, so to preserve the leading type designation arrays are noted with a trailing "A", often in plural form to reflect its status as a collection, e.g.:

gOpenWindowsA
tPasswordsA
pSelectedObjectsA


Some general tips for naming variables and arguments

  • Declare all globals in the first line(s) of your code.

  • If your language supports local variables, also delcare them at the top of your code before executable statements.

  • Use globals sparingly. Code written with globals often has problems which are difficult to find since the globals can be touched anywhere. However, if you need to use a global, you need to use a global.

  • Always use variable names that are descriptive. Stay away from names like "temp," "var1," and other names which have no meaning. Use instead names like "tProjPath."

  • The exception to the previous rule is counters which serve no other function except as an index within a loop. Traditionally, counters begin with the variable "i" and procede through the alphabet (next use "j," then "k," then "l.") You do not need to use "j", &c., unless you are building a nested loop.

In this example we use a number of these tips:

on myHandler pNumPeople,pNames
   global gFilePath 
   global kMaxPeople,kMinPeople -- constants
   if pNumPeople > kMaxPeople then exit myHandler
   if pNumPeople < kMinPeople then exit myHandler
   repeat with i = 1 to the num of items in pNames
      put item i of pNames & cr after tFileData
   end repeat
   open file gFilePath
   write tFileData to file gFilePath
   close file gFilePath
end myHandler


Handlers and Functions

  • Use all lower case, except where noted below

  • Capitalize the first letter in all but the first word of any compound word (e.g. doubleClickList). This applies to handler and function names, variables, and lexicon of the language you are using.

  • Capitalize the first letter in custom hander and function names.

  • Avoid using underscores to separate words (e.g. my_ugly_handler_name). These are harder to work with since the entire word cannot be selected with a double-click.

Example:

on MyHandler
   global gFilePath 
   put cd fld "Names" into tNames
   set the directory to pFilePath
   put the files into tFileList
   pu tconvertDirector(ytFileList) into tDirectory
   if tNames = tDirectory then pass myHandler
end MyHandler