Channels ▼
RSS

Mobile

Programming Paradigms


JUN94: PROGRAMMING PARADIGMS

PROGRAMMING PARADIGMS

Mushroom Programming for Newton

Michael Swaine

I admit, somewhat sheepishly, that the source code for a Newton application accompanies this column. Why, you ask; and I ask myself, is this an exercise in futility?

The computer press has not been easy on Apple's Newton MessagePad, the purported realization of John Sculley's dream of a Personal Digital Assistant. Jokes are made, and references to the Apple III and the Lisa occur with distressing frequency.

Distressing, at least, to someone who has invested in Newton's future by buying a MessagePad, a developer's kit, a place at the developer's conference, and so on. I have the receipts in front of me now, as motivation.

But I am not downhearted. I take solace in this truth: Newton is a technology, not a platform. Writing for Newton doesn't mean writing for the MessagePad, or the MessagePad 100, as the original has been renamed now that there's a new and improved model 110. Certainly there were problems with the 100, and certainly not all of them have been solved in the 110, although it is a significant improvement on the original design.

The 110 has a faster infrared beaming port (38.4 kbps instead of 19.2), different power system, more internal RAM (1 Mbyte total rather than 640 Kbytes, which works out to a big proportional increase in user-usable RAM), a slightly different form factor, a slightly different size of screen, a different pen, a flip-up lid, and a new ROM that includes deferred recognition and a try-by-letter recognition option. The ROM is available as an upgrade for the original MessagePad. Deferred recognition should mean the difference between usability and unusability in some note-taking situations.

Newton's Flaws

But I believe that the significance of the MessagePad's handwriting deficiencies has been inflated. In my humble opinion, the main technical problems with the original Newton MessagePad, in decreasing order of importance, were:

  1. There was no built-in modem. Isn't that a rather serious flaw in a so-called communication device?
  2. It didn't fit in the average pocket. It just missed, but that was a big miss for a so-called portable device. Maybe everybody at Apple carries a bag, but some of us out here grew up in the Midwest and rely on our pockets.
  3. It was buggy. This was less serious. Okay, it was unacceptable, but it was also predictable. This is a very new technology. This was version 1.0. There's a ROM upgrade.
  4. The handwriting recognition failed to recognize handwriting often enough to make the device unusable as a meeting note-taker, which is just what any computer journalist would try to use it for if given the slightest encouragement.
I regard the handwriting problem as being not as serious as the other problems mentioned, although it has received most of the press. That was Apple's fault. The worst problem with the machine, in fact, was Apple's positioning of it. If you ask me, it should have been sold as a device for communications, name-and-address storage, to-do lists, and appointment reminders. The note-taking capability and handwriting recognition should have been presented as a novelty feature, a hands-on demo of technology under development, an ATG (Advanced Technology Group, also known as "Alan's Toys and Games") freebie.

Life Stinks, or BYTE's Reality

Which brings me back to the question that motivated my writing a Newton app: What is a Newton good for?

Apparently not for implementing the game of Life. DDJ contributing editor David Betz described his efforts in this direction in the March 1994 issue of BYTE. He found NewtonScript grindingly slow for this application. Okay, scratch that.

For what it's worth, I'd say that the current Newton devices are well adapted to three kinds of applications:

  1. Personal Information Manager (PIM) stuff. Electronic to-do lists, appointment calendars, name-and-address databases. Small apps you'd like to carry around in your pocket. (Oops. The pocket problem again.)
  2. Communications. A built-in modem is still lacking, but the pager on a PCMCIA card is amazing, and the two-way cellular communication from a Newton device redefines portable computing.
  3. What I think of as "tap apps"--applications that don't require much typing or writing. These apps just ask the user to tap a few buttons or menu choices, then return some brief text or illustration. Mobile kiosks, you might call them. This is not a real category, of course; it could include anything from a calculator to an expert system for medical diagnosis. But it seems like a reasonable way of thinking about the question of how to make the Newton useful, a question that would be heavy on your mind, too, if you had these receipts in front of you.
And in fact I think expert systems are not at all a bad idea for Newton applications.

AI Lite

Why expert systems?

The interface seems right. Expert systems, at least those I've come across, typically take input from users in small chunks and return brief textual opinions (possibly augmented by lengthy explanations). Sounds good for a device with a small black-and-white screen.

Expert systems need not be compute-heavy apps that require heavy iron. Even a Newton ought to have the horsepower for a simple expert system. That suggests a subtle third point.

Devices like Newton could spur interest in truly simple expert systems. AI lite. Users might have quite different expectations of an expert system that can be carried in the pocket (oops, the pocket problem again) and that boots (machine and app) in four seconds or so.

