Chip's Tips for Developers

Contains coding, but not narcotic.

A synthesis of new capabilities in Synergy/DE version 9

October 6th, 2009 12:43:46 pm pst by Sterling Camden

As I threatened in my last post, I’ve combined a lot of my Synergy/DE version 9 work into a single library, which I have named “synthesis”.  Why synthesis?

  • It’s a synthesis of most of my explorations of what I can do with Synergy/DE version 9.
  • The programming model is a synthesis of object-oriented and functional programming.
  • The terminology synthesizes vocabulary from Lisp, Ruby, C++, .NET, and historical Synergy/DE.
  • It needed to start with "syn" to fit my tagging conventions here on ChipsTips.
  • It embodies my “thesis” on the future of Synergy/DE programming.

Included in the download below are all the sources, mixins, tests, documentation, and PVCS archives for the synthesis library.  You can also access the documentation online here.  As usual, this content is published under the Open Works License, so you’re free to pull it apart, modify it, and reuse it in any application, commercial or personal, open or closed source – but please retain credit for the author (and I’d appreciate a link back here).

In the root directory you will find setenv.sh and setenv.bat.  These are scripts to setup the environment variables used for building the library.  setenv.sh is a *nix shell script, and setenv.bat is a Windows batch file.  If you don’t want to use the PVCS archives, also set NOVCS=1 in your environment.  Next, you’ll see the following directories:

  • sources – here’s where you build the library, which ends up in the main directory.
  • mixins – include files for mixins
  • prototypes – you’ll need to create this directory to hold the dbh files
  • tests – build the unit tests here
  • documentation – full documentation, starting at index.html

The build assumes you have PVCS Configuration Builder, so if you don’t you will need to translate makefile.mak in the sources and tests directories to whatever build script you prefer.

Enhancements

One of my reasons for combining these modules into a single library was so I could add more enhancements without duplicating effort.  Here are a few that I have added already:

I’m just getting warmed up, folks.

Posted in SynergyDE | 1 Comment » RSS 2.0 | Sphere it!

Properties: just like public member variables, except when they aren’t

June 29th, 2009 1:34:43 pm pst by Sterling Camden

Many object-oriented languages provide an abstraction called properties:  data members that belong to a class instance, but whose public interface controls how those members can be manipulated via accessor methods.  In languages like C#, VB.NET, and Synergy/DE, the syntax for accessing properties is identical to accessing public member variables.  That’s convenient and intuitive – but misleading, because properties are methods, not data.

Usually that distinction has no practical consequences.  But just try passing a property as an “out” parameter.  Even if the property defines a setter method, the compiler complains “A property or indexer may not be passed as an out or ref parameter”.  That means that you can’t even pass an element of an ArrayList to an “out” parameter.  You can say:

myarray[0] = some_object;

but you can’t say

FillInTheObject(out myarray[0]);

My first reaction to this restriction was the same as that of BCS: Why doesn’t the compiler just generate a call to the setter method when the function returns?

A couple of potential reasons:

  1. Unless somehow the setter method could get passed into the exact statement(s) that modifies the parameter, you could introduce timing issues.  Another thread (or even a routine in the same thread) that has a reference to the property or the member data behind it could modify that data after the logical modification within the function, but before the function returns.
  2. Too much syntactic sugar leads to code diabetes.  When the compiler does too much for you, you get more overhead than you bargained for.

I’m not sure that either of these objections outweighs Caveat emptor.  After all, you currently can’t do this at all, so changing the behavior wouldn’t break any existing code.  Unless you’re in the camp that believes that the compiler should slap your hand whenever you do something it thinks you didn’t mean to do.  I’m not in that camp.

Why doesn’t Ruby have this problem?  After all, it has accessor methods for attributes (the equivalent of properties).  The answer is simple:  Ruby doesn’t support “out” parameters.  Like a good functional language, you must return values as the result of a function, not as an argument.  That constrains assignment to a distinct moment in the execution of the function (after it returns), and thereby eliminates the concurrency issue while simultaneously making the assignment apparent to the most casual perusal of the code.

So the real issue here is not that properties look like data while behaving like functions.  The real problem is that you shouldn’t use “out” parameters.

Posted in .NET, C#, Ruby, SynergyDE, Visual Basic | No Comments » RSS 2.0 | Sphere it!

