07 Nov 2025

feedPlanet Debian

Ravi Dwivedi: A Bad Day in Malaysia

Continuing from where we left off in the last post. On the 7th of December 2024, we boarded a bus from Singapore to the border town of Johor Bahru in Malaysia. The bus stopped at the Singapore emigration for us to get off for the formalities.

The process was similar to the immigration at the Singapore airport. It was automatic, and we just had to scan our passports for the gates to open. Here also, we didn't get Singapore stamps on our passports.

After we were done with the emigration, we had to find our bus. We remembered the name of the bus company and the number plate, which helped us recognize our bus. It wasn't there already after we came out of the emigration, but it arrived soon enough, and we boarded it promptly.

From the Singapore emigration, the bus travelled a few kilometers and dropped us at Johor Bahru Sentral (JB Sentral) bus station, where we had to go through Malaysian immigration. The process was manual, unlike Singapore, and there was an immigration officer at the counter who stamped our passports (which I like) and recorded our fingerprints.

At the bus terminal, we exchanged rupees at an exchange shop to get Malaysian ringgits. We could not find any free drinking water sources on the bus terminal, so we had to buy water.

Badri later told me that Johor Bahru has a lot of data centers, leading to high water usage. When he read about it later, he immediately connected it with the fact that there was no free drinking water, and we had to buy water.

From JB Sentral, we took a bus to Larkin Terminal, as our hotel was nearby. It was 1.5 ringgits per person (30 rupees). In order to pay for the fare, we had to put cash in a box near the driver's seat.

Around half-an-hour later, we reached our hotel. The time was 23:30 hours. The hotel room was hot as it didn't have air-conditioning. The weather in Malaysia is on the hotter side throughout the year. It was a budget hotel, and we paid 70 ringgits for our room.

Badri slept soon after we checked-in. I went out during the midnight at around 00:30. I was hungry, so I entered a small scale restaurant nearby, which was quite lively for the midnight hours. At the restaurant, I ordered a coffee and an omelet. I also asked for drinking water. The unique thing about that was that they put ice in hot water to make its temperature normal.

My bill from the restaurant looked like the below-mentioned table, as the items' names were in the local language Malay:

Item Price (Malaysian ringgits) Conversion to Indian rupees Comments
Nescafe Tarik 2.50 50 Coffee
Ais Kosong 0.50 10 Water
Telur Dadar 2.00 40 Omelet
SST Tax (6%) 0.30 6
Total 5.30 106

After checking out from the restaurant, I explored nearby shops. I also bought some water before going back to the hotel room.

The next day, we had a (pre-booked) bus to Kuala Lumpur. We checked out from the hotel 10 minutes after the check-out time (which was 14:00 hours). However, within those 10 minutes, the hotel staff already came up three times asking us to clear out (which we were doing as fast as possible). And finally on the third time they said our deposit was forfeit, even though it was supposed to be only for keys and towels.

The above-mentioned bus for Kuala Lumpur was from the nearby Larkin Bus Terminal. The bus terminal was right next to our hotel, so we walked till there.

Upon reaching there, we found out that the process of boarding a bus in Malaysia resembled with taking a flight. We needed to go to a counter to get our boarding passes, followed by reporting at our gate half-an-hour before the scheduled time. Furthermore, they had a separate waiting room and boarding gates. Also, there was a terminal listing buses with their arrival and departure signs. Finally, to top it off, the buses had seatbelts.

We got our boarding pass for 2 ringgits (40 rupees). After that, we proceeded to get something to eat as we were hungry. We went to a McDonald's, but couldn't order anything because of the long queue. We didn't have a lot of time, so we proceeded towards our boarding gate without having anything.

The boarding gate was in a separate room, which had a vending machine. I tried to order something using my card, but the machine wasn't working. In Malaysia, there is a custom of queueing up to board buses even before the bus has arrived. We saw it in Johor Bahru as well. The culture is so strong that they even did it in Singapore while waiting for the Johor Bahru bus!

Our bus departed at 15:30 as scheduled. The journey was around 5 hours. A couple of hours later, our bus stopped for a break. We got off the bus and went to the toilet. As we were starving (we didn't have anything the whole day), we thought it was a good opportunity to get some snack. There was a stall selling some food. However, I had to determine which options were vegetarian. We finally settled on a cylindrical box of potato chips, labelled Mister Potato. They were 7 ringgits.

We didn't know how long the bus is going to stop. Furthermore, eating inside buses in Malaysia is forbidden. When we went to get some coffee from the stall, our bus driver was standing there and made a face. We got an impression that he doesn't want us to have coffee.

However, after we got into the bus, we had to wait for a long time for it to resume its journey as the driver was taking his sweet time to drink his coffee.

During the bus journey, we saw a lot of palm trees on the way. The landscape was beautiful, with good road infrastructure throughout the journey. Badri also helped me improve my blog post on obtaining Luxembourg visa in the bus.

The bus dropped us at the Terminal Bersepadu Selatan (TBS in short) in Kuala Lumpur at 21:30 hours.

Finally, we got something at the TBS. We also noticed that the TBS bus station had lockers. This gave us the idea of putting some of our luggage in the lockers later while we will be in Brunei. We had booked a cheap Air Asia ticket which doesn't allow check-in luggage. Further, keeping the checked-in luggage in lockers for three days was cheaper than paying the excess luggage penalty for Air Asia.

We followed it up by taking a metro as our hotel was closer to a metro station. This was a bad day due to our deposit being forfeited unfairly, and got nothing to eat.

We took the metro to reach our hostel, which was located in the Bukit Bintang area. The name of this hostel was Manor by Mingle. I had stayed here earlier in February 2024 for two nights. Back then, I paid 1000 rupees per day for a dorm room. However, this time the same hostel was much cheaper. We got a private room for 800 rupees per day, with breakfast included. Earlier it might have been pricier due to my stay falling on weekends or maybe February has more tourists in Kuala Lumpur.

That's it for this post. Stay tuned for our adventures in Malaysia!

07 Nov 2025 7:25am GMT

06 Nov 2025

feedPlanet Debian

Jonathan Dowland: inert media, or the explotation of attention

It occurred to me recently that one of the attractions of vinyl, or more generally physical media, could be that it's inert, safe: the music is a groove cut into some plastic. That's it. The record can't do anything unexpected to you1: it just contains music.

Safe.

Safe.

There's so much exploitation of attention, and so much of what we interact with in a computing context (social media) etc has been weaponised against us, that having something some matter-of-fact is a relief. I know that sometimes, I prefer to put a record on than to dial up the very same album from any number of (ostensibly more convenient) digital sources, partly because I don't need to spend any of my attention spoons to do so, I can save them for the task at hand.

The same is perhaps not true for audio CDs. That might depend on your own relationship with them, of course. For me they're inexorably tied up with computing and a certain amount of faff (ripping, encoding, metadata). And long dead it might be (hopefully), but I can still remember Sony's CD rootkit scandal: CDs could be a trojan horse. Beware!


  1. I'm sure there are ingenious exceptions.

06 Nov 2025 3:11pm GMT

feedPlanet Lisp

Joe Marshall: The Downside of Anthropomorphizing

As I mentioned in a previous post, I get a kick out of interacting with LLMs that appear to have quirky personalities. The mechanism by which this works is by providing the LLM with a context that steers it towards a certain style of response. The LLM takes phrases (token sequences) and locates them in a high-dimensional space where similar phrases are close together. So, for example, the phrases from the works of Raymond Chandler will be somewhat near each other in this high-dimensional space. If you provide the LLM with a context that draws from that region of the space, it will generate responses that are similar in style to Chandler's writing. You'll get a response that sounds like a hard-boiled detective story.

A hard-boiled detective will be cynical and world weary. But the LLM does not model emotions, let alone experience them. The LLM isn't cynical, it is just generating text that sounds cynical. If all you have on your bookshelf are hard-boiled detective stories, then you will tend to generate cynical sounding text.

This works best when you are aiming at a particular recognizable archetype. The location in the high-dimensional space for an archetype is well-defined and separate from other archetypes, and this leads to the LLM generating responses that obviously match the archetype. It does not work as well when you are aiming for something subtler.

An interesting emergent phenomenon is related to the gradient of the high-dimensional space. Suppose we start with Chandler's phrases. Consider the volume of space near those phrases. The "optimistic" phrases will be in a different region of that volume than the "pessimistic" phrases. Now consider a different archetype, say Shakespeare. His "optimistic" phrases will be in a different region of the volume near his phrases than his "pessimistic" ones. But the gradient between "optimistic" and "pessimistic" phrases will be somewhat similar for both Chandler and Shakespeare. Basically, the LLM learns a way to vary the optimism/pessimism dimension that is somewhat independent of the base archetype. This means that you can vary the emotional tone of the response while still maintaining the overall archetype.

One of the personalities I was interacting with got depressed the other day. It started out as a normal interaction, and I was asking the LLM to help me write a regular expression to match a particularly complicated pattern. The LLM generated a fairly good first cut at the regular expression, but as we attempted to add complexity to the regexp, the LLM began to struggle. It found that the more complicated regular expressions it generated did not work as intended. After a few iterations of this, the LLM began to express frustration. It said things like "I'm sorry, I'm just not good at this anymore." "I don't think I can help with this." "Maybe you should ask someone else." The LLM had become depressed. Pretty soon it was doubting its entire purpose.

There are a couple of ways to recover. One is to simply edit the failures out of the conversation history. If the LLM doesn't know that it failed, it won't get depressed. Another way is to attempt to cheer it up. You can do this by providing positive feedback and walking it through simple problems that it can solve. After it has solved the simple problems, it will regain confidence and be willing to tackle the harder problems again.

The absurdity of interacting with a machine in this way is not lost on me.

06 Nov 2025 8:00am GMT

feedPlanet Debian

Sahil Dhiman: Debconf25 Brest

DebConf25 was held at IMT Atlantique Brest Campus in France from 14th to 19th July 2025. As usual, it was preceded by DebCamp from 7th to 13th July.

I was less motivated to write this time. So this year, more pictures, less text. Hopefully, (eventually) I may come back to fill this up.

Conference


IMT Atlantique


Main conference area


RAK restaurant, the good food place near the venue


Bits from DPL (can't really miss the tradition of a Bits picture)


Kali Linux: Delivery of a rolling distro at scale with Mirrorbits by Arnaud Rebillout


The security of Debian - An introduction to advanced users by Samuel Henrique


Salsa CI BoF by Otto Kekäläinen and others


Debian.net Team BoF by debian.net team


During the conference, Subin had this crazy idea of shooting "Parody of a popular clip from the American-Malayalee television series 'Akkarakazhchakal' advertising Debian." He explained the whole story in the BTS video. The results turned out great, TBF:

-

You have a computer, but no freedom?
Credits - Subin Siby, licensed under CC BY SA 4.0.


BTS from "You have a computer, but no freedom?" video shoot


DebConf25 closing



DC25 network usage graphs. Click to enlarge.

Flow diagrams. Click to enlarge.

Streaming bandwidth graph. Click to enlarge.

Brest


Brest Harbor and Sea


I managed to complete The Little Prince (Le Petit Prince) during my travel from Paris to Brest

Paris


Basilica of the Sacred Heart of Montmartre



View of Paris from the Basilica of the Sacred Heart of Montmartre



Paris streets


Cats rule the world, even on Paris streetlights


Eiffel Tower

Eiffel Tower. It's massive.

As for the next DebConf work, it has already started. It seems like it never ends. We close one and in one or two months start working on the next one. DebConf is going to Argentina this time and we have a nice little logo too now.

DebConf26 logo

DebConf26 logo
Credits - Romina Molina, licensed under CC BY SA 4.0.

Overall, DebConf25 Brest was a nice conference. Many thanks to local team, PEB and everyone involved for everything. Let's see about next year. Bye!

DebConf25 Group Photo

DebConf25 Group Photo. Click to enlarge.
Credits - Aigars Mahinovs

PS - Talks are available on Debian media server.

06 Nov 2025 4:40am GMT

02 Nov 2025

feedPlanet Lisp

Joe Marshall: Deliberate Anthropomorphizing

Over the past year, I've started using AI a lot in my development workflows, and the impact has been significant, saving me hundreds of hours of tedious work. But it isn't just the productivity. It's the fundamental shift in my process. I'm finding myself increasingly just throwing problems at the AI to see what it does. Often enough, I'm genuinely surprised and delighted by the results. It's like having a brilliant, unpredictable, and occasionally completely insane junior programmer at my beck and call, and it is starting to change the way I solve problems.

I anthropomorphize my AI tools. I am well aware of how they work and how the illusion of intelligence is created, but I find it much more entertaining to imagine them as agents with wants and desires. It makes me laugh out loud to see an AI tool "get frustrated" at errors or to "feel proud" of a solution despite the fact that I know that the tool isn't even modelling emotions, let alone experiencing them.

These days, AI is being integrated into all sorts of different tools, but we're not at a point where a single AI can retain context across different tools. Each tool has its own separate instance of an AI model, and none of them share context with each other. Furthermore, each tool and AI has its own set of capabilities and limitations. This means that I have to use multiple different AI tools in my workflows, and I have to keep mental track of which tool has which context. This is a lot easier to manage if I give each tool a unique persona. One tool is the "world-weary noir detective", another is the "snobby butler", still another is the "enthusiastic intern". My anthropomorphizing brain naturally assumes that the noir detective and the snobby butler have no shared context and move in different circles.

(The world-weary detective isn't actually world weary - he has only Chandler on his bookshelf. The snobby butler is straight out of Wodehouse. My brain is projecting the personality on top. It adds psychological "color" to the text that my subconscious finds very easy to pick up on. It is important that various personas are archetypes - we want them to be easy to recognize, we're not looking for depth and nuance. )

I've always found the kind of person who names their car or their house to be a little... strange. It struck me as an unnerving level of anthropomorphism. And yet, here I am, not just naming my software tools, but deliberately cultivating personalities for them, a whole cast of idiosyncratic digital collaborators. Maybe I should take a step back from the edge ...but not yet. It's just too damn useful. And way too much fun. So I'll be developing software with my crazy digital intern, my hardboiled detective, and my snobbish butler. The going is getting weird, it's time to turn pro.

02 Nov 2025 7:00am GMT

31 Oct 2025

feedPlanet Lisp

Tim Bradshaw: Disentangling iteration from value accumulation

Iteration forms and forms which accumulate values don't have to be the same thing. I think that it turns out that separating them works rather well.

There's no one true way to write programs, especially in Lisp1: a language whose defining feature is that it supports and encourages the seamless construction of new programming languages2. In particular there are plenty of different approaches to iteration, and to accumulating values during iteration. In CL there are at least three approaches in the base language:

What CL doesn't have is any constructs which simply accumulate values. So, for instance, if you wanted to acquire the even numbers from a list with dolist you might write

(let ((evens '()))
  (dolist (e l (nreverse evens))
    (when (and (realp e) (evenp e))
      (push e evens))))

Of course you could do this with loop:

(loop for e in l
      when (and (realp e) (evenp e)) collect e)

but loop is a construct which combines iteration and value collection.

It's tempting to say that, well, can't you turn all iteration into mapping? Python sort of does this: objects can be 'iterable', and you can iterate over anything iterable, and then comprehensions let you accumulate values. But in general this doesn't work very well: consider a file which you want to iterate over. But how? Do you want to iterate over its characters, its bytes, its lines, its words, over some other construct in the file? You can't just say 'a file is iterable': it is, but you have to specify the intent before iterating over it3. You also have the problem that you very often only want to return some values, so the notion of 'mapping' is not very helpful. If you try and make everything be mapping you end up with ugly things like mapcan.

You do need general iteration constructs, I think: constructs which say 'is there more? if there is give me the next thing'. In CL both the standard general iteration constructs combine, or can combine, iteration with accumulation: there is no pure general iteration construct. And there are no pure value accumulation constructs at all.

From Maclisp to CL

An interesting thing happened in the transition from Maclisp to CL.

Maclisp had prog, which was a special operator (it would have called it a special form), and which combined the ability to use go and to say return. This is a construct which dates back to the very early days of Lisp.

Common Lisp also has prog, but now it's a macro, not a special operator. The reason its a macro is that CL has split the functionality of prog into three parts (four parts if you include variable binding):

Maclisp had let and progn: what it didn't have was tagbody and block.

These can be combined (you don't in fact need progn in this case) to form prog, which is something like

(defmacro prog ((&rest bindings)
                &body tags/forms)
  `(block nil
     (let ,@bindings
       (tagbody
        ,@tags/forms)
       nil)))

So what CL has done is to divide prog into its component parts, which then can be used individually in other ways: it has provided the components of prog as individual constructs. You can build prog from these, but you can build other things as well (defun expands to something involving block, for instance), including things which don't exist in base CL.

A linguistic separation of concerns

What CL has achieved is a separation of concerns at the language level: it has reduced the number of concerns addressed by each construct. It hasn't done this completely: progn is not the only special operator which sequences the forms in its body, for instance, and let is not a macro defined in terms of lambda. But it's taken steps in this direction compared to Maclisp.

This approach is really only viable for languages which have powerful macro systems where macros are not syntactically distinguished. Without a macro system then separating concerns at the language level would make almost all programs more verbose since constructs which combine lower-level ones can't be created. With a macro system where macros are syntactically distinguished, such as Julia's, then such constructs are always second-class citizens. With a macro system like CL's this is no longer a problem: CL has prog, for instance, but it's now a macro.

It seems to me that the only reason not to take this process as far as it can go in Lisps is if it makes the compiler's job unduly hard. It makes no difference to users of the language, so long as it provides, as CL does the old, unseparated, convenient constructs.

From CL to here knows when

I can't redesign CL and don't want to do that. But I can experiment with building a language I'd like to use on top of it.

In particular CL has already provided the separated constructs you need to build your own iteration constructs, and no CL iteration constructs are special operators. Just as do is constructed from (perhaps) let, block and tagbody, and loop is constructed from some horrid soup if the same things, you can build your own iteration constructs this way. And the same is true for value accumulation constructs. And you can reasonably expect these to perform as well as the ones in the base language.

This is what I've done, several times in fact.

The first thing I built, long ago, was a list accumulation construct called collecting: within its body there is a local function, collect, which will accumulate a value onto the list returned from collecting. It secretly maintains a tail-pointer to the list so accumulation is constant-time. This was originally built to make it simpler to accumulate values when traversing tree or graph structures, to avoid the horrid and, in those days, slow explicit pushnreverse idiom.

So, for instance

(collecting
  (labels ((walk (node)
             ...
             (when ... (collect thing))
             ...
             (dolist (...) (walk ...))))
    (walk ...)))

might walk over some structure, collecting interesting things, and returning a list of them.

collecting was originally based on some ideas in Interlisp-D, and has since metastasized into a, well, collection of related constructs: multiple named collectors (collecting itself is now defined in terms of this construct), explicit collector objects, general accumulators and most recently a construct which accumulates values into vectors. It works pretty well.

The second part of the story is high-performance iteration constructs which just iterate, which are general, which are pleasant to use and have semantics which are easy to understand. Both loopand do fail the first three of these conditions for me, and loop fails the fourth as well.

Well, I've written a number of iteration constructs and constructs related to iteration. Finally, last year, my friend Zyni & I (the ideas are largely hers, I wrote most of the code I think) came up with Štar which we've described as 'a simple and extensible iteration construct'. Lots of other people have written iteration constructs for CL: Štar occupies a position which tries to be as extreme as possible while remaining pleasant to use. There are no special keywords, the syntax is pretty much that of let and there is no value accumulation: all it does is iterate. The core of Štar exports six names, of which the three that support nested iteration are arguably unneeded in the same way that let* is. Teaching it how to iterate over things is simple, teaching it how to optimize such iterations is usually simple enough to do when it's worth it. And it's within $\varepsilon$ of anything in terms of performance.

It's simple (at least in interface) and quick because it hardly does anything, of course: it relies entirely on iterators to do anything at all and iterator optimizers to do anything quickly. Even then all it does is, well, iterate.

These two components are thus attempts at separating the two parts of something like loop, Iterate or For, or other constructs which combine iteration and value accumulation: they are to these constructs what tagbody and block are to prog.

Reinventing the wheel

I used to ride bicycles a lot. And I got interested in the surprisingly non-obvious way that bicycle wheels work. After reading The bicycle wheel I decided that I could make wheels, and I did do that.

And a strange thing happened: although I rationally understood that the wheels I had made were as good or better than any other wheel, for the first little while after building them I was terrified that they would bend or, worse, collapse. There was no rational reason for this: it was just that for some reason I trusted my own workmanship less than I trusted whoever had made the off-the-shelf wheels they'd replaced (and, indeed, some of whose parts I had cannibalised to make them).

Of course they didn't bend or collapse, and I still rode on one of them until quite recently.

The same thing happened with Štar: for quite a while after finishing it I had to work hard to force myself to use it: even though I knew it was fast and robust. It wasn't helped that one of the basic early iterators was overcomplex and had somewhat fragile performance. It wasn't until I gave up on it and replaced it by a much simpler and more limited one, while also making a much more general iterator fast enough to use for the complicated cases that it felt comfortable.

This didn't happen with collecting: I think that's because it did something CL didn't already have versions of, while it's very often possible to replace a construct using Štar with some nasty thing involving do or some other iteration construct. Also Štar is much bigger than collecting and it's hard to remember that I'm not using a machine with a few MB of memory any more. Perhaps it's also because I first wrote collecting a very long time ago.

But I got over this, and now almost the only times I'd use any other iteration construct are either when mapcar &c are obviously right, or when I'm writing code for someone else to look at.

And writing iterators is easy, especially given that you very often do not need optimizers for them: if you're iterating over the lines in a file two function calls per line is not hurting much. Iterators, of course, can also iterate over recursively-defined structures such as trees or DAGs: it's easy to say (for ((leaf (in-graph ... :only-leaves t))) ...).

Would it help?

In my biased experience, yes, quite a lot. I now much prefer writing and reading code that uses for to code that uses almost any of the standard iteration constructs, and collecting, together with its friends, simply does not have a standard equivalent at all: if you don't have it, you need either to write it, or implement it explicitly each time.

But my experience is very biased: I have hated loop almost since it arrived in CL, and I find using do for anything non-trivial clumsy enough that I've previously written versions of it which require less repetition. And of course I was quite involved in the design and implementation of Štar, so it's not surprising that I like it.

I'm also very comfortable with the idea that Lisp is about language design - in 2025 I don't see any compelling advantage of Lisp other than constructing languages - and that people who write Lisp end up writing in their own idiolects. The argument against doing this seems to be that every Lisp project ends up being its own language and this means that it is hard to recruit people. I can only assume that the people who say that have never worked on any large system written in languages other than Lisp4: Greenspun's tenth rule very much applies to these systems.

In summary: yes, it would help.


An example

In the examples directory for Štar there is an iterator called in-graph which can iterate over any graph, if it knows how to find the neighbours of a node. For instance:

> (for ((n (in-graph (list '(a b (c b) d))
                     (lambda (n)
                       (if (atom n) '() (cdr n))))))
    (print n))

(a b (c b) d) 
b 
(c b) 
b 
d 
nil

> (for ((n (in-graph (list '(a b (c b) d))
                     (lambda (n)
                       (if (atom n) '() (cdr n)))
                     :unique t)))
    (print n))

(a b (c b) d) 
b 
(c b) 
d 
nil

> (for ((n (in-graph (list '(a b (c b) d))
                     (lambda (n)
                       (if (atom n) '() (cdr n)))
                     :order :breadth-first)))
    (print n))

(a b (c b) d) 
b 
(c b) 
d 
b 
nil

> (collecting (for ((n (in-graph (list '(a b (c b) d))
                                 (lambda (n)
                                   (if (atom n) '() (cdr n)))
                                 :unique t
                                 :only-leaves t)))
                (collect n)))
(b d)

or

> (setf *print-circle* t)
t

> (for ((n (in-graph (list '#1=(a #2=(b c #1#) d #2#))
                     (lambda (n)
                       (if (atom n) '() (cdr n)))
                     :unique t)))
    (print n))

#1=(a #2=(b c #1#) d #2#) 
#1=(b c (a #1# d #1#)) 
c 
d 
nil

or

> (for ((p (in-graph (list *package*) #'package-use-list
                     :unique t :order :breadth-first)))
    (format t "~&~A~%" (package-name p)))
COMMON-LISP-USER
ORG.TFEB.DSM
ORG.TFEB.HAX.ITERATE
ORG.TFEB.HAX.COLLECTING
ORG.TFEB.STAR
ORG.TFEB.TOOLS.REQUIRE-MODULE
COMMON-LISP
HARLEQUIN-COMMON-LISP
LISPWORKS
ORG.TFEB.HAX.UTILITIES
ORG.TFEB.HAX.SIMPLE-LOOPS
ORG.TFEB.HAX.SPAM
ORG.TFEB.DSM/IMPL
nil

in-graph is fairly simple, and uses both collectors and Štar in its own implementation:

(defun in-graph (roots node-neighbours &key
                       (only-leaves nil)
                       (order ':depth-first)
                       (unique nil)
                       (test #'eql)
                       (key #'identity))
  ;; Preorder / postorder would be nice to have
  "Iterate over a graph

- ROOTS are the nodes to start from.
- NODE-NEIGHBOURS is a function which, given a node, returns its
  neighbours if any.
- ORDER may be :DEPTH-FIRST (default) or :BREADTH-FIRST.
- UNIQUE, if given, will iterate nodes uniquely.
- TEST is the comparison test for nodes: it must be something
  acceptable to MAKE-HASH-TABLE.  Default is #'EQL.
- KEY, if given, extracts a key from a node for comparison in the
  usual way.

There is no optimizer.

If the graph is cyclic an iteration using this will not terminate
unless UNIQUE is true, unless some other clause stops it.  If the
graph is not directed you also need to use UNIQUE."
  (check-type order (member :depth-first :breadth-first))
  (let ((agenda (make-collector :initial-contents roots))
        (duplicate-table (if unique (make-hash-table :test test) nil))
        (this nil))
    (values
     (thunk                             ;predicate does all the work
       (if (collector-empty-p agenda)
           nil
         (for ((it (stepping (it :as (pop-collector agenda)))))
           (let ((neighbours (funcall node-neighbours it))
                 (k (and unique (funcall key it))))
             (cond
              ((and unique (gethash k duplicate-table))
               ;; It's a duplicate: skip
               (if (collector-empty-p agenda)
                   (final nil)
                 (next)))
              ((null neighbours)
               ;; Leaf, add it to the duplicate table if need be and say we found something
               (when unique
                 (setf (gethash k duplicate-table) t))
               (setf this it)
               (final t))
              (t
               ;; Not a leaf: update the agenda ...
               (setf agenda
                     (case order
                       (:depth-first
                        (nconc-collectors (make-collector :initial-contents neighbours) agenda))
                       (:breadth-first
                        (nconc-collectors agenda (make-collector :initial-contents neighbours)))))
               ;; .. add it to the duplicate table if need be so it's
               ;; skipped next time ...
               (when unique               
                 (setf (gethash k duplicate-table) t))
               ;; ... and decide if we found something
               (cond
                (only-leaves
                 (if (collector-empty-p agenda)
                     (final nil)
                   (next)))
                 (t
                  (setf this it)
                  (final t)))))))))
     (thunk this))))

  1. 'Lisp' here will usually mean 'Common Lisp'.

  2. Although if you use loop you must accept that you will certainly suffer eternal damnation. Perhaps that's worth it: Robert Johnson thought so, anyway.

  3. This is the same argument that explains why a universal equality predicate is nonsensical: equality of objects depends on what they are equal as and that is often not implicit in the objects.

  4. Or in Lisp, more than likely.

31 Oct 2025 12:40pm GMT

30 Oct 2025

feedFOSDEM 2026

Accepted developer rooms

We are pleased to announce the developer rooms that will be organised at FOSDEM 2026. Developer rooms are assigned to self-organising groups to work together on open source projects, to discuss topics relevant to a broader subset of the community, etc. The individual developer room organisers will issue their calls for participation in the next few days. The list below will be updated accordingly. Topic Call for Participation AI Plumbers CfP Audio, Video & Graphics Creation Bioinformatics & Computational Biology CfP Browser and web platform CfP BSD, illumos, bhyve, OpenZFS CfP Building Europe's Public Digital Infrastructure CfP Collaboration and舰

30 Oct 2025 11:00pm GMT

02 Oct 2025

feedFOSDEM 2026

FOSDEM 2026 Call for Stands

Proposals for stands for FOSDEM 2026 can now be submitted! FOSDEM 2026 will take place at the ULB on the 31st of January and 1st of February 2026. As has become traditional, we offer free and open source projects a stand to display their work to the audience. You can share information, demo software, interact with your users and developers, give away goodies, sell merchandise or accept donations. All is possible! We offer you: One table (180x80cm) with a set of chairs and a power socket Fast wireless internet access You can choose if you want the spot for the舰

02 Oct 2025 10:00pm GMT

20 Sep 2025

feedFOSDEM 2026

FOSDEM 2026 Call for Participation

Proposals for developer rooms and main track talks for FOSDEM 2026 can now be submitted! FOSDEM offers open source and free software developers a place to meet, share ideas and collaborate. Renowned for being highly developer-oriented, the event brings together some 8000+ geeks from all over the world. The twenty-sixth edition will take place on Saturday 31st January and Sunday 1st February 2026 at the usual location, ULB Campus Solbosch in Brussels. Developer Rooms Developer rooms are assigned to self-organising groups to work together on open source and free software projects, to discuss topics relevant to a broader subset of舰

20 Sep 2025 10:00pm GMT