Imagine using an expert system in the field on your conventional portable computer. If you have to turn on the portable, wait through its boot cycle, load the app, and take an occasional hike in the middle of using it to get an answer to a question (portable or not, you don't want to move a computer while it's running), you probably aren't going to be satisfied with a response of "Gee, I dunno."

Now picture using a handheld device that is carried in your pocket (let's imagine that Apple can crack the pocket problem) and can be consulted in a few seconds. You might shrug off a "Gee, I dunno" response more philosophically.

For some situations, it seems to me, the place where you need that expert advice is in the field, and the time when you need it is ASAP, and pretty good right now is better than excellent some time later. Small expert systems might be ideal in such situations. (You'll notice that I don't actually name any such situations, but take it on faith that there are some.)

Anyway, whether there's a market or not, I'm writing a small expert system for Newton. This month you'll see the application shell and the user interface. Next month, I'll present the inference engine and the knowledge-base structure.

A two-part presentation actually makes sense. The project as I've conceived it breaks down nicely into the front-end and back-end components: the UI and the smarts. Since the Newton Toolkit (NTK) provides an abundance of templates for user-interface development, this first part of the project is mainly an exercise in using the NTK and its templates. On the other hand, the NTK doesn't provide any templates or classes for what I'll be doing in the second half, so that'll be more an exercise in writing NewtonScript code. Just so you know, I mean an exercise for the author, not the reader. You'll be watching me learn here. Scary for all concerned.

A Fungus Amongus

I wanted a tool I could take with me into the woods when I go hunting for mushrooms. It didn't have to give authoritative advice on mushroom species, but it should help me decide whether it was worth throwing the latest find in the basket to take home and look up in my mushroom books. A fallible but helpful advisor.

That sounded like a good candidate for a tap app. Tap to select mushroom features like color and cap shape, tap a button to start the identification, and read off the identification in a text field.

Some mushrooms can be identified from a few features, some require more. The program should accept partial data. It should also allow refinement of the feature list: Hmm, that identification doesn't look right; let's try calling this thing red rather than brown. Or: Here's another mushroom like that last one I identified, but it has a scaly stem; I'll change just that feature and ask for another identification. And, so that I can learn which features matter in identifying different kinds of mushrooms, it should give feedback on what features it used in making its decision, but this information should be shown only on request.

It was clear that the program would need to display a hierarchy of mushroom features, since over 100 features might be relevant for some identifications. For the current version, though, I restricted it to a single screen of feature choices.

Frame and Fortune

A frame is a ubiquitous data structure in NewtonScript, and Listing One (page 137) consists of a collection of frames. A frame consists of an unordered collection of slots, each of which comprises a label and a value. The slot's value can be any data type, including a frame or a function. You access a slot's value using dot notation; see Example 1(a).

Methods are defined for frames by creating slots whose values are functions. NewtonScript uses braces to enclose the frame and commas to separate the slots, so you can create a frame like Example 1(b). The top-level frames in Listing One define views, which are UI components.

In creating views, NTK lets you select from a lot of predefined elements called "prototypes," or "protos." I used the supplied application-shell proto named protoApp to create the base view, Mushrooms, for the program Fungus. ProtoApp comes with slots for a title, view bounds, format, various flags, attributes, methods, and a required slot named declareSelf, which has a default value of 'base for an application, and which identifies the view to the system. (The single quote in front of base identifies it as a symbol.)

You can add slots, of course, to any view that you construct using this or any proto. ViewSetupFormScript is a standard method, executed before any of a view's other slots are evaluated. It's the place to set screen coordinates of the view, for example. Now that Apple has come out with a second MessagePad (the 110) with different screen dimensions, it's important to create views that work with different screen sizes. The code in viewSetupFormScript sizes the view to fit the screen. All subviews should then be sized to fit in this view, and I confess I haven't done that yet.

The observations frame holds the features selected by the user. Its collection of frames will grow as I add more features to be identified, and I'll probably have to rework other parts of the program if these features become hierarchical, as they should; see Example 1(c).

The advisor method will be replaced by a simple expert-system advisor. These are just a few If/Then tests to let the app return some kind of identification based on the observations.

The _proto slot identifies this view as having been derived from the protoApp proto. The _proto slot defines an inheritance path in one of NewtonScript's two inheritance mechanisms. A view can inherit from its proto (in this case a system proto residing in ROM) and from its parent view. This base view, Mushrooms, is the parent view for all the other views in this program. They normally have access to its slots, but not vice versa. There is a mechanism for making child views visible to the parent: You declare the child to the parent. This installs an extra slot in the parent, pointing to the child. It has some overhead, and you should only do it when necessary. I use it with the MessageBox and Size views in this app.

The Kids Are All Protos

Most of the user's selection of mushroom features is handled using views based on the protoLabelInputLine proto. This displays a label with a dotted line next to it. When you tap on the label, a list of possible values pops up. Tapping on one of these selects it, displays it on the dotted line, and places it in a slot of the view. Listing One shows one of the views based on this proto.

The normal way of reading off the user's selection with this proto is by using the textChanged method. The MessagePad has a fixed Undo button at the bottom of its screen, so you can add undo capability wherever it's appropriate. Undoing a selection in this view is a simple matter of putting back the previous selection, so I implemented that. It's necessary to register the undo method with the system, since the Undo button belongs to the system, rather than to your app.

Listing One also shows how I implemented a slider, adding two extra views, one to display a label like those of the protoLabelInputLine views, and one to display the slider's value as a number of centimeters. Declaring the latter to the base view and having the slider view message the base view to update the centimeter display view is one way to synchronize these two sibling views, the slider, and the centimeter display. I doubt, however, that it's the best way.

The last two views in the listing are the Advise and MessageBox views. The only interesting thing about the Advise view, which defines the button the user taps to start the identification, is that it's a picture button. You can include PICT-format pictures in Newton apps, for illustrations or icons, by placing them in resource files and choosing a menu item that adds the files. Figuring I'd let Apple do as much work for me as possible, I created this button's icon using a mushroom picture that I found in the Apple-supplied HyperCard art-bits stack.

The MessageBox view displays the identification to the user. Its text slot is initialized to a brief introductory message. MessageBox is declared to the base view, Mushrooms, so that the base view can update its text slot with the identification.

Programming with the NTK at this level is a mixture of coding and visual programming. You do a certain amount of clicking and dragging to initially create your views, then add functionality by writing methods. Next month will be all coding, though, as I try to put some smarts into the Advisor method.


Example 1: (a) Accessing a slot's value using dot notation; (b) using braces to enclose the frame and commas to separate the slots so as to create a frame; (c) hierarchical approach to mushroom program.

<b>(a)</b>
viewBounds.top := b.appAreaTop + 2

<b>(b)</b>
myFrame := {
  slot1 : 1000,
  frameSlot : {
    name : "David",
    game : "Life"
  },
  methodSlot : 
    fun()
      begin
        // method body;
      end
}

<b>(c)</b>
observations := {
  cap := {
    cap_surface : "",
    cap_color : "",
    cap_shape : "" },
  gills := {
    ... }
}

[LISTING ONE]



// Fungus -- A Mushroom Identification Program for Newton by Mike Swaine
// Fungus presents a single screen of mushroom attributes, lets the user set
// values for some or all of them, and tries to identify the mushroom based
// on these values. NB: This is a demo of NewtonScript, not a useful app.
// Its "advice" is NOT to be relied upon! The base view of the application is
// a frame named Mushrooms. It's based on the protoapp proto.
Mushrooms :=
  { viewSetupFormScript: /* executed during view creation */
      func()
      begin
        // Set view bounds relative to screen dimensions.
        local b := GetAppParams();
        self.viewBounds.top := b.appAreaTop + 2;
        self.viewBounds.left := b.appAreaLeft + 2;
        self.viewBounds.bottom := self.viewBounds.top+b.appAreaHeight-4;
        self.viewBounds.right := self.viewBounds.left+b.appAreaWidth-4;
      end,
    title: "Mushroom Field Guide",
    viewflags: 5, /* visible, clickable, etc. */
    viewFormat: 328017, /* pen, frame, etc. */
    declareSelf: 'base, /* required for base view */

    observations: /* attributes of mushroom to be identified */
      {color : "", /* all initialized to emptiness */
        size : 0,
        cap_shape : "",
        cap_surface : "",
        gill_type : "",
        gill_attachment : "",
        stem_position : "",
        stem_surface : "",
        veils : ""},

    advisor: /* the mushroom identification engine */
      func()
      begin
        // Dummy code. Could be extended to a humungous list of IF-THEN tests,
        // but plan is to replace with a simple expert system. Note: in
        // NewtonScript, all = tests on structured objects compare pointers,
        // while < and > tests compare contents. Hence variations in syntax.
        if strEqual(base.observations.color,"brown")
          AND base.observations.size > 3
          AND strEqual(base.observations.gill_type,"absent")
        then base : advise("a Bolete",1);
        else if strEqual(base.observations.color,"brown")
          AND base.observations.size <= 3
        then base : advise("an LBM (little brown mushroom)",1);
        else base : advise("too little data for a conclusion",0);
      end,

    advise: /* outputs the identification */
      func(m,c)
      begin
        setValue(MessageBox,'text,"It looks like you have" && m & ".
        \n Confidence level:" && c & ".");
      end,

    showSize: /* updates Size display to current SizeSlider value */
      func(n)
      begin
        setValue(Size,'text,n && "cm"); /* value shown as centimeters */
      end,

    _proto: protoapp, /* proto inheritance link */
  };

// Cap Shape, based on the protolabelinputline proto, is a child view of
// base view. It displays a label and an input line. Tapping the label
// shows a list of values. Tapping a value puts it in the input line.
Cap Shape :=
  { viewSetupFormScript: /* executed during view creation */
      func()
      begin
        // This should set base-relative view bounds.
        // The protolabelinputline proto that this view is based on
        // has a child view, entryLine, responsible for the input line.
        // This is how its slots are accessed:
        entryLine.viewFont := userFont10;
        entryLine.text := "";
        prevText := entryLine.text; /* save for undo */
      end,
    label: "Cap Shape", /* the label */
    labelCommands:      /* the values displayed */
      ["cylindrical", "conical", "bell", "convex", "flat", "concave"],

    textChanged: /* invoked when text in input line is changed */
      func()
      begin
        // Store user selection in slot in observations frame.
        base.observations.cap_shape := entryline.text;
        // Register this method's undo method with the system
        // so the Undo button will know what to do.
        AddUndoAction('undoTextChange,[prevText]);
      end,

    labelClick: /* invoked when user taps the label */
      func(unit)

      begin
        prevText := entryLine.text; /* save for undo */
        return nil; /* otherwise method is not passed */
      end,
    undoTextChange: /* the undo method for this method */
      func(t)
      begin
        entryLine.text := t;
        base.observations.cap_shape := entryline.text;
      end,
    _proto: protolabelinputline, /* proto inheritance link */
  };
// ...and so on. There are also frames for user input of Cap Surface, Gill
// Type, etc., but they look like this frame for Cap Shape.
// The frame(s) for input of Size, though, are a little different:
// SizeSlider, SizeLabel, and Size are views that implement a kind of slider,
// an alternative to the protolabelinputline used in Cap Shape.
// SizeSlider is based on the protoslider proto.
SizeSlider :=
  { viewSetupFormScript: /* executed during view creation */
      func()
      begin
        viewValue := 5; /* initial setting */
        / Display initial slider setting in Size view.
        base : showSize(self.viewValue);
        prevValue := viewValue; /* save for undo */
      end,

    // Slider settings are interpreted by interpolating between
    // minValue and maxValue. This app treats the result as centimeters.
    minValue: 0,
    maxValue: 24,
    changedSlider: /* invoked when slider is moved to a new position */
      func()
      begin
        // Store user selection in slot in observations frame.
        base.observations.size := viewValue;
        // Register this method's undo method with the system
        // so the Undo button will know what to do.
        AddUndoAction('undoValueChange,[prevValue]);
      end,
    trackSlider: /* invoked as slider is moved */
      func()
      begin
        // Display slider setting in Size view.
        base : showSize(self.viewValue);
      end,
    viewClickScript: /* invoked when user touches slider */
      func(unit)
      begin
        prevValue := viewValue; /* save for undo */
        return nil; /* otherwise method is not passed */
      end,
    undoValueChange: /* the undo method for this method */
      func(v)
      begin
        viewValue := v;
        base.observations.size := viewValue;
      end,
    _proto: protoslider, /* proto inheritance link */
  };
// SizeLabel, based on the protostatictext proto, labels SizeSlider.
SizeLabel :=
  { text: "Size",
    _proto: protostatictext, /* proto inheritance link */
  };
// Size displays the setting of SizeSlider numerically.
// It's based on the protostatictext proto.
Size :=
  { text: "",
    _proto: protostatictext, /* proto inheritance link */
  };
// View Size is accessible from Mushrooms.

// Advise is the button pressed to start the identification.
// It's based on the protopicturebutton proto.
Advise :=
  { icon: GetPictAsBits("Mushrooms", 1),
    buttonClickScript: /* invoked when button clicked */
      func()
      begin
        base : advisor(); /* fire up the identification engine */
      end,
    _proto: protopicturebutton, /* proto inheritance link */
  };
// Messagebox displays messages to user. Based on clParagraphView view class.
MessageBox :=
  { text: /* initial value */
      "Select the characteristics that best describe your find
      and tap the picture of the mushroom.",
    viewclass: 81,
  };
// View MessageBox is accessible from Mushrooms.

Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video