Fathoming out Racket Package’s version

/ 编程, 教程, racket, english

Recently I received an email of a github comment on a Racket Package of mine named http-client, which was announced to release by my post of Announcing the release of http-client. Until then, I realized there really exist someone using my packages except me. This also make me realize I already came to the point to fathom out the design of Racket’s package version that I tried before and abandoned because of being short on time, and I should commit to add versions to my packages.

My understanding about the Racket Package version may be limited on my own experiences. Therefore, I will try to change this post along with my learning of Racket.

    1 The ambiguous part

    2 It does have version

      2.1 Through using git tags

      2.2 Declare version in info.rkt

      2.3 Racksnaps

1  The ambiguous part🔗

Talking about Racket’s package version, actually all the explanation and description of its design is documented at Package Management in Racket. The only thing is, which coincides with my feeling about other docs of Racket, that is the doc is too seriously focusing on its implementation theory and leaving out the examples, this is making it unfriendly to be understood straightly. And about that, I think perhaps it is because the Racket world is too small to be allowed for a person to spend too much time on writing elaborately understandable documents. Anyway, after reading the doc I mentioned above and asking a few questions on its Slack channel, I make a progress and have the feeling I should write something down for someone who has the same questions as me.

After generally reading the doc I mentioned above, my first impression is Racket’s packages doesn’t have a version, probably this impression is from version explanation on chapter of:

Naming and Designing Packages
A version declaration for a package is used only by other package implementors to effectively declare dependencies on provided features. Such declarations allow raco pkg install and raco pkg update to help check dependencies. Declaring and changing a version is optional, and the package catalog ignores version declarations; in particular, a package is a candidate for updating when its checksum changes, independent of whether the package’s version changes or even in which direction the version changes. We suggest using a version smaller than "1.0" to indicate that a package’s interface is unstable and changing it to "1.0" when you are ready to commit to backwards compatibility going forward.

and from version explanation on chapter of:

Package Concepts
A version is intended to reflect available features of a package, and should not be confused with different releases of a package as indicated by the checksum.

En...then, it seems it is true that Racket does not advocate to use a version for its packages and thus it naturally does not support a package version, at least on the common sense way of most engineers are familiar with. And this is truth, check the answer of questions on its FAQ:

How can I specify which version of a package I depend on if its interface has changed and I need an old version?
Therefore, package authors should not make backwards incompatible changes to packages. Instead, they should release a new package with a new name. For example, package libgtk might become libgtk2.

But isn’t this too ideal? I know this answer is surely yes for me.

2  It does have version🔗

2.1  Through using git tags🔗

But if you dig it deeper, you will notice that there is still a way to make it. If we check the Package Sources chapter, we will notice we can install a package instead of directly using the name(for this case it will access the catalog of pkg.racket-lang.org) such as run raco pkg install http-client, we can achieve the same goal by run raco pkg install "https://github.com/yanyingwang/http-client.git".

And further, if we add a tag to this github repo, we can specify a version to install with running raco pkg install "https://github.com/yanyingwang/http-client.git#v0.0.1". And by the time of this, if we run raco pkg show http-client, it will show:
  Installation-wide:
   [none]
  User-specific for installation "8.0":
   Package      Checksum               Source
   http-client  d61d66b410c7e53cb7...  url...ient.git#v0.0.1

Noticing the value of Source column is showing url...ient.git#v0.0.1 instead of  catalog http-client, which is the result value of installing from using raco pkg install http-client as shown below:
  Installation-wide:
   [none]
  User-specific for installation "8.0":
   Package      Checksum                                  Source
   http-client  09d1f7f086c5c8b64710b6996e293fd220080a77  catalog http-client

You can use find-pkgs-dir and find-user-pkgs-dir to find out where code was installed, and view the source to check if package is correctly installed on the specific version as expected.

2.2  Declare version in info.rkt🔗

Let’s say I declare a version 0.1 in info.rkt as below:
#lang info
(define collection "http-client")
(define deps '("base" "html-parsing" "at-exp-lib"))
(define version "0.1")
And what is this version used for? Well, this version is used for the deps of the "info.rkt" file. This is explained in the chapter of:

Package Metadata
A version-string specifies a lower bound on an acceptable version of the needed package.

So let’s say I have another package named qweather, which declare a dependency of http-client to version 0.1 like below:
#lang info
(define collection "qweather")
(define deps '("base" "at-exp-lib" "http-client" '#:version "0.1"))
(define version "0.1")

Actually I haven’t got the chance to try this, but from the official doc this seems should be working. And I also think we doesn’t have an easy way to fix it if we forgot to update this version after already pushed the git commit of new feature code.

2.3  Racksnaps🔗

From the engineering perspective, the package version system of Racket seems too simple and error-prone. In my opinion, it should simply just use the git tags as versions unless there is a risk people may force-push or somewhat change the checksum of git commits.

For this case, the only resolution is caching each day’s catalogs of packages and their git sources, this is also the way other languages’s package systems are doing. For example in a Gemfile of Ruby, if you write in gem 'ransack', '1.8.10', it will consult official server for a packed version, and if you write in gem 'jquery-rails', github: 'prepsmith/jquery-rails', ref: '59fb16511a40248cd481dc0330ec536733f64758', it will go to the github to find a specified version.

So here comes the Racksnaps, you can find the basic usage in its home page. And especially, if your network is not stable to access pkg.racket-lang.org and github.com just like mine in China, you can use raco pkg config --set catalogs https://racksnaps.defn.io/built-snapshots/2021/02/20/catalog/, this will speed up the builds very much.