Functional Let for Synergy/DE

April 23rd, 2009 9:41:15 am pst by Sterling Camden

Synergy/DE possesses a macro definition syntax that’s very similar to the #define syntax in C, but not quite as robust.  It doesn’t support macros that expand to multiple lines of code.  An individual parameter passed to a macro cannot be continued to the next line, either.  A macro cannot be used as a parameter to itself, nor within its expansion (as of 9.1.5).  But despite these limitations, I find macros to be a powerful tool for either simplifying my code or making it hopelessly obscure.  The basic Synergy/DE macro syntax is:

.define macroname(parameter, …) expansion

Wherever parameter is encountered within expansion it is replaced with whatever is passed to the macro in that argument position when it’s invoked.  In order to be recognized as the desired token within expansion, it must either be syntactically isolated as a token, or escaped with a back-quote (`).  Within strings, you use two back-quotes preceding and one following the identifier.  If you continue a macro to the next line, its expansion is also continued – which is why macros are limited to a single statement.  There is no syntax (yet) for saying, “this is a continuation of my macro, but I want it to be a new statement rather than a continuation of the same statement within the expansion.”

One problem that’s common to any parameterized macro system (be it Lisp’s, C’s, or Synergy/DE’s) is how many times an argument gets evaluated at runtime.  Macro invocation syntax looks like functional syntax, but how arguments get evaluated is quite a different ball game.  Consider the classic “max” macro:

.define max(val1, val2) fif(val1 > val2, val1, val2)

For a definition of the fif macro, see this post.  Now lets try using it:

account.fee = max(5.00, account.CloseQuarter() * 0.01)

Seems innocent enough, we’re going to close out the quarter, which returns the total, from which we’ll compute a fee of 1% of total activity, but not less than $5.00.  If max were a function, then each parameter would be evaluated once before invoking it.  But the max macro expands out into:

account.fee = fif(5.00 > account.CloseQuarter() * 0.01, 5.00, account.CloseQuarter() * 0.01)

which means that if 1% of activity is not less than 5.00, we’ll call account.CloseQuarter() twice.  Actually, because fif is implemented under the hood as a static method, we’ll always call account.CloseQuarter() twice.  Even if account.CloseQuarter() had no side-effects, it’s more than we intended to do.  I could also devise examples of macros in which an argument may not get evaluated at all.  This, too, can cause confusion when you’re looking at the code and thinking of the macro as if it were a function.

One way to solve this problem in Lisp is to use LET.  LET exists in many functional languages to allow you to define a scope for local assignments, but it can also be used to force single evaluation of macro parameters:

(defmacro mymacro (param1)  
  `(let ((,var1 ,param1))
      (maybe do things with ,var1)))

The macro generates a LET statement in the code, assigning the parameter param1 to a local variable var1, which only has the scope of the LET statement.  That assignment forces param1 to be evaluated once and only once within the generated code.  The LET then returns whatever the (maybe do things with var1) statement returns.  Even if there is a var1 declared in the surrounding code, the var1 referenced inside the LET will refer to the parameter’s value.  Other variables defined in the surrounding code may, however, be referenced and used within the macro (which isn’t hygenic, but is sometimes useful).

I have a dream

How could we do something similar in Synergy/DE?  We do have the ability to define a very local scope for variables in version 9, using the DATA statement — but there are at least two reasons why this won’t help us with macros:  (1) a DATA statement has to be on a line by itself, and we don’t have multi-line macro expansions (remember?), and (2) even if we did, we don’t have any way to return a value from a multi-statement block.   Probably the right way to solve this in the language would be to allow inline anonymous functions with access to the local scope (a la JavaScript and other languages with closures) and also provide a syntax for multiline macros.  We could dream, for instance, of something like this:

.define mymacro(param1) function, ^val
.&                          var1, i
.&                      proc
.&                          freturn maybe_do_things_with(var1) ;a macro that might use var1 or not
.&                      end(param1)  ;Invoke it, passing param1

By converting the macro into a function, you’d automatically evaluate arguments only once.  But that’s pie-in-the-sky – how about something I can use today?

Let my parameters go

The downloadable code below works with Synergy/DE version 9.1.5 and above, and provides a form of scoped LET.  The syntax is a bit more cluttered than I’d like, but it’s functional (pun intended).  For instance, you could define our mymacro example as:

