value

  Evaluation of handlers using value()
handlers value




  Evaluation of handlers using value()
> -- simple handler
> 
> on getTextHandler()
>   return "doo"
> end
> 
> -- in Message Window
> thisVar =  value ("getTextHandler()")
> put thisVar
> -- "doo"> 


From: Stephane Comeau <scomea@microsoft.com>
Subject: RE: <lingo-l> Evaluation of handlers using value()

Cool, eh?

I use this to help in localization.  

For example, say you have a string to display that goes something like "At
your weight, [activity x] burns [y] calories per minute". Constructing
complex sentences programmatically can vary a lot by language. To make this
easily localizable I create an external cast called "lclstrng" which is
swapped depending on the language version, all strings that change by
language go there.  

In this cast I create a field castmember, in this case "activity output"
which contains the following string:

"At your weight," && lspParams.activityLong && "burns" && lspParams.cals &&
(getCals(script "activity output",lspParams.cals)) && "per minute."

So in my non-localized scripts that are internal to my dcr when I need to
recover this string I call a standard handler:

on sysstrGetLocalized strID, lspParams
  strBaseString = member(strID, "lclStrng").text
  strBaseString = value(strBaseString)
  return strBaseString
end 

In this case the call would look something like:
strDisplay = sysstrGetLocalized("activity output", [#cals:10,
#activityLong:"cycling"])

So in the sysstrGetLocalized handler I get the string from the field and do
a value call on it.  Now note that in the field is a function call
("getCals()"), it could point to any script but in this case it points to a
handler called "getCals" on the field castmember itself.  This keeps all the
details of creating this localized text block neatly packaged on one
castmember.  The "getCals" handler is a script on the field castmember and
looks like this:

on getCals me, strCals
  if strCals = "1" then
    returnString = "calorie"
  else
    returnString = "calories"
  end if
  return returnstring
end

It simply returns the plural form of the word calorie based on the value of
strCals.  Now the interesting thing is where "strCals" comes from - related
to what zac points out that you can call handlers when you call "value" on a
string, you can also use variables that are in scope at the point where you
execute the "value()" statement.  

Where this comes in very useful is that if I need to make this work in
German or Japanese and find that the sentence structure is radically
different I don't need to radically change anything in my program outside of
this one castmember. 

Steph


> Expressions are evaluated (boiled down) until there is ultimately one value 
> that it equals.  It is that value that an IF statement looks at.  So, (A=B) 
> will evaluate to TRUE or FALSE (1 or 0), boolean expressions (A and B) or 
> (C and not D), evaluate accordingly, and so forth.  Ultimately, everything 
> returns a value.
> 
> So, when you call the "value" function, you are forcing the evaluation of 
> an expression.  If the expression consists solely of a handler, then so be 
> it.  I think you're misled by the more typical use of it to perform a 
> conversion, but that's not what's really going on.

This is a really interesting thread, and Stephane's example, though
complex, is a good real world example (even if I did find the
description a bit difficult to follow).

I've been thinking a lot about 'evaluation' because I have been using
Applescript more and more in the last year. Superficially, Applescript
resembles pre-dot-syntax lingo, (so I hope this will not drift too far
off topic) and what interests me is the way that everything in
Applescript really does have a value in a way which it does not in lingo.

To explain: Despite what Tab says, not everything in lingo returns a
value, even if, under the hood, the interpreter relies on the principle
that it does.
 
Another way to put this is to say that 'the result' does not always
contain a reference to the result of the most recently coded action.
i.e. Not everything returns a value _to lingo_, even if it does return a
value to the interpreter. 

Let's take a high level example, the 'open window' command. We all know
how it works, and somewhere along the line, there is a lower level
mapping which 'returns' a window pointer to the player engine. Only
after this has happened can you start to 'tell' things to the window. 

Now if the lingo evaluator were a little more sophisticated, we'd have

tell (open window "xyz")
  -- some messages
end tell

So 'open window' would not only set up a reference in the window list,
it would also 'represent' that reference. I seem to remember this is
called 'functional programming' (correct me if I'm wrong) and is quite a
paradigm shift away from seeing commands and functions as seperate kinds
of entity.

I've been really pleasantly surprised by the way that Applescript
supports this kind of syntax. This was really brought home to me when I
wanted to write an Applescript which copied any Quicktime legible image
file to the clipboard without having to open an image editor. The
(excellent) freeware scripting addition 'Akua Sweets' provided the most
significant keywords, but what amazed me was the elegance of the final script:

clip (the image from (choose file)) as picture

OK I ended up adding a few lines to handle errors, but this single line
did the most important part of the work. Astounding! Of course, the line
itself evaluates to the same picture data which ends up on the
clipboard, so the entire line could be used as a parameter for yet
another level of nesting. 

This hints at Applescript's ancestor, LISP, which treats every line of
code as a list, nested or otherwise, and every list as a function to be
evaluated. Code therefore plays the role of data, and vice versa, in
different contexts.

Lingo, of course, shares this ancestry, which is most obvious when you
work with lists and the value() function, but when I see the Applescript
evaluator at work, allowing entire 'parent' scripts to be embedded in
birthing calls to those same scripts, it makes me realise how far away
Lingo is in this area.

Even so (and I've mentioned this before) you can, in Lingo, get some of
the way by using fields which contain 'pointers' to other fields, which
can contain pointers to other fields and so on. These fields can also
contain function calls to custom functions, or even to lingos own
properties, so that simply evaluating the field gives you an up to date
dynamic data structure:

FieldA:
[value("FieldB")]

FieldB:
[value("FieldC") & "]

FieldC:
[sprite(rollover()).member.name]

Imagine what would happen if you evaluate fieldA in this simple example.
More complex and useful equivalents could be constructed (as Stephane suggests).

I have found this extremely useful when making
'getPropertyDescriptionList' dialog boxes, which, as we all know, are
not particularly dynamic. I sometimes use evaluated fields to construct
'range' lists intelligently, and also use fields as raw parameters in
the property dialog for behaviors so that I can feed more complex data
to the behavior without the property dialog getting too big. One obvious
(and very simple) example is a 'path' behavior which searches for any
field which contains a list of points. If it can't find any, you can
just enter a string which will get evaluated on beginsprite. If the
evaluation is successful, (i.e. if the result is a list of points), the
behavior does its thing.

The flaw here is having to remember that your behavior may be connected
to one or more other field cast members, (behaviors should be able to
contain more than one cast member because it would solve this problem)
but it's an interesting approach if you take a little care.

What would make this kind of programming even more powerful would be if
'setaprop' and 'getaprop' worked with Director's own highlevel objects
(members, sprites etc.), so that evaluating a field could actually have
broad side effects. 

Well, as luck would have it, the XDK example lingo Xtra 'DrAccess'
actually provides functions to do this:

playerProp object me, string prop -- Get player prop
setPlayerProp object me, string prop, any value -- Set player prop

movieProp object me, string prop -- Get movie prop
setMovieProp object me, string prop, any value -- Set movie prop

castProp object me, any cast, string propName -- Get cast prop
setCastProp object me, any cast, string propName, any value -- Set cast prop

castMemProp object me, any castmem, string prop -- Get castmem prop (cast#1)
setCastMemProp object me, any castmem, string prop, any value -- Set mem
prop (cast#1)

scoreSpriteProp object me, int chanInd, string prop -- Get score sprite prop
setScoreSpriteProp object me, int chanInd, string prop, any value -- Set
score sprite prop

Apart from the limitation that cast member properties can only be set in
the first castlib, this is a killer set of functions from an essentially
freeware Xtra.

DrAccess also provides this neat one too:

callACommand  object me, symbol otherHandler, * -- call another command
(no result) handler

...and that * means 'zero or more parameters' so you can even embed 'do'
commands in parameters to the 'value()' function! (Expect some crashes
if you attempt anything weird). I haven't yet put together a set of
fields which generate a parent script and instantiate it using a single
evaluation call, but it would not be too difficult. 

Home shock + cgi Bits 'n pieces Director Lingo ShockLets Contact