reddit / emacs

How to associate functions with object?

xorlop · 2022-03-21 15:30
I am working on a new package but I need help because I don't really know how to "link" a function to an object. Here is the current structure of my code: (defmacro print-first (first) "Print FIRST name." `(message ,first)) (defmacro print-full (first last) "Print FIRST and LAST name." `(message ,(concat first " " last))) (defun make-pet (first-name last-name) "Create a pet using FIRST-NAME and LAST-NAME." (make-record 'pet 1 `(first-name last-name (print-first first-name) (print-full first-name last-name)))) Basically I want a user to be able to define a pet, and my library should be able to generate functions for that pet. In my actual library, some of these functions modify the properties of the pet itself not shown here. **Is what I have shown here the best way to do this? Should I be using** `make-record`? Also, are there any tips for testing macros? I am reading about how when they are compiled and ran that they behave differently than when evaluated and ran.
patrick_thomson · 2022-03-21 16:07
What you want here is probably `eieio`. `make-record` and `record` are lower-level, and sticking functions in the slots of a record doesn’t, as you’ve noticed, allow you to change instance variables. Use `defclass` to declare a `pet` class, then use `cl-defmethod` or `cl-defgeneric` to declare whatever object methods you need. If you want to do some magic function definitions with macros, the body of that macros should probably be a bunch of `defmethod` calls. As far as testing macros, I don’t have a huge amount of advice, other than the fact that macros should generally be a tool of last resort. Your `print-first` and `print-full` functions don’t take advantage of any of the power of macros, so they and other such functions should be functions or methods.
xorlop · 2022-03-21 16:11
Thank you so much this is exactly the response I was hoping for! Have a great day! :)
ieure · 2022-03-21 16:13
There's no implicit encapsulation of data and methods in Emacs Lisp like in OOP-centric languages. Records and objects only hold data. Your functions need to take the pet record as their first argument and extract the data they need from the record's slots. You can check out [my EmacsConf 2021 talk](https://emacsconf.org/2021/talks/form/) which covers OOP in Emacs Lisp using EIEIO for more information. It's a little adjacent to where you are now, but most things are still applicable.
xorlop · 2022-03-21 16:33
Thank you so much! Excited to dig into this :)
deaddyfreddy · 2022-03-21 16:50
what's the advantage of associating functions with data?
[deleted] · 2022-03-21 19:22
`cl-defstruct` should probably be enough, you can define methods on those too.
TheDrownedKraken · 2022-03-21 23:30
You could also leverage lexical binding and closures to do this. As an example: ```lisp ;; -*- lexical-binding: t -*- (setq get-name nil) (setq set-name nil) (let ((name "Joe")) (setq get-name (lambda () name)) (setq set-name (lambda (x) (setq name x)))) (funcall get-name) ;; => “Joe” (funcall set-name "Schmo") (funcall get-name) ;; => “Schmo” ```
oantolin · 2022-03-22 13:25
The examples are missing a `funcall`, they should say `(funcall get-name)` and `(funcall set-name "Schmo")`. (Also all your fancy “ quotes should be normal straight quotes ".)
TheDrownedKraken · 2022-03-22 14:01
Thank you. I always forget `funcall`. I write a lot of Scheme, and I always forget about it when I write Elisp or CL. And I am really annoyed at the fancy quotes. I just typed it on my phone, and I wish it wouldn’t use those automatically.
TheDrownedKraken · 2022-03-22 16:58
Can you set the function value of an identifier so it can be called normally? Does something like `setq-function-value` exist?
oantolin · 2022-03-22 17:36
You can use `(setf (symbol-function 'identifier) (lambda...))`.
oantolin · 2022-03-22 17:50
I answered in another comment, but forgot to say that for your example above you can just use defun inside the let (and remove the setq nil from outside the let which set the unrelated variables with the same name as the function you defun).
TheDrownedKraken · 2022-03-22 21:30
Heh, in Scheme doing that wouldn’t expose the definition to the enclosing environment, so you have to define something in the outer scope to set first. You could expose it some other way of course, like returning a dispatcher lambda though. It’s hard to keep track what is and isn’t lexically scoped in Emacs Lisp. I really bounced off of Common Lisp, and I tolerate Emacs Lisp because of Emacs. I always found it very hard to get a mental model for what the name of a standard library function would be (or whether something *was* a standard library function). With Scheme, it’s usually pretty obvious.
TheDrownedKraken · 2022-03-22 21:30
Nice. Thanks.
oantolin · 2022-03-22 21:54
Yeah, maybe it is a little weird that in both Common Lisp and Emacs Lisp `defun` defines global functions. For local functions you use `flet` (like `let`) or `labels` (like `letrec`); and in Emacs Lisp those have a `cl-` prefix.