Do What I Mean

Quasi (0.8) variable substitution has, as the Americans would say, “gotten smarter”.  What an odd language they do speak.

Anyway, for a good while, there have been two forms of variable substitution in Quasi.  Given a variable called a, you can write either $a or $(a).  Example:

>>>a = "*.py"
>>>os ls $a
['bag.py', 'bomreader.py', 'fix.py', 'floofinstall.py', 'grabbit.py', 'listproxy.py', 'mutabilitytest.py'...

or

>>>os ls $(a)
['bag.py', 'bomreader.py', 'fix.py', 'floofinstall.py', 'grabbit.py', 'listproxy.py', 'mutabilitytest.py'...

The original idea was that the short form (no parentheses) would be for simple identifiers, while the longer form allowed the parser to recognise Python expressions and the like.  That’s still true, so if you wanted to do something clever with a list of filenames, you might write:

os ls $(map(lambda x: x.split('.')[0]+'.pyc', files))

…and that’d all work.  In the context-centric Quasi way of looking at the world, the $() construction escapes out of the os context back into Python.

However, the short form now has added intelligence (“New! Improved! With added context-awareness!”).  Since one can always write $(a) instead of $a, there’s no real downside to this; it addeth, but it taketh not away.

In general, I’m not a fan of languages that try to do what they think you want (*cough* Perl *cough), as opposed to requiring you to state it explicitly.  Explicit is better than implicit, in fact.  But context-sensitive substitution is plain Interesting, and has a certain “cool!” factor to it, as much as a mundane subject like coding ever can.

Consider this; I have the result of a SQL query in variable res.  In Quasi fashion, it’s a self-describing object, an instance of quasi.Bag() which records the column names (as keys), the values (as, er, values) and allows dict or object semantics to access them.  Let’s say that it has the effective value:

{ "ColumnA" : 1, "ColumnB" : 2, "ColumnC", "Eric" }

Now, here are some SQL statements with clever variable substitutions and their results.

Ben@KANDINSKY2 ~/python
$ ./quasi.py
Welcome to Quasi, the multiple-context Python Shell.
    (c) 2004 Ben Last, and released under a BSD license.  Enjoy.

Version 0.8

>>>res = quasi.Bag()
>>>res["ColumnA"]=1
>>>res["ColumnB"]=2
>>>res["ColumnC"]="Eric"
>>>
>>>#Check the values
>>>res
(1, 2, 'Eric')
>>>res.keys()
['ColumnA', 'ColumnB', 'ColumnC']
>>>
>>>#use res as a set of column names
>>>explain `sql select $res from MyTable`
"select 'ColumnA','ColumnB','ColumnC' from MyTable"
>>>
>>>#use res as a set of values
>>>explain `sql insert into MyTable set $res`
"insert into MyTable set ColumnA=1,ColumnB=2,ColumnC='Eric'"
>>>
>>>#use res as both
>>>explain `sql update MyTable ($res) values ($res)`
"update MyTable ('ColumnA','ColumnB','ColumnC') values (1,2,'Eric')"
>>>

I think that’s rather excellent.  SQL is just regular enough that, by looking backwards along the command from the point where the variable substitution occurs, the parser can figure out how to insert the variable.  It also works (in a different way) for the os and shell contexts – strings get quoted and lists get comma-separated on Windows.  The explain command (as in the examples above) also lets you see what a substitution will do, without having to actually do it.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s