branch: elpa/llama commit b890bfb76aea8df253c5baf3a95df008b67dd1dc Author: Jonas Bernoulli <jo...@bernoul.li> Commit: Jonas Bernoulli <jo...@bernoul.li>
Revive and give up on display magic No longer suggest using `l' instead but also give up on using `display' property to display it as ##(...). --- README.md | 106 +++++++++++++++++------------------------- llama.el | 155 +++++++++++++++++--------------------------------------------- 2 files changed, 83 insertions(+), 178 deletions(-) diff --git a/README.md b/README.md index 7cc580ef7b..30189c2e12 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,41 @@ -Compact syntax for short lambdas. - -Unfortunately compact syntax for short `lambda`s won't be added to -Emacs anytime soon. IMO the arguments as to why we would like to have -that has been layed out convincingly but the proposal has been -rejected anyway. - -Several packages exist that implement anonymous function literals, -but until now they all either are waiting for a patch to the C part -be merged into Emacs, or they depart too far from the ideal syntax. - -This package is another attempt. - -> **Update:** I have come up with a second syntax, which does not put -> anything *before* the opening parenthesis and looks like `(l'foo %)`. -> Unlike with my original `llama` idea I am no longer suggesting making -> it look like some character(s) appear before the paren using font-lock -> or similar trickery (see below). If you so desire, then you could -> however use `prettify-symbols-mode` to display it as e.g. `(ƒ'foo -> %)`. This approach is implemented in [`l`](https://git.sr.ht/~tarsius/l). - -In a stroke of luck I discovered a loophole that allows us to have -almost the syntax that we want without having to convince anyone. - - $(foo %) is what I would have used if it were up to me. - #(foo %) works just as well for me. - ##(foo %) is similar enough to that. - (##foo %) is the loophole that I discovered. - -Even though there is no space between the second `#` and `foo`, this -last form is read as a list with three arguments `(## foo %)` and it -is also indented the way we want! - - (##foo % - bar) - -This is good enough for me, but with a bit of font-lock trickery, -we can even get it to be display like this: - - ##(foo % - bar) - -This is completely optional and you have to opt-in by enabling -`llama-mode` (or the global variant `global-llama-mode`). - -An unfortunate edge-case exists that you have to be aware off; if -no argument is placed on the same line as the function, then Emacs -does not indent as we would want it too: - - (##foo ##(foo - bar) which llama-mode displays as bar) - -I recommend that in this case you simply write this instead: - - (## foo ##( foo - bar) which llama-mode displays as bar) - -It is my hope that this package helps to eventually get similar -syntax into Emacs itself, by demonstrating that this is useful and -that people want to use it. - -*Also see the [announcement](https://emacsair.me/2021/01/28/llama-0.1).* - -*Also see [`l`](https://git.sr.ht/~tarsius/l), my second attempt at -faking such syntax.* +This package implements the macro `##`, which provides compact +syntax for short `lambda`, without actually being new syntax, +which would be difficult to get merged into Emacs. Past attempts +to add syntax were met with determined pushback and the use of a +macro was suggested as an alternative. + +The `##` macro, whose signature is `(## FN &rest args)`, expands +to a `lambda` expressions, which wraps around its arguments. + +This `lambda` expression calls the function FN with arguments +ARGS and returns its value. Its own arguments are derived from +symbols found in ARGS. Each symbol from `%1` through `%9`, which +appears in ARGS, is treated as a positional argument. Missing +arguments are named `_%N`, which keeps the byte-compiler quiet. +In place of `%1` the shorthand `%` can be used, but only one of +these two can appear in ARGS. `%*` represents extra `&rest` +arguments. + +Instead of: + +```elisp +(lambda (a _ c &rest d) + (foo a (bar c) d)) +``` + +you can use this macro and write: + +```elisp +(##foo % (bar %3) %*) +``` + +which expands to: + +```elisp +(lambda (% _%2 %3 &rest %*) + (foo % (bar %3) %*)) +``` + +The name `##` was choosen because that allows (optionally) +omitting the whitespace between it and the following symbol. +It also looks similar to `#'function`. diff --git a/llama.el b/llama.el index 4baae2201e..250684b263 100644 --- a/llama.el +++ b/llama.el @@ -1,4 +1,4 @@ -;;; llama.el --- Anonymous function literals -*- lexical-binding:t -*- +;;; llama.el --- Compact syntax for short lambda -*- lexical-binding:t -*- ;; Copyright (C) 2020-2022 Jonas Bernoulli @@ -25,61 +25,41 @@ ;;; Commentary: -;; This package implements compact syntax for short `lambda's, without -;; relying on a C patch to Emacs or adding an additional pair of -;; parentheses. - -;; [!] I have come up with another approach that does not put anything -;; [!] before the opening parenthesis: https://git.sr.ht/~tarsius/l. - -;; Unfortunately anonymous function literals won't be added to Emacs -;; anytime soon. The arguments as to why we would like to have that -;; has been layed out convincingly but the proposal has been rejected -;; anyway. - -;; Several packages exist that implement anonymous function literals, -;; but until now they all either are waiting for a patch to the C part -;; be merged into Emacs, or they depart too far from the ideal syntax. - -;; In a stroke of luck I discovered a loophole that allows us to have -;; almost the syntax that we want without having to convince anyone. +;; This package implements the macro `##', which provides compact +;; syntax for short `lambda', without actually being new syntax, +;; which would be difficult to get merged into Emacs. Past attempts +;; to add syntax were met with determined pushback and the use of a +;; macro was suggested as an alternative. + +;; The `##' macro, whose signature is (## FN &rest args), expands +;; to a `lambda' expressions, which wraps around its arguments. + +;; This `lambda' expression calls the function FN with arguments +;; ARGS and returns its value. Its own arguments are derived from +;; symbols found in ARGS. Each symbol from `%1' through `%9', which +;; appears in ARGS, is treated as a positional argument. Missing +;; arguments are named `_%N', which keeps the byte-compiler quiet. +;; In place of `%1' the shorthand `%' can be used, but only one of +;; these two can appear in ARGS. `%*' represents extra `&rest' +;; arguments. + +;; Instead of: ;; -;; $(foo %) is what I would have used if it were up to me. -;; #(foo %) works just as well for me. -;; ##(foo %) is similar enough to that. -;; (##foo %) is the loophole that I discovered. - -;; Even though there is no space between the second # and `foo', this -;; last form is read as a list with three arguments (## foo %) and it -;; is also indented the way we want! +;; (lambda (a _ c &rest d) +;; (foo a (bar c) d)) ;; -;; (##foo % -;; bar) - -;; This is good enough for me, but with a bit of font-lock trickery, -;; we can even get it to be display like this: +;; you can use this macro and write: ;; -;; ##(foo % -;; bar) +;; (##foo % (bar %3) %*) ;; -;; This is completely optional and you have to opt-in by enabling -;; `llama-mode' (or the global variant `global-llama-mode'). - -;; An unfortunate edge-case exists that you have to be aware off; if -;; no argument is placed on the same line as the function, then Emacs -;; does not indent as we would want it too: +;; which expands to: ;; -;; (##foo ##(foo -;; bar) which llama-mode displays as bar) +;; (lambda (% _%2 %3 &rest %*) +;; (foo % (bar %3) %*)) -;; I recommend that in this case you simply write this instead: -;; -;; (## foo ##( foo -;; bar) which llama-mode displays as bar) - -;; It is my hope that this package helps to eventually get similar -;; syntax into Emacs itself, by demonstrating that this is useful and -;; that people want to use it. +;; The name `##' was choosen because that allows (optionally) +;; omitting the whitespace between it and the following symbol. +;; It also looks similar to #'function. ;;; Code: @@ -87,17 +67,16 @@ ;;;###autoload (defmacro ## (fn &rest args) - "Return an anonymous function. - -The returned function calls the function FN with arguments ARGS -and returns its value. + "Expand to a `lambda' expression that wraps around FN and ARGS. -The arguments of the outer function are determined recursively -from ARGS. Each symbol from `%1' through `%9' that appears in -ARGS is treated as a positional argument. Missing arguments -are named `_%N', which keeps the byte-compiler quiet. `%' is -a shorthand for `%1'; only one of these can appear in ARGS. -`%*' represents extra `&rest' arguments. +This `lambda' expression calls the function FN with arguments +ARGS and returns its value. Its own arguments are derived from +symbols found in ARGS. Each symbol from `%1' through `%9', which +appears in ARGS, is treated as a positional argument. Missing +arguments are named `_%N', which keeps the byte-compiler quiet. +In place of `%1' the shorthand `%' can be used, but only one of +these two can appear in ARGS. `%*' represents extra `&rest' +arguments. Instead of: @@ -113,13 +92,9 @@ which expands to: (lambda (% _%2 %3 &rest %*) (foo % (bar %3) %*)) -Note that there really does not have to be any whitespace -between \"##\" and \"foo\"! - -If you enable `llama-mode' or `global-llama-mode', then the -above is *displayed* as: - - ##(foo % (bar %3) %*)" +The name `##' was choosen because that allows (optionally) +omitting the whitespace between it and the following symbol. +It also looks a bit like #\\='function." (unless (symbolp fn) (signal 'wrong-type-argument (list 'symbolp fn))) `(lambda ,(llama--arguments args) @@ -158,52 +133,6 @@ above is *displayed* as: (seq-doseq (elt data) (llama--collect elt args))))) -(defvar llama--keywords - '(("(##" (0 (llama--transpose))))) - -(defun llama--transpose () - (and (not (nth 8 (syntax-ppss))) - `(face nil display "##("))) - -(defvar-local llama--display-already-managed nil) - -;;;###autoload -(define-minor-mode llama-mode - "Toggle Llama mode. - -When Llama mode is enabled then forms like (##foo %) are -displayed as ##(foo %) instead. - -You can enable this mode locally in desired buffers, or use -`global-llama-mode' to enable it in all `emacs-lisp-mode' -buffers." - :init-value nil - (cond - (llama-mode - (font-lock-add-keywords nil llama--keywords) - (add-to-list 'font-lock-extra-managed-props 'display) - (when (memq 'display font-lock-extra-managed-props) - (setq llama--display-already-managed t))) - (t - (font-lock-remove-keywords nil llama--keywords) - (if llama--display-already-managed - (setq llama--display-already-managed nil) - (setq font-lock-extra-managed-props - (delq 'display font-lock-extra-managed-props))) - (with-silent-modifications - (remove-text-properties (point-min) (point-max) '(display nil))))) - (font-lock-flush)) - -;;;###autoload -(define-globalized-minor-mode global-llama-mode - llama-mode llama--turn-on-mode - :group 'lisp) - -(defun llama--turn-on-mode () - (when (and (not llama-mode) - (derived-mode-p 'emacs-lisp-mode)) - (llama-mode 1))) - ;;; _ (provide 'llama) ;; Local Variables: