I’ve just released v0.3 of suggest.el, an Emacs package for discovering elisp functions. You supply an example input and output, and it makes suggestions.
v0.3 is much smarter, and almost magical in places. Let’s take a look.
What is this, anyway?
Communicating what suggest.el does is difficult. Bodil’s tweet did a much better job of communicating usage, so I’ve overhauled the README. It’s now full of examples like this:
suggest.el can also help you find tricky dash.el functions:
;; Inputs (one per line): (list 'a 'b 'c 'd) 'c ;; Desired output: 2 ;; Suggestions: (-elem-index 'c (list 'a 'b 'c 'd)) ;=> 2
This helps users get an idea of what they can do with suggest.el.
Let’s not segfault Emacs
It turns out that brute-forcing elisp primitives can expose some nasty Emacs bugs. suggest.el will no longer make your Emacs crash, and upstream have fixed the underlying issue.
Isn’t there a function for that?
Since the initial release, suggest.el can now suggest 38 additional functions. suggest.el is now able to help you find values in a vector, convert symbols to strings, and much more besides. Serendipitous discoveries are now much more likely.
Any number of arguments
If your input is a list, you might be able to call a function with
apply to get your desired result. Given an input
'(2 3) and an
5, suggest.el can now propose
(apply #'+ '(2 3))!
suggest.el had a major limitation: what if you need to combine a few functions to get the result you want?
I added a breadth-first search of nested function calls. Using a few judicious search heuristics, suggest.el can search for up to three nested function calls.
This proved to be a really fun feature that sometimes produces surprising results:
;; Inputs (one per line): 0.0 ;; Desired output: 3.0 ;; Suggestions: (1+ (1+ (1+ 0.0))) ;=> 3.0 (float (length (number-to-string 0.0))) ;=> 3.0
Converting to a string, then taking the length, does indeed produce 3.0!
> (number-to-string 0.0) "0.0" > (length "0.0") 3 > (float 3) 3.0
Sorting results is still an open problem: suggest.el prefers fewer function calls with a crude notion of ‘simple’ functions.
;; Inputs (one per line): '(a b c) ;; Desired output: 'c ;; Suggestions: (cl-third '(a b c)) ;=> 'c (-last-item '(a b c)) ;=> 'c (cadr (cdr '(a b c))) ;=> 'c (car (last '(a b c))) ;=> 'c (apply #'last (last '(a b c))) ;=> 'c
(car (last ...)) is largely a matter
of taste. The last result is entirely silly.
Testing the search
I’ve also written a series of tests to verify that my search returns the intended result (in addition to any others). This is really helpful when tweaking search heuristics.
I wrapped this up in a pretty macro:
(ert-deftest suggest-possibilities () ;; A particularly deep search. (should-suggest 0 => 3 (1+ (1+ (1+ _)))) ;; Ensure we offer built-in list functions. (should-suggest '(a b c d) => '(c d) (cdr (cdr _))))
However, the implementation of the
should-suggest macro is not
pretty at all. Macros are great for finding better ways of expressing
ideas, but I often find they produce insight that leads to refactoring
them away entirely. Time will tell if this macro is worth the
suggest.el v0.3 is available on MELPA. If you discover some interesting function combinations, I’d love to hear about them!