Skip to Content

Using Elisp to Define a Media Package Channel

In my last post I wrote about how I’ve come around to understand functional programming, without really setting off to.

In this post I’m going to go back and look at a concept I’ve been chewing on recently, of a media package channel. I have a lot of documents, that are available in different places, in different formats. I want a way to have a record of where they all are, that I can work with in a meaningful way to get the latest version of the rendered doc. Note that this is different than something like Git, which is for - usually - getting a whole source repository. I suppose the project could be written on top of Git, if each format were… tagged as its own release and the rest of the files were ignored for that tag?

It’s not worth exploring because often, docs aren’t in Git repositories, and that’s even more often true for other forms of media.

Anyway, my notion for the media package stuff is similar to how software packages work, where there’s a channel that has a list of packages, and a manifest for each package. In an earlier draft, I implemented the notion of such a media package channel definition in JSON. Here’s what that looked like:

{"brutstrap-css": {
    "name": "Brutstrap CSS",
    "creator": "emsenn",
    "license": "CC0",
    "description": "a cascading stylesheet for representing HTML content as it is constructed.",
    "versions": {
	"current": {
	    "sourcehut": {
		"location": "https://git.sr.ht/~emsenn/brutstrap-css/blob/master/",
		"formats": ["html","md","pdf","tex","txt"]
	    }
	}
   }
}}
Code Snippet 1: A block of JSON, json-package-channel

Last night after writing about my understanding of functional programming, I figured it was time to learn some Elisp syntax, since, as an Emacs user, that’s a convenient language for me to write functions in.

Here’s one way - probably the wrong way - of representing the JSON above as Elisp:

'(("brutstrap-css"
   ("name" . "Brutstrap CSS")
   ("description" . "a cascading stylesheet for representing HTML content as it is constructed.")
   ("creator" . "emsenn")
   ("license" . "CC0")
   ("versions"
    ("current"
     ("sourcehut"
      ("location" . "https://git.sr.ht/~emsenn/brutstrap-css/blob/master/")
      ("formats" . ("html" "md" "pdf" "tex" "txt")))))))

Code Snippet 2: An Elisp list, elisp-package-channel.

Now, I should be able to write a function that takes some inputs, and gives me back a path to the package specified.

(defun get-package-location
    (channel id version origin format)
  "Return the address of version VERSION of media package ID as
  defined by media package channel CHANNEL from source ORIGIN in
  format FORMAT."
  (concat
      (cdr (assoc "location" (cdr (assoc origin (cdr (assoc version (cdr (assoc "versions" (cdr (assoc id channel))))))))))
      id
      "."
      format
   ))
Code Snippet 3: An Elisp function, get-package-location.

And now, I should be able to call that function, with a line like:

#+CALL: get-package-location(channel=elisp-package-channel, id="brutstrap-css", version="current", origin="sourcehut", format="html")

Let’s see what happens:

Hmm. That’s not quite what I expected - it gave me the function name, not the function itself…

Oh. I’m only defining the function there, not calling it.

(get-package-location channel id version origin format)

Now let’s call THAT, with

#+CALL: call-get-package-location(channel=elisp-package-channel, id="brutstrap-css", version="current", origin="sourcehut", format="html")

Hey! That works! But there has to be a more straightforward way to do this, than writing a function and a separate call block…

I could do, instead, a source block that calls the function… but then I have to pass the variables to that source block, and then the function.

Could I… not have it in a function declaration, and still pass it variables?

Let’s try:

(concat
 (cdr
  (assoc "location"
	 (cdr
	  (assoc origin
		 (cdr
		  (assoc version
			 (cdr
			  (assoc "versions"
				 (cdr
				  (assoc id channel))))))))))
 id
 "."
 format
 )
Code Snippet 4: An Elisp statement, get-package-location-2

That’s named get-package-location-2, so if I call it the same as I tried the first time…

Then it works! Wonderful!

But let’s admit, get-package-location-2 is a rubbish function name, and now, I’m not sure the best way to get it back into an elisp block, if I’d want to call it there - I suppose beyond passing it as a variable, explicitly?

Perhaps at this point it would be worthwhile to look at how something literately programmed in Elisp would look, but I’m not finished exploring the topic myself yet.

And I’m glad - I see the section of the reference manual that’s relevant. I’ve used it before - and even written about using it before, but haven’t really used it for several months: Noweb.

Noweb is part of Babel, I think, the same featureset that lets you take the code blocks and put them into separate files. It lets you reference a code block by name, by putting it inside <<>>, so <<get-package-location-2()>>

Let’s try that - but I’m going to, instead of using a source code block, try my hand at at some inline source: .

Well it has a bunch of weird escapes now… Let’s try that in a source block

'<<get-package-location-2(channel=elisp-package-channel,id="brutstrap-css",version="current",origin="sourcehut",format="html")>>

Interesting side effect! I don’t… I don’t want it, do I? Let me try another approach

(prin1
 '<<get-package-location-2(channel=elisp-package-channel,id="brutstrap-css",version="current",origin="sourcehut",format="html")>>)

So the string that’s given to the source block has the escapes.

https://git.sr.ht/~emsenn/brutstrap-css/blob/master/brutstrap-css.html

But the string output by Elisp does not.

(prin1 (get-package-location channel id version origin format))

Yup, definitely not, it’s a noweb thing. Well, I’m not going to stress any more about it in the abstract. I think I’ve got a feel for how to at least start using source blocks and functional programming together. My next step will be outlining the document into which that code will go and be written about, and then, well, it’ll be time to start planning the actual code.

Editorial and License Information

My name is emsenn and I wrote this essay for the benefit of the commons. To the extent possible under law, I have waived all copyright and related or neighboring rights to it. If you're viewing it on a remote server, you're encouraged to download your own copy. Essays like this are made possible with financial support from readers like you. Thank you. To read more of my work and to learn more about me, visit https://emsenn.net