Cat’s hacks:

Libraries as hacks

Have an idea, take it to its logical extreme... and see what happens :-)

UPDATE: and the answer is this turns out to be a bad idea because it changes loading libraries from something that you can do dynamically at the Arc prompt to something you do staticly before starting Arc.

In Sharing Arc hacks and continued in Using Git Commits for Hacks, I explored my idea of making patches more like libraries: making it easy to choose which patches you wanted in your program, just as how in a typical programming environment you’re free to choose which libraries you want to use.

Now I’m going further and exploring, “what if we made libraries more like patches”?

A patch becomes part of your program when you merge it into your source code. If a library were like a patch, then it would also be loaded when it was merged in. For example, when a library “foo” was merged in,

 $ git pull git://github.com/some-repo somename.foo

that commit could cause foo to add itself to libs.arc:

 (map load '("strings.arc"
             "pprint.arc"
             "code.arc"
             "html.arc"
             "srv.arc"
             "app.arc"
             "prompt.arc"
             "foo.arc"))

However, we run into a problem with Git if we try to load libraries this way. If another library “bar” also tries to add itself to the end of the list in libs.arc, we’ll immediately get a merge conflict at the end of libs.arc.

...which suggests that since we’re programming in a language of lists, perhaps it would be useful to be using a merge process that knew about lists. In the meantime I borrow a trick I first saw in Gentoo Linux.

My libs patch causes all the .arc files present in the lib subdirectory to be loaded automatically. Now the commits for foo and bar can independently add themselves to the lib directory, and Git is able to merge both without conflict.

Some libraries need to be loaded after others. While a library bar doesn't have to be loaded after a library foo just because some functions in bar call functions in foo, it does have to be loaded after foo if it uses some macros defined in foo or calls some of foo’s functions while it’s loading.

What Gentoo does is configuration files are loaded alphabetically by name. If two configuration files need to be loaded in a particular order, they’ll be given a numeric prefix. “bar” would normally come before “foo”, but by giving them the names “10-foo” and “20-bar”, they’ll be loaded in the opposite order. That works OK when you have one set of files and you can give every one (or every ordering subset) a number ahead of time, but isn’t practical when people can be creating new libraries at any time.

The approach that most languages use is that libraries indicate which other libraries they need by having an “import”, “use”, or “require” statement at the beginning of their code. However, since my goal is to let libraries be more like patches, I may want my hack to get itself loaded before another library. If the hack patches an import list at the beginning of the library’s source code, we run into the same problem that we had with libs.arc: two patches changing a library’s prerequisites will immediately cause a Git merge conflict.

My solution is that the presence of a (zero-length) file “after:foo:bar” in the lib directory causes bar to be loaded after foo. Now patches can independently modify the library loading order, or position themselves before another library, without gratuitous Git conflicts.

Implementing this for Arc, I have the following tags:

 catdancer.arc3rc5.libs0
 catdancer.arc3rc5.libs0-srv
 catdancer.arc3rc5.libs0-app
 catdancer.arc3rc5.libs0-prompt
 catdancer.arc3rc5.libs0-news

The first loads a basic Arc without the web server, and each subsequent tag loads that library. In arc3 news needs to be loaded explicitly since not everyone wants to be running news in the web server; with this setup pulling the news tag causes news to be loaded automatically.

Comment

Comment in the Arc Forum.