Chip's Tips for Developers

Contains coding, but not narcotic.

Swapping two variables in Synergy/DE

July 3rd, 2009 9:19:44 am pst by Sterling Camden

Swapping the contents of two variables is a common programming task that should be easy to perform.  In scripting languages of the Perl tradition it’s dead simple, using simultaneous assignment:

Perl:  ($a,$b) = ($b,$a);
Python:  (a,b) = (b,a)
Ruby:  a,b = b,a

Common Lisp has a macro for the purpose:

(rotatef a b)

Most other languages require more than one statement.  For instance, in C (assuming that a and b are integers):

{ int swap = a; a = b; b = swap; }

which isn’t bad – after all, you could define it as a macro (although you’d have to make the type an argument as well if you wanted to support multiple types).

In Synergy/DE, you can’t put it all on one line, so you can’t define it as a macro:


begin
  data swap, int, a
  a = b
  b = swap
end

Well, actually, now you can define it as a macro, by using Let and Progn.  The downloadable code below does just that, in an include file named swap.def.  The following macros are defined therein:

swap(obj1, obj2, type) – swap two objects, casting as type.
swapi(int1, int2) – swap two integers
swapd(dec1, dec2) – swap two decimal values
swapa(a1, a2) – swap two alphanumeric values (or strings)
swapv(var1, var2) – swap two Vars

Note that you can swap different types as long as both the variables and the objects they contain are compatible with the specified type.  The type you specify will be used to cast both operands, so it can either be a common ancestor or one of the objects has to provide an op_Explicit conversion.  In the case of primitives, “type” will specify the type of conversion you want performed, via Var.

The “type” parameter must be enclosed in parentheses, if it is a type.  If parentheses are omitted, type will be treated as a function, method, or macro name, to which will be passed an object parameter (either a Var or some other class).   This gives you a way to insert your own conversion routine if you need it.  Or you can omit type entirely, if both operands are just object variables (@*).

The test routine test_swap.dbl provides some tests, using assertions.  As you can see from the commented out code at the end, the swap macro does not work for properties (including indexer properties).  That’s because Synergy/DE does not return a value from an assignment to a property.  This issue has already been reported as tracker 23837, so hopefully it will be fixed soon.  Because of that limitation, you can’t use these macros to swap elements of an ArrayList.  But I have even bigger plans for extending ArrayList that will include a method for swapping elements, among other things.

UPDATE 2009-09-13: for Synergy/DE 9.3

Posted in SynergyDE | 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!

Better Tag Cloud