.define mymacro(param1) let(assign(“var1”, param1), maybe_do_things_with(valueof(“var1”)))

and we can write a single-evaluation-safe version of the max macro as:

.define max(val1,val2) let(assign("val1",val1) && assign("val2",val2), fif((vv("val1") > vv("val2")), vv("val1"), vv("val2”)))

This version of max takes any two primitives (or Vars) and returns a Var containing the greater of the two values.  Both val1 and val2 will be evaluated once and only once, in their respective assigns.

The basic syntax for let is:

return_value = let(assignments, body)

where

return_value is whatever body returns.  It will have the same type as what body returns, so you may need to cast it.

assignments are one or more “assign” macros, joined with the logical AND operator (&& or .and.)

body is some expression whose result will be returned.

I lied just a bit when I said that the return value is the same as what body returns.  It will be one of the following, whichever is the best match for what body returns:

  • i (which covers boolean, int, and ^val functions)
  • decimal (which covers d and d.)
  • a (which also handles strings)
  • @Var (if it’s a Var)
  • @* (for all other object classes)

The syntax for the assign macro is:

assign(name, value)

where

name is the name of a let variable

value is the value to assign it

Under the hood

The assign macro expands into a static method named Lets.Set.  It returns a Lets object that is passed to let to maintain scope (let actually expands into a static method named Lets.Return).  The logical AND between two Lets objects causes the first one to maintain the scope of the second one.   Thus, we always use only one argument for all assignments, and the second argument is for the body.

The assignment of let variables is maintained in a static Hash of Stacks.  Each member of the hash is the stack of values for a given name.  An assign pushes a new value onto the stack for its name, and the let to which that assign belongs removes that value from the stack before it returns.  Within either a subsequent assign or within the body of the let, you can reference the topmost value on the stack for a given name via the valueof macro.

Since Stacks can only hold objects, if a primitive type is passed to assign, it is boxed in a Var.  The valueof macro (which expands into the static method Lets.Get) always returns an object, so I’ve provided a handy shortcut to cast it as a (Var): the vv macro.  vv(name) is equivalent to (Var)valueof(name).

What is It?

Another handy shortcut is the “it” macro.  “it” provides a reference to the most recently assigned value, without having to know its name.  “it” is also scoped to a let, so in the case of nested lets, it goes back to what it was in the outer let when the inner let returns.  To cast “it” as a Var, you can use the itv macro as a shortcut.

Why wouldn’t you know the name of the most recently assigned value?  Sometimes you don’t care, especially if you only need one temporary variable.  So, to generate a unique name, we have gensym, and as a shortcut for a let with only one assignment that uses gensym for its name, we have the genlet macro:

result = genlet(value, body)

Believe me, “it” will come in handy in that body.  In case you do want to inspect the name of whatever is assigned to “it”, there is the “itsname” macro to provide it to you.

I’ve provided quite a few tests in test_let.dbl.  These also serve as some good examples of usage.

Even though Synergy/DE doesn’t allow macros to be nested in their own parameters, I’ve worked around that for let and assign.  They aren’t actually parametric macros – they’re just text replacements for their static method names.  So you can have a let within an assign, and you can have a let within the body of a let.  The let variable scoping all works as you would expect.  You can also continue a statement to the next line in the middle of a let or an assign.

Back in the 1980s I stretched the DIBOL language (which at that time lacked integers, dynamic memory allocation, and recursion) to implement various DSL interpreters.  In the early 90s, I used the newly available xsubr routine to create a form of object-orientation by convention.  Ken Lidster and I subsequently designed a couple of prototypes for a true OOP implementation, which has finally come to fruition in Synergy/DE version 9.  Now once again, I’m pushing the limits of the language to enable the functional model.  Let us see where it goes from here.

UPDATE 2009-07-06: Change the namespace to “ChipsTips”, and included an updated version of Var.
UPDATE 2009-09-13: Updated for Synergy/DE 9.3

Posted in SynergyDE | 1 Comment » RSS 2.0 | Sphere it!

Stack class for Synergy/DE

April 20th, 2009 2:22:31 pm pst by Sterling Camden

The concept of a stack has been useful to computing for more than fifty years.  It represents a LIFO (Last In First Out) series of data elements.  Of course you can use a simple array as if it were a stack — but one of the ways in which object orientation can actually simplify programming (rather than hopelessly obfuscating it as usual) is by clearly representing useful abstractions as classes and members.

The downloadable code below contains a Stack class that extends the Synergy/DE ArrayList class (which is patterned on the .NET version), adding methods for treating it as a stack:

  • Stack.Push(object) will insert object at index 0 of Stack.
  • Stack.Pop() returns the object at index 0 of Stack, removing it (or returning ^null if Stack is empty).
  • Stack.Remove(object) removes the first occurrence of object from Stack, returning true if found, otherwise false.

That last method doesn’t fit the pure model of a stack, but I needed it for my own evil purposes.

Stack pushes new objects onto the front of the base ArrayList for one simple reason:  the Synergy/DE foreach statement will therefore iterate over the items in LIFO order.  In fact, because Stack extends ArrayList rather than containing one, you can use any ArrayList method on a Stack.  Thus, Stack[0] always accesses the top stack item, without removing it.  Stack.Count gives you the number of items.  Etc.

Beware of overusing stacks – often where they seem to be necessary, recursion provides a better model.  Recursion is essentially a stack push, iterate through the code, and pop.  But it not only pushes the local data, it also pushes your current location in the algorithm.  However, some cases of nested operations (like the one I’m currently working on) can’t readily be solved through recursion.  That’s when you should use a stack instead.

Putting together a Queue class will be left as an exercise for the reader (until I need one).

UPDATE 2009-07-06:  Changes namespace to “ChipsTips”.

Posted in SynergyDE | 1 Comment » RSS 2.0 | Sphere it!

Var class provides more flexible boxing of primitives in Synergy/DE

April 9th, 2009 12:35:09 pm pst by Sterling Camden

The object model in Synergy/DE version 9 treats primitive types as essentially different from classes — much like Java and C#.  Also imitating those two languages, Synergy/DE supports the practice of “boxing” primitives — wrapping a primitive type within an object.  That’s useful whenever you need to pass a primitive as a parameter where an object is required.  A common case is the ArrayList class, which stores a variable-length array of objects.  If you want a variable-length array of numbers or strings, then you need to box them:

myArrayList.Add((object)"hello world")

As you can see from the example, all you need to do to box a primitive is to cast it as an object.  To unbox, you cast the object as its primitive type:

writes(1, (a)MyArrayList[0])   ;Treating the first element of the ArrayList as alpha

If you use the wrong type on the unbox, you’ll get an “Invalid cast” error at runtime.  Unfortunately, there is no way to interrogate a boxed primitive for its primitive type, so you just have to know.  That even goes for decimal versus integer, so you’d better be aware that boxing the number 1 gives you a decimal object (which you must cast as (d), not (i)).  The only method for extracting the value that works for all primitive types is Object.ToString(), which gives you the string representation of the value.

I have my own reasons (which will be revealed in a later post) why I need more flexibility here, but I’ll bet some of you might find this useful as well.  The downloadable code below contains a set of classes in var.dbl to support a more type-agnostic boxing of primitives.  The only class you need to deal with is Var, which can be one of VarInt, VarDec, or VarAlpha.  When you cast a value as (Var) you’ll get an instance of one of those three classes — but you can treat it as an instance of Var for all practical purposes.  A Var can be cast as (i), (decimal), or (a) — no matter what its type.  Because of a compiler bug, I had to exclude (d) — op_Explicit thinks that d and decimal are the same type, but it won’t convert a d without precision into a decimal on the cast.  So if you need to use a d value, you’ll have to convert it to implied first.

Even though a Var remembers its type, it performs automatic conversions between types.  Casting a VarInt as (decimal) will give you a decimal equivalent of the integer value, for example.  Furthermore, Vars can be compared against one another — and also against primitives.  Thus:

(Var)1.0 == 1
(Var)1.0 == (Var)%integer(1)
(Var)"1" == 1

etc.  All of the comparison operators are supported, since Var includes the Comparable mixin.  For each comparison, if the primitive types do not match, then integer is promoted to decimal, and either integer or decimal is promoted to alphanumeric.

Vars also support the mathematical operators +, -, *, and /.  When operand types don’t match, integers are promoted to decimal.  Alphanumeric operations are a bit different, so they require some explanation.  Alphanumeric addition is string concatenation, and the other operand is promoted to alpha if needed.  Subtraction removes the first occurrence of the second operand from the first operand, and the other operand is promoted to alpha if needed.  Multiplication treats one of the operands as integer (converting the second one if both are alpha) and creates a new VarAlpha that contains a string of that number of replications of the alpha operand.  Alphanumeric division doesn’t make sense, so both operands get converted to decimal.

When converting alpha to decimal or integer, if an invalid digit is encountered then the result is zero (rather than throwing an exception).  Dividing by zero, however, still throws an exception (what would we return instead?).

Var also includes the Boolean mixin, so you can use any Var as a boolean expression.  Standard DIBOL rules apply:  integer and decimal are true if non-zero, alpha is true if non-blank.

Var supports unary negation, so -(Var)1.0 == -1.

You may wonder why I have a Var.Box method for an object parameter, which simply returns its parameter.  That’s so I can pass any type or object to Var.Box and get an object back.  Of course, in the case of an object parameter, that returned object will not support the same methods as a Var.  But the compiler will catch you if you try to use one on it, since it won’t be typed Var.  Duck typing of disparate classes is a problem for another day.

UPDATE 2009-04-22:  I updated the Functional If macro to implicitly handle Var without casting.

UPDATE 2009-07-06:  Changed the namespace to “ChipsTips” and added override for Object.Equal.

UPDATE 2009-07-13:  Changed alpha comparison to use string tests (.GTS., .LTS.) and added override for Object.ToString.

Posted in SynergyDE | 3 Comments » RSS 2.0 | Sphere it!

Boolean operations mixin for Synergy/DE

April 6th, 2009 11:48:39 am pst by Sterling Camden

The new class syntax in Synergy/DE version 9 allows you to define a whole host of operators for your own classes.  These include the boolean operations of testing for True, LogicalNot (! or .not.), LogicalAnd (&& or .and.), LogicalOr (|| or .or.), and ExclusiveOr (.xor.).  Once you’ve defined these in your class, you can write statements like:


if (myobject) ...
if (!myobject)...
if (myobject1 && myobject2) ...

etc.

Unfortunately, just defining op_True doesn’t automatically make the other operations default to anything.  Using the .not. operator (!) on an object that only defines op_True will give a compiler error, because op_LogicalNot is not defined.  Likewise for the others.  Furthermore, in the case of the binary operators LogicalAnd, LogicalOr, and ExclusiveOr, you not only need to define operations in which both operands are members of your class, but also any other combination with different types of operators.  Otherwise:


if (myobject && some_function_that_returns_an_integer())

will get a compilation error.  Likewise, you also need to define different orders of operands.  It turns out that to get full coverage for primitives, you need to define operands for your class and integer, decimal, and alphanumeric (integer also handles boolean, and alphanumeric also handles string), in both orders.  Including the case of two operands from your own class, that’s seven versions of each binary operator!  There are three such operators, plus op_True and op_LogicalNot, for a total of 23 methods just to implement a consistent test for truth.

The downloadable code below provides an include module that will define all those methods for you, using the mixin pattern that I provided earlier.  Within your class definition, you must define BOOLEAN_CLASS as the name of your class (since Synergy/DE does not, as of version 9.1.5, offer any compile-time symbol for the class under compilation), then include Boolean.dbl.  The generated methods will call a member method named “truth” to determine whether your object is true or not — so you need to supply just that one routine.  For example:

public class myclass
.define BOOLEAN_CLASS myclass
.include "Boolean"

method truth, boolean
proc
    mreturn whatever_makes_me_true
endmethod

endclass

Given the code above, you can use an instance of myclass in any boolean expression except one in which the other operand is a member of a different class.  In the latter case, you still have to anticipate the operand mix and either add your own operators or invoke a member of the other class that returns a primitive instead.

 

 

 

Posted in SynergyDE | 2 Comments » RSS 2.0 | Sphere it!

Scoping a wait cursor in Synergy/DE

May 5th, 2008 11:23:28 am pst by Sterling Camden

On Windows, Synergy/DE provides access to the mouse cursor via the WP_CURSOR subfunction of W_PROC. Because this API is purely imperative, managing a wait cursor properly becomes a recurring problem in non-trivial applications. Typically, it begins like this (in pseudo-code):

set wait cursor
do lengthy process
clear wait cursor

But what happens if “do lengthy process” throws an exception that’s caught outside this code? You’ll skip right over “clear wait cursor” and leave the user unable to click anything. What you’d like to do is more like this:

begin
  set wait cursor
  do lengthy process
end

… and have the cursor reset whenever and however you exit the code block.

The downloadable code below demonstrates how to do this with the new object syntax in Synergy/DE version 9. The WaitCursor class sets the wait cursor in its constructor and resets it in its destructor. So you can code it like this:

begin
  data wait, @*, new WaitCursor()
  do lengthy process
end

The data statement declares a variable that has the scope of its containing begin-end block. The variable wait is declared as an object (@*). We could have more precisely declared it as @WaitCursor, but that’s just more typing (pun intended). It’s initial value is returned by the “new” expression, creating a new WaitCursor object. This invokes its constructor, setting the wait cursor. When wait goes out of scope, the last reference to the object is lost, and the destructor is invoked and the cursor is reset — no matter how you get out of the code block. If you want to preserve the cursor instead, you can just pass a reference to the object out of the block in another variable, and clear that variable when you no longer want the wait cursor.

But what happens if you have nested operations that, agnostic of each other, each want to set a wait cursor? When the inner one finishes, it will reset the cursor to normal. That’s not what you want at all. I solved this by adding a static counter to the class, so we only reset the cursor if our count of active WaitCursor objects gets back to zero.

Of course it’s possible that other code is manipulating the cursor without using our class. That could definitely interfere with our scheme, and should be avoided if possible. One common case is the set of message box routines in the UI Toolkit (U_MESSAGE, U_MSGBOX, U_WAIT). These routines always reset the cursor to the normal pointer so the user can use the mouse to click the buttons. If you want to have the cursor go back to what it was before these routines were called, then you’ll probably need to wrap them in your own version that saves off the current cursor state, calls the original, and then restores the cursor.

Posted in SynergyDE, UI Toolkit, Windows | 2 Comments » RSS 2.0 | Sphere it!

Mixin modules for Synergy/DE

April 26th, 2008 5:26:39 pm pst by Sterling Camden

The new object-oriented syntax for Synergy/DE version 9 is pretty closely modeled after C#, because Synergex plans to create a CLR version of the language and they don’t want the syntax supported on the various platforms to differ by much.

That means, of course, that when we hear “Multiple Inheritance” we are supposed to close our eyes and ears, hold up a cross, load up with silver bullets, and make it go away.

And it’s true that MI has probably caused more problems than it solves in languages that support it. Sometimes the “need” for MI is really a symptom that you should have used composition instead of inheritance (the object “has a” <whatever you’re deriving from> instead of “is a”).

But sometimes MI can be useful. There are abstractions that apply to an object itself that cut across normal inheritance lines. For instance, there are lots of classes that you could compare for equality in some manner more abstract than comparing the identity of the objects. So, you’d like to overload the ==, !=, >, <, >=, and <= operators with methods that compare the objects in some custom way specific to the class. But why write all of those methods for every class that needs them? If each class had one method to compare the objects and return a value indicating less than, equal, or greater than — then all the operator overload methods could call that method, and they’d be essentially identical for each class. If you could inherit that code from a common source, you’d save yourself a lot of typing — and possibly a lot of paste and modify errors.

This idea comes to me from Ruby, which has the concept of “mixin modules” — code that can be added to any class to extend its functionality, without regard for its inheritance hierarchy. It creates a form of MI without all the screaming.

The downloadable code below includes a module in Comparable.dbl. Don’t compile that module, but if you include it within a class definition it will define the comparison operator overloads for you. These methods assume that your class contains a “compare” method that it can call to determine the appropriate result for each operation. Because Synergy/DE is a statically typed language, you also have to give it the types to work with, via .defines. So, for instance, in Person.dbl we have:

class Person
.define COMPARE_CLASS Person
.include "Comparable"

This creates the op_Equality, op_Inequality, and related methods that call the compare method to compare Persons. How do you compare Persons? Why, by their likability, of course:

protected static method compare, int
in req obj1, @Person
in req obj2, @Person
proc
    using obj1.likability select
    (< obj2.likability), mreturn -1
    (> obj2.likability), mreturn 1
    endusing
    mreturn 0 ;==
endmethod

You might expect that the compare method will look pretty similar from class to class as well, so I also added the ability to generate the compare method automatically to compare a specific member (data or method). Just define COMPARE_MEMBER, as shown in SalesPerson.dbl:

.define COMPARE_CLASS SalesPerson
.define COMPARE_MEMBER annual_sales ;Compare their annual sales
.include "Comparable"

It’s also possible to have different comparisons for mismatched operand types. Just define COMPARE_TYPE2 (and optionally COMPARE_MEMBER2), as when comparing a SalesPerson to a Person.

.define COMPARE_TYPE2 @Person
.undefine COMPARE_MEMBER ;Don't generate "compare"
.include "Comparable"
protected static method compare, int
in req obj1, @SalesPerson
in req obj2, @Person
proc
    mreturn -1 ;a SalesPerson is always less than a Person
endmethod

You may wonder why the second argument needs to have the “@” included in the definition, and why I called it COMPARE_TYPE2 instead of COMPARE_CLASS2. This is so you can define comparisons against primitive types like “int” or “string” as well.

The Programmer class in Programmer.dbl shows how the COMPARE_MEMBER definition can resolve to a member function:

.define COMPARE_CLASS Programmer
.define COMPARE_MEMBER geekQuotient()
.include "Comparable"

private method geekQuotient, decimal
proc
  begin
    data quotient, decimal
    quotient = (num_languages * num_languages) * years_coding
    if (ever_used_VB) quotient /= ^x(dead)
    mreturn quotient
  end
endmethod

See testComparable.dbl for examples of usage.

Naturally, you can use the same technique to create comparison operators for any class. As long as your comparison operators have complementary meaning (which I would recommend), you should never have to write your own overloads for them again.

You can expand on this idea to create other inheritable traits that cross inheritance boundaries. For instance, in Ruby, anything that acts vaguely like an array or collection includes “Enumerable” to get a whole host of useful methods.

So SalesPerson “is a” Person, but it also “is a” Comparable object. So is Programmer. Any other class into which you include Comparable also “is a” Comparable, without necessarily being a Person. We’ve achieved MI (Multiple Inheritance) via MI (MixIns), blurring the line between inheritance and composition.

UPDATED 4/28/2008 to add support for comparisons against primitive types, and to automatically undefine COMPARE_TYPE2 and COMPARE_MEMBER2 at the end of Comparable.dbl.

UPDATED 4/6/2009 to automatically generate operators for swapped operand types when COMPARE_TYPE2 is defined.  Thus, (myobj == 2) and (2 == myobj) will have identical results.  Note, however, that if the first operand’s class defines a suitable operator, that one has precedence.

Posted in SynergyDE, Wildly popular | 6 Comments » RSS 2.0 | Sphere it!

Fun with global objects in Synergy/DE

April 11th, 2008 1:04:17 pm pst by Sterling Camden

I’m not a big fan of globals, but sometimes they make sense.  If you have a singleton object that’s needed by a lot of different code, you don’t want to have to pass it around everywhere.  Unfortunately, in Synergy/DE you cannot declare an object in global scope (common or global data section) — it’s disallowed by the compiler.  Tod Phillips, a Synergy/DE consultant, asked me about this limitation today and whether I knew any way around it.  Well, it turns out that you can declare static members in a class to do the same thing.  I sent him the following example, in which “wife.wilma” is the static member accessible from anywhere:

namespace fred
    public class wife
          public static wilma, @wife ;Here's our "global" object


          public method nag, string
          proc
              mreturn "Now fred..."
          endmethod


    endclass


    main test_nagging
    proc
          u_start
          wife.wilma = new wife()
          barhop()
          u_finish
    end

    subroutine barhop
    proc
          u_message(wife.wilma.nag())
          xreturn
    end

 

endnamespace

To which Tod responded:

That’s great! (and I mean both the snippet and the subject matter)

I assume that I could also add :

                Public static betty, @wife
                Public static Teresa, @wife
                Public static…, etc.

without any data collisions/overwrites from one wife to the next? (This will be very important for the Flirt() and Liaison() methods).

-Tod

Me:

Absolutely.  You could even have a static array of type wife – though I’d recommend using the class girlfriend instead of wife.  Both classes should be derived from significant_other, and girlfriend implements the methods you mentioned more effectively with fewer side-effects when handling multiple instances.  You might also want to make those private, accessed via discreet methods.

Tod:

Uh-oh…

Be careful with the Bigamy() method. There’s a hidden system call that alerts the Feds. So I think you’re right re: girlfriend class – it doesn’t seem to contain any such calls, though the Gossip() method is so convoluted there’s no telling what will happen if it fires with other Girlfriend objects instantiated in adjacent memory…

Me:

True – that would likely cause a failure, since Synergy doesn’t support multiple threads.

Posted in SynergyDE | 4 Comments » RSS 2.0 | Sphere it!

Associative array (hash, dictionary) class for Synergy/DE

March 16th, 2008 1:29:29 pm pst by Sterling Camden

One of the more useful features found in many scripting languages is the concept of an associative array.  It goes by the name of a hash in Perl and Ruby, or a dictionary in Python.  A hash lets you associate key values with data, and later retrieve a datum using its key — often via a syntax similar to an array element reference.  On this lazy Sunday morning it occurred to me that Synergy/DE could benefit from such a facility.

The downloadable code below contains a Hash class that acts as an array of untyped objects with an alphanumeric index.  You can control the case-sensitivity of the key by setting the CaseSensitive property (which defaults to false) before adding the first item.  Because this uses the new Synergy class syntax, it requires at least version 9 of Synergy/DE.

Usage:

myhash = new Hash()
myhash['key'] = anyobject
anyobject = myhash['key']

If a key is not found, then ^null is returned.  Thus, to clear an item, just set its entry to ^null.  This hash does not reclaim any array entries, so it will eventually grow to the size of all keys that have ever been used to store a value.

As you can see in the code, the class uses an internal ArrayList to store the object references, and associates the keys with array indices using the built-in Symbol Table functions in Synergy/DE.  If the Symbol Table API could take objects as associated data then we wouldn’t need this class at all — but it doesn’t.  The Symbol Table API can only take primitive types for data, so we have to introduce this extra layer of reference in order to support objects.

Synergy/DE’s static typing gets in our way in usage as well.  Because the elements are typed as System.Object, client code must cast any returned element to the type expected.  If a primitive type is to be stored in the hash, then it must be cast as an object in order to box it.  Even though you can store a hash as an element of a hash to created nested arrays, you cannot dereference a nested element using a single statement, because you must first store the nested hash element into a variable of type Hash.  And we don’t get any benefit at all from static typing here.  Of course, you could create a derived class of Hash that expects and returns a specific object type.  That would better be a job for generics, which have not yet been implemented in Synergy/DE.  Personally, I’d like to suspend static typing altogether for a class like this, but that’s just the Ruby talking.

UPDATED 3/22/08: Added an iterator and Count property.

UPDATED 7/6/09: Changed the namespace to “ChipsTips”.

UPDATED 9/9/09: Corrected iterator to use base-zero indexing.

Posted in SynergyDE, Wildly popular | 12 Comments » RSS 2.0 | Sphere it!

ElapsedTime class for Synergy/DE

January 7th, 2008 2:15:57 pm pst by Sterling Camden

For all of you Synergistas out there just getting familiar with the new object syntax in version 9, I’ve provided a simple but useful example of how to use a class to encapsulate reusable functionality (translate: make your life simpler).

When tuning code for performance, it’s often useful to be able to get the elapsed time of a specific operation.  Sure, you can use the Unix “time” command, but that gives you the execution time for the entire process.  Usually you want to narrow it down to a specific section of code, and you might even want to subtract out overhead caused by looping structures or other code that’s required just to get to the code in question.

Enter the ElapsedTime class.  When you create a new instance of this class, it makes a note of the current time and date.  You can reset the starting time at any time by calling its “reset” method.  When you want to find out how much time has gone by, you can either query the “nSeconds” property for a decimal value, or call the “ToString” method to get a formatted result.  For example:



import CamdenSoftware.ChipsTips

record
        timer ,@ElapsedTime

proc
        timer = new ElapsedTime()         (do something here you want to time)         writes(ttchn, "Elapsed time " + timer.ToString())

To subtract out additional overhead, set the object’s “Overhead” field to the number of seconds to subtract from the overall time.  See the example program in the download below showing how to time a looping structure and then use that result as the overhead for timing operations within the loop.

Posted in SynergyDE | No Comments » RSS 2.0 | Sphere it!

Better Tag Cloud