On Tue, Sep 17, 2019 at 8:46 AM John Kitchin wrote: > I don't totally understand what you are trying to do here. > I think the explanation was a little unclear! > If this were Python, it sounds like you want some kind of class that > stores a variable and reuses it several different functions? Something kind > of similar to that in elisp is a closure, which is like what you described. > For example, here, we define a variable a, and then define two functions > that use it persistently. > > > I think you can wrap this in a macro to make new functions, e.g. > > #+BEGIN_SRC emacs-lisp > (defmacro f-maker (a) > `(lexical-let ((a ,a)) > (defun f1 (x) > (* a x)) > > (defun f2 (x) > (+ a x)))) > > (f-maker 3) > > (list (f1 2) (f2 2)) > #+END_SRC > > #+RESULTS: > | 6 | 5 | > > This is basically what I want, except it turned out to be easier to just wrap the body forms in a let *within*the function. THis is what I came up with: #+BEGIN_SRC emacs-lisp (defmacro dh-factory (name body &optional docstring) "A helper macro that sets up the environment to simplify defining multiple functions with the same environment variables. NAME will bcome the functin name, BODY is a list containing the lisp forms specific to the function, and DOCSTRING is an optional ... docstring. NAME wil lbe wrapped in a `let` statement setting all the remelvant variables." `(lexical-let (()) (defun ,name ,() ,docstring (interactive) (let* ((gh (org-entry-get (point) "GITHUB")) (base (org-entry-get (point) "ORG_LMS_ASSIGNMENT_DIRECTORY")) (findFiles `( ,(concat "Reflection/" gh ".md") ,(concat "students/" gh ".json"))) (browseFiles `( "index.html" )) (testOutput "TestResults/testresults.html") (testCommand "MARKING=instructor npm test")) ,@body)))) (dh-factory dh-find-files ((dolist (f findFiles) (message "%s" (concat base "/" f)) (if (file-exists-p (concat base "/" f)) (find-file-other-window (concat base "/" f) ) (message "File %s does not exist, not opening." f))) ) "Open gradable files in Emacs windows") (dh-factory dh-view ((loop for f in browseFiles do (browse-url-of-file (concat base "/" f )))) "open viewable files in browser") ;; this one should really pass a variable to allow different branches! oh well. (dh-factory dh-status ((magit-status base) (let ((currentBranch (shell-command-to-string (format "cd %s && git rev-parse --abbrev-ref HEAD" base) )) (currentCommit (shell-command-to-string (format "cd %s && git rev-parse HEAD" base)))) (magit-stash-worktree (format "Stashed random files from %s after commit %s." currentBranch currentCommit))) (magit-checkout (concat gh "-master"))) "Open a magit-status buffer and check out this student's branch in the repo") (dh-factory dh-tests ((let ((output)) (with-temp-buffer (message (concat "cd " base " ; npm test")) (setq output (shell-command-to-string (concat "cd " base " ; npm test")))) (message "WELL, sorta made it through: %s" output) (browse-url-of-file (concat base testOutput)))) "Run tests directly from macs and view output") #+END_SRC I quite like it, though it's a bit unwieldy not to have the the source code available for edebug etc. It feels a little like having a namespace; I'm not afraid of polluting the global namespace, but I can use these symbol names throughout the code I'm processing via the factory. I'm not sure it's *all* that much more efficient though... > > There is also a class system you can probably use like this called eieio, > Here is one approach to doing this. > > #+BEGIN_SRC emacs-lisp > (require 'eieio) > > (defclass Grade () > ((:a :initarg :a))) > > (cl-defmethod gf1 ((g Grade) x) > (* (oref g :a) x)) > > (cl-defmethod gf2 ((g Grade) x) > (+ (oref g :a) x)) > > (let ((G (Grade :a 3))) > (list (gf1 G 2) (gf2 G 2))) > #+END_SRC > > #+RESULTS: > | 6 | 5 | > > I would love to learn to use eieio but it feels like a bit of a jump. > > Finally, maybe the simplest thing to do is pass this information in as an > argument, e.g. a plist or alist, or get them from a function that you only > change in one place? > That might have been more sensible but this was pretty fun! Thanks everyone for the help! > On Tue, Sep 17, 2019 at 7:31 AM Matt Price wrote: > >> I have a number of convenience functions define to help me with grading >> assignments. As I go through the semester, i update all of these functions >> modestly so that they'rehelpful for grading the current assignment. >> >> I big chunk of these simple functions is taken up just declaring >> variables with (let (())) forms. Each function uses some ofhte same >> variables, e.g: >> >> (defun dh-find-files () >> (interactive) >> (let* ((base (org-entry-get (point) "ORG_LMS_ASSIGNMENT_DIRECTORY")) >> (gh (org-entry-get (point) "GITHUB")) >> (f2o `( ,(concat "Reflection/" gh ".md") ,(concat "students/" gh >> ".json")))) ;;;; "01/index.html" "02/index.html" "03/style.css" >> "04/style.css" >> (message "%s" f2o) >> ;; make more flexible for resubmits >> (shell-command (concat "cd " base " && git checkout " gh "-master")) >> (dolist (x f2o) >> (if (file-exists-p (concat base "/" x)) >> (find-file-other-window (concat base "/" x) ) >> (message "File %s does not exist, not opening." x))))) >> >> (defun dh-tests () >> (interactive) >> (let* ((base (org-entry-get (point) "ORG_LMS_ASSIGNMENT_DIRECTORY" )) >> (gh (org-entry-get (point) "GITHUB"))) >> (with-temp-buffer (shell-command (concat "cd " base " && npm test") >> t)) ;; the "t" lets us suppress buffer >> (browse-url-of-file (concat base "/TestResults/testresults.html")) >> ;; (dh-mocha-run) >> >> )) >> >> ---------- >> >> This semester I changed some elements of my workflow and I had to update >> all the (org-entry-get) calls to new values. It makes me think the code is >> less maintainable than it could be. I would like to do something like this: >> >> (lexical-let ((base `(org-entry-get (point) >> "ORG_LMS_ASSIGNMENT_DIRECTORY") >> (gh `(org-entry-get (point) "GITHUB")) ) >> (defun dh-find-files () >> (with-temp-buffer (shell-command (concat "cd " base " && npm test") t)) >> ;; the "t" lets us suppress buffer >> (browse-url-of-file (concat base "/TestResults/testresults.html"))))) >> >> >> Obviously it doesn't work this way. But is there any way to set macros >> like this to be expanded later inside a function definition? I feel certain >> there must be... >> >> Thanks, >> >> Matt >> >