From mboxrd@z Thu Jan 1 00:00:00 1970 From: John Kitchin Subject: Re: lisp: scoping vars in repetitive defuns Date: Tue, 17 Sep 2019 08:46:15 -0400 Message-ID: References: Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="000000000000d6d45e0592bf1bdb" Return-path: Received: from eggs.gnu.org ([2001:470:142:3::10]:38326) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iACsI-0005Fw-Mf for emacs-orgmode@gnu.org; Tue, 17 Sep 2019 08:46:33 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iACsG-0008Bk-Oe for emacs-orgmode@gnu.org; Tue, 17 Sep 2019 08:46:30 -0400 Received: from mail-wm1-x336.google.com ([2a00:1450:4864:20::336]:32781) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1iACsG-0008BK-BM for emacs-orgmode@gnu.org; Tue, 17 Sep 2019 08:46:28 -0400 Received: by mail-wm1-x336.google.com with SMTP id r17so2364257wme.0 for ; Tue, 17 Sep 2019 05:46:28 -0700 (PDT) In-Reply-To: List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: "Emacs-orgmode" To: Matt Price Cc: Org Mode --000000000000d6d45e0592bf1bdb Content-Type: text/plain; charset="UTF-8" I don't totally understand what you are trying to do here. 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. #+BEGIN_SRC emacs-lisp (lexical-let ((a 5)) (defun f1 (x) (* a x)) (defun f2 (x) (+ a x))) (list (f1 2) (f2 2)) #+END_SRC #+RESULTS: | 10 | 7 | 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 | 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 | 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? John ----------------------------------- Professor John Kitchin Doherty Hall A207F Department of Chemical Engineering Carnegie Mellon University Pittsburgh, PA 15213 412-268-7803 @johnkitchin http://kitchingroup.cheme.cmu.edu 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 > --000000000000d6d45e0592bf1bdb Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I don't totally understand what you are trying to do h= ere. If this were Python, it sounds like you want some kind of class that s= tores 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 t= hat use it persistently.

#+BEGIN_SRC emacs-lisp
(lexi= cal-let ((a 5))
=C2=A0 (defun f1 (x)
=C2=A0 =C2=A0 (* a x))

= =C2=A0 (defun f2 (x)
=C2=A0 =C2=A0 (+ a x)))


(list (f1 2) (f2= 2))
#+END_SRC

#+RESULTS:
| 10 | 7 |

I think you can wrap this in a macro to make new functions, e.= g.=C2=A0

#+BEGIN_SRC emacs-lisp
(defmacro f-mak= er (a)
=C2=A0 `(lexical-let ((a ,a))
=C2=A0 =C2=A0 =C2=A0(defun f1 (x= )
=C2=A0 =C2=A0 =C2=A0 =C2=A0(* a x))

=C2=A0 =C2=A0 =C2=A0(defun = f2 (x)
=C2=A0 =C2=A0 =C2=A0 =C2=A0(+ a x))))

(f-maker 3)

(= list (f1 2) (f2 2))
#+END_SRC

#+RESULTS:
| 6 | 5 |
<= br>

There is also a class system you can probably = use like this called eieio, Here is one approach to doing this.
<= br>
#+BEGIN_SRC emacs-lisp
(require 'eieio)

(defcla= ss Grade ()
=C2=A0 ((:a :initarg :a)))

(cl-defmethod gf1 ((g Grad= e) x)
=C2=A0 (* (oref g :a) x))

(cl-defmethod gf2 ((g Grade) x)=C2=A0 (+ (oref g :a) x))

(let ((G (Grade :a 3)))
=C2=A0 (list = (gf1 G 2) (gf2 G 2)))
#+END_SRC

#+RESULTS:
| 6 | 5 |
<= div dir=3D"ltr" class=3D"gmail_signature" data-smartmail=3D"gmail_signature= ">


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 yo= u only change in one place?=C2=A0

John

-----------------------------------
Professor John = Kitchin=C2=A0
Doherty Hall A207F
Department of Chemical EngineeringCarnegie Mellon University
Pittsburgh, PA 15213
412-268-7803
<= div dir=3D"ltr">@johnkitchin
http://kitchingroup.cheme.cmu.edu



<= div dir=3D"ltr" class=3D"gmail_attr">On Tue, Sep 17, 2019 at 7:31 AM Matt P= rice <moptop99@gmail.com> w= rote:
I have a number of convenience functions define to help me wi= th grading assignments. As I go through the semester, i update all of these= functions modestly so that they'rehelpful for grading the current assi= gnment.=C2=A0

I big chunk of these simple fun= ctions is taken up just declaring variables with (let (())) forms.=C2=A0 Ea= ch function uses some ofhte same variables, e.g:

(= defun dh-find-files ()
=C2=A0 (interactive)
=C2=A0 (let* ((base (org-= entry-get (point) "ORG_LMS_ASSIGNMENT_DIRECTORY"))
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0(gh (org-entry-get (point) "GITHUB"))
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 (f2o `( ,(concat "Reflection/" gh &qu= ot;.md") ,(concat "students/" gh ".json")))) ;;;; = "01/index.html" "02/index.html" "03/style.css"= ; "04/style.css"
=C2=A0 =C2=A0 (message "%s" f2o)=C2=A0 =C2=A0 ;; make more flexible for resubmits
=C2=A0 =C2=A0 (shell-= command (concat "cd " base " && git checkout " = gh "-master"))
=C2=A0 =C2=A0 (dolist (x f2o)
=C2=A0 =C2=A0 = =C2=A0 (if (file-exists-p (concat base "/" x))
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 (find-file-other-window (concat base "/" x) = )
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (message "File %s does not exist, not= opening." x)))))

(defun dh-tests ()
= =C2=A0 (interactive)
=C2=A0 (let* ((base (org-entry-get (point) "OR= G_LMS_ASSIGNMENT_DIRECTORY" ))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(= gh (org-entry-get (point) "GITHUB")))
=C2=A0 =C2=A0 (with-temp= -buffer (shell-command (concat "cd " base " && npm t= est") t)) ;; the "t" lets us suppress buffer
=C2=A0 =C2= =A0 (browse-url-of-file (concat base "/TestResults/testresults.html&qu= ot;))
=C2=A0 =C2=A0 ;; (dh-mocha-run)
=C2=A0 =C2=A0
=C2=A0 =C2=A0= ))

----------

This semester I changed some= elements of my workflow and I had to update all the (org-entry-get) calls = to new values.=C2=A0 It makes me think the code is less maintainable than i= t could be.=C2=A0 I would like to do something like this:

(lexical-let ((base `(org-entry-get (point) "ORG_LMS_ASSIGNMEN= T_DIRECTORY")
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (gh `(org-e= ntry-get (point) "GITHUB")) )
=C2=A0=C2=A0=C2=A0 (defun= dh-find-files ()
(with-temp-buffer (shell-command (concat "= cd " base " && npm test") t)) ;; the "t" l= ets us suppress buffer
=C2=A0 =C2=A0 (browse-url-of-file (concat base &q= uot;/TestResults/testresults.html")))))


<= /div>
Obviously it doesn't work this way. But is there any way to s= et macros like this to be expanded later inside a function definition? I fe= el certain there must be...

Thanks,

=
Matt
--000000000000d6d45e0592bf1bdb--