Saturday, May 5, 2007

Qualified naming in Factor

One problem that occasionally comes up in Factor is overlapping word names in different vocabularies. Factor's vocab system allows for these overlaps, and whichever vocab is used last has the definition that is used. For example, if you have the following code:

IN: foo
: x 1 ;
IN: bar
: x 2 ;

USING: foo bar ;
IN: baz
: y x ;

Then y would return 2 from bar's x, rather than 1 from foo's x. bar is used after foo, so it overrides foo's definitions. For this reason, it is always appropriate to put the IN: line after the USING: line. If IN: comes before, then the USE:'d vocabularies could potentially override the the vocabulary that you're actually in!

Inevitably (this may have already happened; I'm not sure) there will be a case of conflict so bad that names need to be changed. They would need be changed because they overlap, but both need to be used in the same third piece of code. But this is terrible for modularity. The authors of the first two pieces of code shouldn't have to care about each other, or about the ways they might be combined.

Most programming languages solve this using qualified names for modules. That is, instead of just writing x and hoping it comes from the right module that you've previously imported, you could write Foo.x or Bar.x. Until 15 minutes ago, this was impossible in Factor, but now you can, using the syntax << foo x >> or << bar x >>.

I thought this would be hard, but it was actually extremely easy. The entire code is below. It's so small, it doesn't even need to use kernel. It's great how easy Factor makes it to arbitrarily extend the parser for little hacks like this.


USING: parser sequences namespaces ;
IN: qualified

: use- ( -- )
use get pop* ;

DEFER: >>

: <<
scan use+ \ >> parse-until
[ parsed ] each use- ; parsing


The code is up in my darcs repository.

By the way, there's one major flaw with this code (as Slava will be quick to point out): it doesn't interact with the prettyprinter properly. The prettyprinter would need major changes to deal with printing something like [ << foo x >> << bar x >> ]. Probably, in that case, both xs should be printed as qualified. However, this occurs very rarely, and unless two words with the same name from different vocabularies are used, the prettyprinter acts appropriately.

Update: I should note that this construct is a bit more general than it looks at first. If you want to include a vocabulary for the duration of one word definition, you can do the following.

<< foo
: bar ( a -- )
word1-from-foo swap [ word2-from-foo ] each-with ;
>>

The words from foo will be taken from the appropriate vocab, while swap and each-with will also function (assuming they're already in the use path). A weird side effect of this is that << foo swap >> works the same as swap, probably providing the kernel definition in both cases. The qualified naming system could disallow this, but I don't see the point.

1 comment:

Anonymous said...

Could something like

http://ronware.org/reva/wiki/Contexts

help with this problem?