libera/#commonlisp - IRC Chatlog
Search
16:23:24
mfiano
Has anyone any reference apart from AMOP on how to add additional slot options to a class via a metaclass? I would like to add a :encoder and :decoder slot option, that decides on how slot values are serialized and deserialized when parsing/unparsing.
17:00:51
mfiano
In reading the MOP a bit, I found the way to do this is to create specialized instances of direct-slot-definition-class and effective-slot-definition-class, and define methods for them that return those classes. However, I am a bit confused in that these generic functions have a single argument to specialize, the class to add these new slot definitions to. I therefor am unsure how to add _two_
17:03:51
Bike
i am not sure what you mean. neither of those generic functions add slots to any classes. they just indicate what the classes of slot definitions should be.
17:04:08
mfiano
I would like a class to have two new additional slot options, such as the slot specification (foo :reader foo :initarg :foo :encoder #'some-encoder :decoder #'some-decoder)
17:05:18
mfiano
Ok, do I have to subclass both standard-direct-slot-definition and standard-effective-slot-definition classes with the same body?
17:05:57
Bike
you can have (defclass my-slot-definition (standard-slot-definition) ((%encoder ...) (%decoder ...))) and then have your my-direct-slot-definition and my-effective-slot-definition be subclasses of that
17:07:26
mfiano
I am unsure where to put the supers standard-direct-slot-definition and standard-effective-slot-definition
17:10:27
Bike
unless you're doing something much more exotic than you are, you should probably have your direct slot def class inherit from standard-dsd, and your effective from standard-esd, and you can have them both inherit from the class with the %encoder and %decoder slots to save some typing
17:10:42
mfiano
I will admit, I never used the MOP before to extend slot definitions, just trivial metaclasses. This is new and fun to learn.
17:24:10
mfiano
Bike: Is this what you were talking about? https://gist.github.com/mfiano/b824a68c58d72501afd5fca6005e66e8
17:26:17
Bike
did you compile-file this? it might have tried to define the class before the method was loaded.
17:31:14
Bike
You need to specify the metaclass, yes. since the metaclass can influence how inheritances work, it's necessarily prior
17:34:16
Bike
(class-of (find-class 'foo)) will be zfs-property-class, which is not a zfs-property-class
17:34:31
Bike
try (class-slots (find-class 'foo)), you should see zfs-property-effective-slot-definitions
17:34:51
Bike
however they will not have the information you want until you define a compute-effective-slot-definition method
17:36:40
mfiano
Oh I see. Yes that is the correct class, but the meta slots are unbound. I have more to learn it seems with regard to compute-*-slot-definition
17:37:16
mfiano
Wow lots of boilerplate here for a simple task. I have to learn how to do this so I can abstract it away for similar things to do for other classes.
17:38:25
Bike
what that function does is take all the direct slot definitions from the class precedence list, and combine them together into an effective slot definition
17:39:04
Bike
This is necessary because there's no obvious way to combine inherited slot information; even the standard slot options inherit in all kinds of different ways http://www.lispworks.com/documentation/lw70/CLHS/Body/07_ec.htm
17:39:45
Bike
it's been a while, but iirc your method should do call-next-method and then set the metadata slots in the result appropriately
17:39:47
mfiano
I see. So once I do all of this, what is the correct way to query the encoder/decoder functions of one of my leaf class instances?
17:40:36
Bike
You get at the slot definition (e.g. through class-slots) and then use your ENCODER and DECODER accessors
17:41:35
Bike
https://github.com/yitzchak/common-lisp-jupyter/blob/master/src/widgets/traits.lisp#L33-L73 here's some code in cl jupyter that defines a new slot class
17:43:41
Bike
in this case, the inheritance rules are simple: the most recent wins. your encoder and decoder might work the same way
17:44:15
Bike
also, iirc, you might have problems specifying them in the way you do. i don't think the slot definition initargs are evaluated, so the direct slots will just have the list (FUNCTION IDENTITY) instead of an actual function
17:48:22
Bike
i think i ran into this in my initial version of this cl jupyter code (it has since been totally rewritten by yitzi) which had observer functions, and i just had it take symbols instead. so you'd write :observer foo instead of :observer #'foo
17:48:56
Bike
well, basically, imagine you have a class foo that defines (%a :encoder #'x), and then a class bar that defines (%a :encoder #'y), and then say bar inherits from foo
17:50:05
mfiano
Well in my case there won't ever be subclasses of a zfs-property class, but I would expect the deepest subclass to win in almost all cases.
17:50:28
Bike
the GF receives the direct slots in class precedence order, so using SOME like this is kosher
17:54:20
mfiano
So if I wanted it to behave like any other initarg value, how would I accomplish that?
17:56:14
Bike
well, the others have funny behavior as well. like if you have an :initarg, it's not evaluated right off, it's saved and then evaluated each time you instantiate an instance
17:56:29
Bike
I don't think you can change the behavior of the defclass macro to make it be evaluated like you want, unfortunately
17:56:34
Arthur
I ended up hacking together some c2mop:compute-effective-slot-definition stuff when encountering the #'fn non-evaluated slot inits over in https://github.com/arthev/spiclum/blob/master/src/prevalence-class.lisp, maybe there are better ways
17:57:28
Bike
you can ofc define your own defclass macro, but that can be sort of a pain in its own way
17:57:59
mfiano
Can I use the mop to get a single class slot, or do I have to use #'find or whatnot?
17:58:31
Bike
if you mean you want to get a particular slot definition from a class, you do need to use find or something yeah
17:59:15
mfiano
instead of #'c2mop:class-slots <class> -> list, something like #'foo:class-slot <class> <slot-name> -> #<esd>
18:04:17
mfiano
THe MOP always impresses me at its flexibility at the cost of boilerplate. It's to CLOS as macros are to code. Not much you can't do with CL with those two powers combined :)
18:56:41
trev
does anyone know a plugin for emacs where you can basically hover over keywords or functions and see docs for them?
19:06:12
mfiano
Anyone know if alexandria or some utility library has a shorthand for (setf (slot-value instance slot-name) (call-some-func (slot-value instance slot-name))) ?
19:09:52
mfiano
They can, but in this case it won't work anyway as slot-name is not known at compile time.
19:10:51
mfiano
THey can also be a source of serious performance issues if you are evaluating an expensive accessor effective method each read of the symbol.
19:26:57
Bike
now that i'm thinking about it, maybe it would be nice if alexandria or something had a macro like atomic-update but without the atomicity
19:36:51
_death
that's kinda like PG's _f macro in On Lisp.. I don't think it's so nice.. instead I prefer to name the operation, like INCF
19:48:01
Alfr
Bike, not in the library, but here: (defun %update (thing fun &rest args) (apply fun thing args)) (define-modify-macro update (fun &rest args) %update)
20:05:46
mfiano
Not that I think I'd need to do type-checking considering the stringified source data is coming from the correct application I am parsing into lisp objects, but it makes me wonder if I should anyway.
20:07:50
mfiano
Like one slot is fed any of "none"i, "available", "unavailable", and encoded as any of :none, :available, :unavailable. But if my program logic is wrong and tries creating an instance with bad data, I have no way to type check.
20:10:11
mfiano
Though not sure if it makes sense to type check decoding if it is symmetric with the input.
20:14:08
mfiano
I'm also not sure if it even makes sense to specify an :encoder _and_ :decoder slot. The decoder just puts it into the original form it was encoded from. That logic can be derived with a closure or object representing the transcoding method.
20:19:25
mfiano
My plan was to build a CST, producing an AST afterwards to do type-checking/generation phases on. I think I need to sketch out the pipeline better...it might be overly complex like all my compiler theory implementations are :/
21:43:03
Guest74
ugh, I hate when you can't interrupt a slime repl. This is why I shouldn't have my editor in the same process, let alone my window manager.
1:45:25
Josh_2
Does anyone have an example of using multiple :around methods combined with call-next-method?
1:45:53
Josh_2
I only just realized that call-next-method when inside of an :around looks for the next most applicable :around method before executing the primary (by default) :facepalm:
2:21:23
zacque
Josh_2: You can play around with it using hierarchical classes like animal, dog, poodle, cat, persian-cat
2:22:23
zacque
Then define a generic function like MAKE-SOUND or PRINT-NAME for specialising it on different classes
2:24:18
zacque
The trick in understanding the `Standard Method Combination` is to understand the interplay of "shadowing" and "accumulative" effects for both primary and auxiliary methods
2:25:29
Josh_2
I though (call-next-method) invokes the primary method when in :around, but it doesn't, it looks for the next most applicable :around and if it doesn't find one then it calls the most applicable primary
2:27:41
zacque
Also, I suppose you can always define a "non-crazy" order with DEFINE-METHOD-COMBINATION
2:28:34
Bike
but usually if i don't understand a method combination thing i just figure out what the form looks like based on the method combination definition
2:30:11
Bike
like if your applicable methods are two arounds, two primaries, a before, and an after, what you get is (call-method #<around1> #<around2> (make-method (progn (call-method #<before>) (multiple-value-prog1 (call-method #<primary1> #<primary2>) (call-method #<after>)))))
2:31:00
Bike
in other words, if you use call-next-method and the next method is an around, you call that, but otherwise you call the whole before/primary/after situation
2:35:06
Bike
if you get this system https://github.com/sellout/method-combination-utilities you can use method-combination-expand to get that tree i wrote out manually