Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -npartial for arbitrary partial application #204

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

daantjie
Copy link

@daantjie daantjie commented Nov 12, 2016

Currently, we have -partial and -rpartial for currying partial application with
arguments at either the left or the right, but we don't have anything
for truly arbitrary application of curried arguments.

Therefore I've included -npartial to plug that gap. It takes an offset
n to determine where to put the curried arguments, a function fn, and
the curried args.

E.g.:

(funcall (-npartial 1 (lambda (a b c) (+ a (* b c))) 5) 2 3) ;; => 17
(funcall (-npartial 2 (lambda (a b c d) (+ a (* b c) d)) 3) 2 4 5) ;; => 19

(Note that I have a slightly different version of texinfo – 6 instead
of 4 – so the way that the paragraphs have been filled is slightly
differently, but I'm afraid there's nothing much I can do, because Arch
can't downgrade all the way to 4. If this is a big problem, I can go
through and edit this manually, but I'd rather not if it isn't necessary.)

@Fuco1
Copy link
Collaborator

Fuco1 commented Nov 12, 2016

Offset 1 as "first" is confusing I think.. I assumed that skips the first argument.

Wouldn't -cut be enough?

(funcall (-cut (lambda (a b c) (+ a (* b c))) 2 3 <>) 5) ;; => 17

@Fuco1
Copy link
Collaborator

Fuco1 commented Nov 12, 2016

Actually, I think I don't understand how the offset works at all. Because you only give it one argument (5) to be curried partially applied right?

@daantjie
Copy link
Author

I did't spot -cut – true, it does take away some of the usefulness of -npartial, but I suppose for a function with many, many arguments – though I don't know how many of those you'd have – it would mean you wouldn't have to write <> many times.

I'll try to explain the offset better. It basically says where to place the curried arguments in the list of additional arguments. If the offset is 0, -npartial is equivalent to -partial. Assuming fn has arity n, an offset of n would make -npartial equivalent to -rpartial. In effect, the curried arguments are spliced into the list of additional arguments after offset items. The implementation is

(defun -npartial (n fn &rest args)
  (lambda (&rest args-around) (apply fn (append (-take n args-around)
                                                args
                                                (-drop n args-around)))))

It's a bit difficult to write good examples to show this happening – perhaps some sort of string concatenation would be better than a calculation that's difficult to follow – because this is only useful for a function which has many arguments, but I'll give it a shot later.

@daantjie
Copy link
Author

Also: shouldn't the (unless (version< emacs-version "24") ... stop this from failing with Emacs 23?

@Fuco1
Copy link
Collaborator

Fuco1 commented Nov 12, 2016

I don't know why the build fails, it has to do with when the code is compiled vs evaled... Same problem happens in another issue.

We are dropping e23 support anyway so don't worry.

@daantjie
Copy link
Author

daantjie commented Nov 12, 2016

I had a quick look to see how many functions have loads of arguments – and (at least in my config), there are the following functions (w/ arities). Note that these are only the ones that were loaded in Emacs at the time.

  • 6speedbar-insert-button
  • 6speedbar-make-button
  • 5vc-git-create-extra-fileinfo--cmacro
  • 5highlight-markup-buffers
  • 5vc-git-print-log
  • 5vc-git-diff
  • 4xref--rgrep-command
  • 4edebug--make-form-data-entry--cmacro
  • 4with-tramp-file-property
  • 4ad-Advice-byte-compile-log-warning
  • 4speedbar-insert-generic-list
  • 4vc-git-command
  • 4tramp-completion-make-tramp-file-name
  • 4pcomplete-insert-entry
  • 4eieio-read-subclass
  • 4ezimage-insert-over-text
  • 4hilit-chg-set-face-on-change
  • 4packed-libraries
  • 4vc-git-region-history
  • 4helm--completion-in-region
  • 4dabbrev--try-find
  • 4helm-flx-sort
  • 4packed-main-library
  • 4dabbrev--substitute-expansion

Though I really like the syntax of -cut, if you just wanted to curry one of the arguments (in the middle of the arglist) in a hexary function, -npartial is more convenient.

;; (speedbar-insert-button TEXT FACE MOUSE FUNCTION &optional TOKEN PREVLINE)
(-npartial 3 #'speedbar-insert-button #'my/button-function)
(-cut speedbar-insert-button <> <> <> #'my/button-function <> <>)

Though perhaps the latter is more readable! Either way, it's nice to have alternatives – that's why we have aliases, right?

@Fuco1
Copy link
Collaborator

Fuco1 commented Nov 13, 2016

Yea, I don't really have a problem adding this, but it was difficult for me to understand how this works.

Could you please remove the texi/info changes from the patch? I will regenerate it on my version (we haven't figured out yet how to build this reliably... we should probably do that on travis and push back to the repo).

@daantjie
Copy link
Author

daantjie commented Nov 13, 2016

Done! I've made a slight change also, to allow the offset to be negative (i.e. from the end of the list). The commit message is (I think) also more explanatory.


Add `-npartial' for arbitrary partial application

Currently, we have -partial and -rpartial for currying with
arguments at either the left or the right.

-npartial is a generalisation of -partial and -rpartial. It takes
an offset N to determine where to put the curried arguments, a function
FN, and the partially applied ARGS.

ARGS will be spliced in after the Nth element in the additional args. If
N is negative, -1 is taken to be the final item, -2, the penultimate
item, etc.

This function satisfies the following laws:

  (-npartial 0 ...) ≡ (-partial ...)
  (-npartial -1 ...) ≡ (-rpartial ...)

E.g.:

  (funcall (-npartial 1 (lambda (a b c) (+ a (* b c))) 5)
      2 3) ;; => 17
  (funcall (-npartial 2 (lambda (a b c d) (+ a (* b c) d)) 3)
      2 4 5) ;; => 19
  (funcall (-npartial -1 #'concat "last " "words.")
      "These " "are " "the ") ;; => "These are the last words."
  (funcall (-npartial 3 (lambda (a b c d e f) (concat a b c d e f "."))
                      "penultimate " "words ")
      "I'll " "put " "the " "here")
  ;; => "I'll put the penultimate words here."

EDIT: I've also added myself to the list of contributors in the two template files.

@daantjie daantjie changed the title Add -npartial for arbitrary currying Add -npartial for arbitrary partial application Nov 13, 2016
Currently, we have `-partial' and `-rpartial' for currying with
arguments at either the left or the right.

`-npartial' is a generalisation of `-partial' and `-rpartial'. It takes
an offset N to determine where to put the curried arguments, a function
FN, and the partially applied ARGS.

ARGS will be spliced in after the Nth element in the additional args. If
N is negative, -1 is taken to be the final item, -2, the penultimate
item, etc.

This function satisfies the following laws:

  (-npartial 0 ...) ≡ (-partial ...)
  (-npartial -1 ...) ≡ (-rpartial ...)"

E.g.:
  (funcall (-npartial 1 (lambda (a b c) (+ a (* b c))) 5)
      2 3) ;; => 17
  (funcall (-npartial 2 (lambda (a b c d) (+ a (* b c) d)) 3)
      2 4 5) ;; => 19
  (funcall (-npartial -1 #'concat "last " "words.")
      "These " "are " "the ") ;; => "These are the last words."
  (funcall (-npartial 3 (lambda (a b c d e f) (concat a b c d e f "."))
                      "penultimate " "words ")
      "I'll " "put " "the " "here")
  ;; => "I'll put the penultimate words here."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants