04 Nov 2026

Follow-up: QuickCheck: shrinking like tuples, writing like applicative

Joachim wrote that

Your (\*/) operator reminds me of the applicative instance defined in https://hackage.haskell.org/package/successors-0.1.0.3/docs/Control-Applicative-Successors.html based on https://stackoverflow.com/a/41944525.

Ah, yes indeed! I actually started out defining an Applicative for Wane, but convinced myself that it wouldn't satisfy the laws. Maybe I was a little too quick to dismiss it.

Tags: haskell quickcheck
04 Nov 2026

Follow-up: Emacs salmagundi, 2026-10-11

A comment from Dave on Emacs salmagundi, 2026-10-11:

I preload compile-history in some directory trees via .dir-locals.el, and then I can just type M-x compile and choose something from the history. I document this at https://davemq.github.io/2026/05/12/remote-linux-dev-with-emacs.html.

That's a very nice tip!

He continues

This multi-compile package looks interesting. But neither your article or the README for the package have examples of running it. Do I type M-x multi-compile?

No, the command is actually M-x multi-compile-run. I probably should have mentioned that at least once when writing about it.

In the upstream repo the information is located at the very end of the README. It probably should be mentioned much earlier.

Tags: emacs
04 Nov 2026

Follow-up: Switching to project.el

A long while ago (in February this year) I got nice comment from Mike on my switch to project.el

That said, I was surprised to see your comment about having to set vc-handled-backends to nil because otherwise vc interferes with magit, as I have not seen any such conflicts myself, and I made the transition to project.el several years ago.

Maybe it's not worth it to get into it: you obviously found a straightforward workaround, and perhaps there's additional background that you simply didn't include, or I'm over-interpreting "interfere" when you just mean some quirk that I just no longer notice having lived with it for long enough—but it seemed worth it to mention that this may not be universal, and thus might be worth looking into?

I did a search through my commit history and found that I disabled it in August 2024:

Disable VC completely

Apparently VC was activated when loading the config as all of it is in git repos (thanks to straight). This resulted in a question about following a symbolic link in a git repo (thanks to 'vs-follow-links being set to 'ask). I could have turned off the asking, but since I prefer to use magit anyway and I don't use any other VCS I decided to turn VC off completely for now.

Yes, there's a type in there, vs-follow-links should be vc-follow-symlinks I believe.

I reverted about two years later when I switched to elpaca:

Remove the config for VC

It looks like the need for disabling VC disappeared with the move from straight. Now only .projectile is an extra marker for projects.

I would be very surprised if my workaround is still needed when using straight.

Tags: emacs
13 Oct 2026

QuickCheck: shrinking like tuples, writing like applicative

The QuickCheck documentation for Arbitrary says that

There is no generic arbitrary implementation included because we don't know how to make a high-quality one.

Though in practice I've found that very often it suffices to write one that uses the fact that Gen is an Applicative. For instance, this type

data Foo = Foo
    { tata :: Int
    , titi :: Double
    }

would get an implementation that looks like this

instance Arbitrary Foo where
    arbitrary = Foo <$> arbitrary <*> arbitrary

When it comes to writing the function for shrinking Foo it is possible to rely on a generic function and the documentation says that

The default implementation returns the empty list, so will not try to shrink the value. If your data type has no special invariants, you can enable shrinking by defining shrink = genericShrink, but by customising the behaviour of shrink you can often get simpler counterexamples.

Based on some manual testing it seems that genericShrink generates something like this for the type Foo above

instance Arbitrary Foo where
    shrink (Foo tata_ titi_) = Foo <$> shrink tata_ <*> shrink titi_

which means that if shrink tata_ generates a list of m elements and shrink titi_ generates a list of n elements, then shrink foo will generate a list of m×n elements. It's clear that the size can become problematic with more fields. It also means that it's very easy to get an empty list – if shrink v results in an empty list, then shrink foo is also empty. Oh, and shrink Nothing is []!

A colleague pointed out that tuples have a rather efficient implementation of shrink that grows linearly with the number of elements and doesn't reduce to the empty list as easily. I could use it for Foo like this

instance Arbitrary Foo where
    shrink (Foo tata_ titi_) = uncurry Foo <$> shrink (tata_, titi_)

However, that doesn't scale that well to more fields. Up to a point I can use list comprehensions, but it gets a bit difficult to read, I find. On top of that, the largest tuple with an Arbitrary instance has 10 elements, and then I'll have to either define more or use inner tuples.

Wouldn't it be nice to write shrink in something that looks Applicative, but behaves like tuples?

Here's one approach to this.

First a type to hold information about what's being shrunk, or in this case waned

data Wane a = Wane a [a]
    deriving (Generic, Show)

instance Functor Wane where
    fmap f (Wane a as) = Wane (f a) (f <$> as)

Then a couple of functions to bring values in and out, unWane to get the list of values out and wane to wrap a value in Wane

unWane :: Wane a -> [a]
unWane (Wane _ as) = as

wane :: (Arbitrary a) => a -> Wane a
wane a = Wane a (shrink a)

Finally a way to combine two Wane values is needed, and here I was inspired by the implementation of Arbitrary for (a,b)

infixl 4 \*/

(\*/) ::  Wane (a -> b) -> Wane a -> Wane b
(\*/) (Wane f fs) (Wane a as) =  Wane (f a) ([f a' | a' <- as] <> [f' a| f' <- fs])

It's now possible to write shrink for Foo like this

instance Arbitrary Foo where
    shrink (Foo tata_ titi_) = unWane $ Foo <$> wane tata_ \*/ wane titi_

That scales to more fields just as nicely as applicative style, and the result matches what the variant using tuples produces (same elements, but the order is different).

Tags: haskell quickcheck
13 Oct 2026

Emacs salmagundi, 2026-10-11

Combining lists of compilation commands

To my delight I found that emacs-multi-compile will combine the of all matches compilation commands!

I added this to my setup

(add-to-list 'multi-compile-alist
               '((mes/is-in-git-project) . (("git clean -n" "git clean -ndx" (project-root (project-current)))
                                            ("git clean -f" "git clean -fdx" (project-root (project-current))))))

and those commands are now shown together with the ones I defined for Haskell project (see my previous post) when both match.

Recognising a git project

To add the git commands to multi-compile-list I needed a function to determine if the current buffer is in a git working tree. I failed to find something fitting so I ended up adding this little function.

(defun mes/is-in-git-project ()
  "Check if we're currently in a git project."
  (when-let* ((proj-dir (project-root (project-current))))
    (vc-git-root proj-dir)))

Taming compilation mode buffers

I'm using display-buffer-alist to control how Emacs places buffers. For compilation mode I had the following actions.

(display-buffer-reuse-mode-window display-buffer-in-side-window)
(mode compilation-mode)
(window-height . 0.25)
(side . bottom)
(post-command-select-window . t)

That last command made switching buffers with consult-buffer a bit irritating as previewing would interrupt the buffer switching by selecting the compilation window. However, I really want the compilation window selected after a compilation. Or rather, as I realised after reflecting a bit, I want the compilation window selected if there are errors.

Compilation mode has a way of running functions when a compilation finishes so I defined two functions and added them to compilation-finish-functions.

(defun mes/close-successful-compilation (buf msg)
  "Close compilation buffer after success."
  (when (and (buffer-live-p buf)
             (with-current-buffer buf (eq major-mode 'compilation-mode))
             (string-match "finished" msg))
    (run-with-timer 3 nil
                    (lambda ()
                      (kill-buffer buf)))))

(defun mes/select-failed-compilation (buf msg)
  "Select compilation buffer after failed compilation."
  (when (and (buffer-live-p buf)
             (with-current-buffer buf (eq major-mode 'compilation-mode))
             (string-match "abnormally" msg))
    (select-window (get-buffer-window buf))))

  (add-to-list 'compilation-finish-functions #'mes/close-successful-compilation)
  (add-to-list 'compilation-finish-functions #'mes/select-failed-compilation)

With that a successful compilation is closed automatically after 3s and on a failed compilation the compilation buffer is selection.

Tags: emacs
Other posts