Info on Smalltalk DSLs or Metaprogramming...
Rich Warren
rwmlist at gmail.com
Fri Aug 18 10:35:20 UTC 2006
On Aug 17, 2006, at 6:44 AM, Ramon Leon wrote:
>> All my DSL examples are in Ruby.
>>
>> For DSL's, Ruby's syntax seems slightly more flexible than
>> Smalltalk (or maybe I just know how to abuse it more).
>>
>
> Might I suggest that Smalltalk itself, isn't so much a language, as
> it is a
> notation for creating DSL's. Smalltalk for example, models
> predicate logic,
> with simple objects and blocks, no special keywords such as
> if/unless/do/while are necessary. Ruby can't do this, and requires
> special
> syntax in the language, due to its inability to pass multiple
> blocks and its
> lack of Smalltalk style keyword selectors. I'm curious where you
> see Ruby's
> syntax being more flexible?
>
>
Before I get too far into this, let me again point out that my sense
that Ruby handles DSLs (as I am currently thinking about them)
possibly better than Smalltalk may simply be because I am more
familiar with Ruby syntax than Smalltalk. And because I have several
good examples that give clear examples of different methods for
building Ruby classes/methods/etc. to support DSLs.
So, please, please, please. If anyone knows of any good examples that
show how to do these things in Smalltalk, please let me know. That
might give me a better handle on how to approach this in Smalltalk,
and might just win me over.
First off, I'm talking exclusively about using internal DSLs here.
That is, I will be using the natural features of a general purpose
language. I'm not talking about building a full-fledged parser/
interpreter. However, I do want to be able to read in and interpret
arbitrary text files at runtime.
I think the approach should be similar in both the Ruby and Smalltalk
case. Take a line of text. Evaluate it as code in a context so that
the individual words in the line get interpreted as objects/messages/
methods/arguments/whatever.
If this is the case, then I can see a few places where Ruby would be
simpler. For example, if you're executing text as code within an
object, you don't need 'self' when referencing methods. As a result,
most Ruby DSLs execute inside objects--I suspect Smalltalk DSLs don't
do this.
To me, this seems to imply that in Smalltalk, the first word on each
line will typically be a class, followed immediately by either a
class method or a constructor. Ruby DSLs can do this as well, but
they can also start immediately with a method call on the containing
class. That, potentially, gives Ruby a considerable bit of extra
flexibility.
Second, in Ruby methods that take arguments do not need to end in a
colon. This can help keep the DSL syntax clean, particularly for
single-argument methods. Ruby methods can also have default values
set for the parameters (which I don't believe is available in
Smalltalk), and if the final parameter is a hash, you can use the
simplified hash syntax.
On the smalltalk side, I suspect cascading and the separate selector
for each argument could produce highly readable DSL syntax--but I'd
really like to see a few demo examples to get an idea of how it all
fits together.
There may also be ways of doing things in Smalltalk syntax that could
make DSLs easier--things that I don't know, or that I'm just not
thinking about. For example, I don't see how avoiding if/unless/do/
while really gains anything (especially since 'if' is arguably more
human readable than 'ifTrue:'). I also don't see a big gain in
passing single blocks vs. multiple blocks. I doubt I'd use blocks in
the DSL directly anyway (or 'if' for that matter)--since the block
syntax is a bit ugly in both languages--and I can't imagine it being
necessary.
Really, I'll probably be implementing something very simple--so it's
probably a wash.
It looks like I'll need to set the following information:
Run name, ID, number of agents, target function, max error,
visualization on/off, verbose on/off
In Ruby this could be done as follows (calls the new_run method and
passes in a hash):
new_run name => 'my name', id => 0, agent_count => 200, target =>
30sphere ,error => 1e-10, visualize => true
Since we are passing a hash into the new_run method, these arguments
can be in any order. They can also be omitted (the target object
presumably sets a default for anything not present).
The same thing in smalltalk would look like this (note, they can be
in almost any order--'name:' is a constructor and must come first--
the others can be in any order and can be dropped):
TrialRun name: 'my name'; id: 0; agentCount: 200; target: 30sphere;
error: 1e-10; visualize: true
or, for a prettier option (but one that sacrifices flexibility)
TrialRun name: 'my name' id: 0 agentCount: 200 target: 30sphere
error: 1e-10 visualize: true verbose: false
None of them are really as readable as I would like, but any would
do. The last one is the most readable, but if the number of optional
flags started to grow--it would quickly become a real pain.
-Rich-
More information about the Squeak-dev
mailing list
|