The Gamma language has the capability to generate an event when a variable is modified in any way. A variable that can trigger an event is called an active value. The code that is executed when the variable changes is called the set expression that is effectively a callback.
The add_set_function permits attaching an
expression to any defined variable. (It is an error to attach a set
expression to a constant symbol.) When the value of a symbol changes, either
by being declared within a sub-scope or by being explicitly changed using
=, :=, --, or
++, the Gamma engine checks the symbol for the
existence of an associated set expression. If a set expression exists, then
it is executed directly after the value of the symbol is changed. This set
expression can be any valid Gamma code, and is evaluated within its own
sub-scope. The symbols value, previous
and this are defined within this sub-scope to be the
current value of the symbol, the previous value of the symbol, and the
symbol itself respectively. If an active value causes another active value
to change, then that new active value's set expression is also evaluated.
This provides a very simple and powerful means by which forward chaining
algorithms such as those in expert systems can be implemented.
Active values provide a very powerful way of constructing an event-driven application with a high degree of cohesiveness. Often, there is some functionality that is related to a derived variable, not an event itself. The function can be attached to that internal variable, decoupling it from the event, and generating clearer and more concise code. In the following example, we wish to attach an action to an alarm condition, which in turn is generated by a change in a measured variable (e.g., the temperature). The # sign protects an expression from evaluation, causing it to be treated as a literal. (We discuss Deferring Expression Evaluation in more details later, in the chapter on Advanced Types and Mechanisms).
Gamma>temp = 35;35Gamma>hi_limit = 40;40Gamma>function check_temp (tp) {if (tp > hi_limit) alarm_hi_on = t; else alarm_hi_on = nil;}(defun check_temp (tp) (if (> tp hi_limit) (setq alarm_hi_on t) (setq alarm_hi_on nil)))Gamma>function print_alarm () {if (alarm_hi_on == t) princ("Alarm is on: Temp is over ", hi_limit, ".\n"); if (alarm_hi_on == nil) princ("Alarm is off.\n");}(defun print_alarm () (progn (if (equal alarm_hi_on t) (princ "Alarm is on: Temp is over " hi_limit ".\n")) (if (equal alarm_hi_on nil) (princ "Alarm is off.\n"))))Gamma>add_set_function(#temp,#check_temp(temp));(check_temp temp)Gamma>add_set_function(#alarm_hi_on, #print_alarm());(print_alarm)Gamma>temp = 38;Alarm is off. 38Gamma>temp = 39;39Gamma>temp = 42;Alarm is on: Temp is over 40. 42Gamma>