Skip to content

Dusa @ POPL 2025

The POPL 2025 talk on finite-choice logic programming and Dusa links to this page. Here are some of the examples in the talk and how to run them.

Stop or continue

The โ€œstop or continueโ€ program shown in the talk can almost be cut-and-pasted into Dusa: the only changes are that free variables need to be uppercased, and that Dusa does not have an infix addition.

The simplest path is just to replace N+1 with s N, and use Dusaโ€™s Prolog-style structured terms instead of built-in numbers:

run 0 is { "๐Ÿ›‘", "๐Ÿƒ" }.
run (s N) is { "๐Ÿ›‘", "๐Ÿƒ" } :- run N is "๐Ÿƒ".

You can explore this example on dusa.rocks, but beware: you really want to hit โ€œpauseโ€ relatively quickly before you start generating very large examples.

Your other option is to use Dusaโ€™s built-in addition.

#builtin INT_PLUS plus
run 0 is { "๐Ÿ›‘", "๐Ÿƒ" }.
run (plus N 1) is { "๐Ÿ›‘", "๐Ÿƒ" } :- run N is "๐Ÿƒ".

You can explore this example on dusa.rocks as well: it is a little less eager to crash your browser.

Command-line execution

If you have npx command line utility installed, you can run either of these examples by putting them in a file called run.dusa and, on the command line, running

npx dusa run.dusa -n10

The -n10 argument tells the dusa command-line utility to generate ten solutions.

Map generation

The central generation program example from the talk is this one:

reach "๐Ÿ ".
reach R2 :-
  reach R1,
  next_to R1 R2,
  terrain R2 is "๐ŸŒณ".
terrain R is {"โ›ฐ๏ธ", "๐ŸŒณ", "๐ŸŒŠ"} :-
  region R.
terrain R2 is {"๐ŸŒณ", "๐ŸŒŠ"} :-
  terrain R1 is "๐ŸŒŠ",
  next_to R1 R2.

This code doesnโ€™t actually do much unless we populate the region and next_to relations. We could do that by adding a bunch of facts, but itโ€™s nicer to write a program to generate all the regions. These rules create 26 regions, home and every space on a 5x5 grid:

dim 1. dim 2. dim 3. dim 4. dim 5.
region "๐Ÿ ".
region (coord X Y) :- dim X, dim Y.

To populate the next-to relation, we place home adjacent to the upper-left corner of the map, use an auxillary delta relation along with the built-in addition operation to place every other region adjacent to the region to its right and to the region below it. Finally, we force the relation to be transitive.

#builtin INT_PLUS plus
delta 0 1.
delta 1 0.
next_to "๐Ÿ " (coord 1 1).
next_to (coord X Y) (coord (plus X DX) (plus Y DY)) :-
  delta DX DY,
  dim X,
  dim Y,
  dim (plus X DX),
  dim (plus Y DY).
next_to X Y :- next_to Y X.

The result is the example shown in the in-browser animation during the talk. Explore this example on dusa.rocks.

Additional constraints

We can add additional constraints, for example requiring that the lower-right region be reachable. This uses a new kind of rule, a #demand constraint, that is described in the paperโ€™s appendix and documented here.

#demand reach (coord 5 5).

Another aesthetic preference might be to ensure that thereโ€™s exactly one contiguous ocean in every example. This is easily handled by a variant of the canonical-representative-selection algorithm from Section 3.2 in the paper.

representative X is? X :-
  terrain X is "๐ŸŒŠ".
representative Y is Z :-
  next_to X Y,
  representative X is Z,
  terrain Y is "๐ŸŒŠ".

#demand representative _ is _.
#forbid representative _ is X, representative _ is Y, X != Y.

Explore this example on dusa.rocks.