Factor language (factor-lang) on NixOS

The factor programming language and its environment (package factor-lang) run beautifully on NixOS … except that the scaffold words (which are used for creating various kinds of skeleton projects, and which are recommended heavily in the documentation) want access to write-only directories.

Some of the scaffold words can be made to work with easy workarounds. For example, this works:

"/tmp" add-vocab-root
scaffold-vocab

But this doesn’t:

"/tmp" add-vocab-root
scaffold-work

Does anyone have suggestions for more robust workarounds?

Thank you!

In case anyone needs a quick desperate workaround, this seems to work:

sudo ln -sf /home/whatever/factor /var/lib/

but obviously that is crazily inelegant.

That looks like it was just installed with the wrong prefix or such then. Not that I have any idea about this language, maybe have a read through their installation docs.

It appears that the choice of /var/lib/factor was deliberate. See this PR which introduced the change, and this PR which fixed a regression in this behavior.

It seems to me that you have a few options:

  1. Modify the derivation for factor to set “resource:work” to another directory. Note that if you choose a directory in your home directory, factor wouldn’t work for other users on your machine — probably not a concern for you if you are the only user of your machine.
  2. Create /var/lib/factor as a writable directory. If you are the only user on your machine, it probably suffices to ensure it’s writable by everyone; for example, with sudo mkdir -m 777 /var/lib/factor. If there are multiple users on your machine, you might want to set the sticky bit with sudo mkdir -m 1777 /var/lib/factor instead.
  3. Open an issue in nixpkgs for this. The current behavior clearly isn’t the best for a new user. I’m not sure what should be done instead — perhaps wrap the factor executable with a script that checks for a writable /var/lib/factor at startup, and prompts the user for sudo credentials if it does not? — but discussion with other github users might yield a workable solution.

I don’t know enough about factor to know if there’s any way to specify/override these resource directories other than rebuilding the language. If there’s a way to do so with environment variables or command line flags, you might want to do that. Perhaps you could suggest such functionality as an improvement to factor.

1 Like

Disclaimer: I am the maintainer of this package and introduced this behaviour.

The factor runtime is image-based like many other dynamic languages (e.g. Scheme, CommonLisp, Smalltalk). It bootstraps itself off an image usually called factor.image but may be run with any user-defined image.

In the current state, the factor language makes the following assumptions:

  1. You are the sole user of the factor.image
  2. The factor sources are writable
  3. The work vocabulary root (which is used by the scaffolding) is in a writable location.

I have not found any turn-key solution to support these assumptions in a central multi-user installation. The work vocabulary root can obviously not point to the store path, so /var/lib/factor is the conventional, system-wide, machine-specific directory where this kind of data should be held. Wildly pointing into the current user’s home directory would only yield wrong assumptions and annoyances. And I haven’t even touched point 2. or 1.

After your first dabbling steps into factor you might want to look into creating and loading your own factor image which points to writable factor sources and sets the work vocabulary root to a path of your liking. This is not hard to do.

I am open for suggestions about how this could be made more convenient. I don’t find the idea of supplying the path as a configuration option into the derivation convincing at the moment. There is nothing hacky about symlinking to /var/lib/factor on a single-user system; at least not more-hacky than installing a system-wide factor which points into the home directory of a specific user, which would be possible with such a configuration option.

1 Like

Thank you both @mwhite and @spacefrogg, and @spacefrogg thank you extra for maintaining the package.

I don’t have a better suggestion than yours. I’m going with @mwhite’s suggestion 2 for my own use.

This hadn’t occurred to me. Thank you. If I stay with Factor for a long time then I’ll do that, and I’ll also document it upstream.

In other news, I don’t think Discourse will let me mark two replies as “Solution”! Posterity please note that both @mwhite’s post above and @spacefrogg’s post above contain (different) solutions.

Best to still mark it as solved with either of the two comments anyway.

Have you considered doing sonething with bubblewrap that drops factor into a chroot where the sources are writeable? Might be able to solve the lack of a user-specific /var/lib/factor that way too.

I consider bubblewrapping overkill. You would also not want to bubblewrap python just to be able to make its sources writable.

In addition to the aforementioned solutions, I would advise the following:

Work around scaffold-work

The current state is that scaffold-work is hard-coded to /var/lib/factor. I think that the semantics of resource:work is flawed as for any other language that has the concept of a “workspace” at a fixed location. So, I don’t intend to support it further than this. The simple workaround (without accessing /var/lib/factor) is, create a ~/.factor-roots file containing one directory per line to your desired vocabulary roots. ~/... paths can be used and are resolved to your home directory. Then, use scaffold-vocab instead of scaffold-work for all your work. All of this is documented in the factor documentation, and I regard this as the superior approach to using the work vocabulary root.

Writable standard library

If you want to change the factor standard library, you can easily nix develop nixpkgs#factor and run the build steps in any directory of your liking. This yields a writable factor.image and writable factor sources.
For the impatient, the command chain is as follows:

nix develop nixpkgs#factor-lang
export out=<desired-writable-location>
cd "$TMPDIR" # nix develop created this for you
unpackPhase
cd *
patchPhase
eval "$buildPhase"
eval "$installPhase"
cd && rm -rf "$TMPDIR"
exit
1 Like