Replies: 1 comment 11 replies
-
I think that the problem you're describing is already solved pretty well by project-local dependency directories? If I'm understanding correctly, you only have this clobbering issue if you install dependencies to the system path, and if you don't use the system path the issue goes away for free.
If I'm distributing an application, I want to specify exactly the version of all of my dependencies that I expect. But if I'm distributing a library, I don't want to set exact constraints on what will become transitive dependencies for applications or libraries that use my library. If library If I'm the author of I know that JPM gives me no way to express this right now, but that makes me sad, and I wouldn't want to get further away from what I see as the right end goal.
I would add another part to this, which is that it's a bit fragile: you could update the version in your
It seems like a heavy solution to a problem that already has a cheap solution. Maybe it's worth saying more about what you're using the system path for? I think that Janet's packaging story has a lot of shortcomings (dev dependencies! -- I know you're aware of that one :) and there's certainly a lot of room for improvement in JPM. I haven't felt the pain that this particular proposal allays, and I think that you could get the same benefit with no change to user code with a more ambitious change to JPM's library directory structure. Which would be a backwards-incompatible change to existing installations, but would be completely transparent to the user... |
Beta Was this translation helpful? Give feedback.
-
One of the limitations of Janet's code importing system is dependency versioning. Although a user can specify in a
project.janet
file a Git tag/commit to use, there is no guarantee that the Janet interpreter will import the file from this revision of the repository. This discussion proposes a backwards-compatible way in which Janet could allow users to specify a dependency at a particular revision.Background
Janet source files can import other Janet source files (each, a 'dependency'). When a dependency is imported, the source file is read, parsed and compiled into values that are stored in an environment table. During importation, the public values of this table are merged into the environment table of the importing file (with the symbols typically prepended with a name so as to avoid conflicts). A Janet source file can import dependencies that are located relative to the importing file or it can import dependencies from the system library (often
/usr/local/lib/janet
).Janet files are typically added to the system library using JPM, the Janet Project Manager. By listing the URLs of Git repositories in a
project.janet
file, a user can import the dependencies into the system library usingjpm deps
. JPM allows a user to import the files at a particular revision of each repository. Users do this by specifying a:tag
(despite the name, the value can be a Git tag or a commit SHA).Problem
Unfortunately, while a user can specify a revision in the
project.janet
file, there's no guarantee that the dependency imported at runtime is the version from the revision specified. If the user subsequently installs a different version of the file, this will simply overwrite the version in the system library.Solution
This post proposes a two-part solution. The proposal is detailed below but the way this would be called by the user is demonstrated first.
Rather than this:
a user could write this:
In other words, the user would specify at the call site of
import
the revision desired. Janet would then import a version of the file from that revision using the tag as part of the filename (e.g.foo.1.2.3.janet
). If the file with that name could not be located, an error would be thrown. Existing code would work exactly the same as now; specifying a tag would be optional.Implementation
As noted above, the solution involves two parts.
Janet interpreter
The first part is a change to the private
require-1
function in Janet'sboot.janet
.The changes here:
Set the
(kargs :tag)
to the value of a dynamic binding if the import is for a relative path (this dynamic binding is set up later).Append
.
and the value of(kargs :tag)
to the path if a value exists.Search for the modified path.
Set the initial environment for the imported file to include the
(kargs :tag
) value as a dynamic binding (this is necessary for the relative paths described at 1).No other changes are required to the interpreter.
JPM
The second part involves changes to JPM. These would cause JPM to append the tag specified in a
project.janet
to the Janet source files installed into the system library.A symlink (without the tag) could also be created to point to the source file. This would allow the source file to be used by import statements that don't include a tag.
Objections
Inconsistent with other languages
Some may object that specifying the tag at the import call site is inconsistent with other programming languages. While I agree this is true, I argue that this is the preferable approach.
The author of the source file is in the best position to know which revision of the dependency they wish to use and they should be able to specify this explicitly. It should not be the case that a user of the source file can change the revision of the dependencies used (without any change to the source code). We should avoid unnecessary coupling but the revision of a dependency is necessarily coupled to the file importing it. A different revision of a dependency may change the implementation of a function used or it may change the function signature, both of which may be incompatible. By allowing an author to specify the revision, that author can know the code will not run unless that specific revision is available.
Painful to update dependency versions
When updating a dependency, a user would need to change both the
project.janet
file as well as the import call sites. I agree this is painful but argue that it is a necessary consequence of being explicit about the revision used.Increased resources
This increases the disk space used, particularly as more and more Janet source files are installed in the system library. My argument would be that, in 2023, this is fine.
Non-source dependencies
This proposal has only discussed Janet source files but Janet also supports native dependencies and Janet image files. The way the code has been suggested above would allow a user to specify a tag for a native dependency.
While I don't think that would cause an issue if only one version of that dependency is imported, I don't think multiple revisions of native dependencies can be imported into the same process (but maybe they can—I'm not sure). I'm also unsure what the consequences of multiple Janet image files would be.
init.janet
filesThe above proposal doesn't work cleanly for source files that are found by appending
/init.janet
to the path. I don't have a great answer for this at the moment.Thoughts
For those who read through all that, I'm curious what you think. Any thoughts? :)
Beta Was this translation helpful? Give feedback.
All reactions