* Keywords in GOOPS methods
@ 2024-11-19 16:41 Mikael Djurfeldt
2024-11-21 20:33 ` Mikael Djurfeldt
0 siblings, 1 reply; 10+ messages in thread
From: Mikael Djurfeldt @ 2024-11-19 16:41 UTC (permalink / raw)
To: guile-devel, Ludovic Courtès, guile-user; +Cc: Mikael Djurfeldt
[-- Attachment #1.1: Type: text/plain, Size: 407 bytes --]
Hi all,
I've implemented support for keyword arguments (corresponding to define*
and lambda*) in GOOPS. The functionality is similar to that of CLOS (which
also has keyword in methods) in that dispatch is not done on the keyword
part.
You can find the changes in the goops-keyword branch at
https://github.com/mdjurfeldt/guile/tree/goops-keyword or in the included
patch.
Comments?
Best regards,
MIkael
[-- Attachment #1.2: Type: text/html, Size: 642 bytes --]
[-- Attachment #2: goops-kw-patch.patch --]
[-- Type: text/x-patch, Size: 11808 bytes --]
diff --git a/module/oop/goops.scm b/module/oop/goops.scm
index 8ed68694c..c0490c84a 100644
--- a/module/oop/goops.scm
+++ b/module/oop/goops.scm
@@ -135,7 +135,7 @@
class-slots
generic-function-name
generic-function-methods method-generic-function
- method-specializers method-formals
+ method-specializers method-formals method-keyword-formals?
primitive-generic-generic enable-primitive-generic!
method-procedure accessor-method-slot-definition
make find-method get-keyword))
@@ -1052,6 +1052,7 @@ slots as we go."
specializers
procedure
formals
+ keyword-formals?
body
make-procedure)
(define-standard-class <accessor-method> (<method>)
@@ -1156,6 +1157,7 @@ function."
(#:specializers specializers ())
(#:procedure procedure #f)
(#:formals formals ())
+ (#:keyword-formals? keyword-formals? #f)
(#:body body ())
(#:make-procedure make-procedure #f))))
((memq <class> (class-precedence-list class))
@@ -2018,14 +2020,14 @@ function."
(else
(let lp ((specs specs) (types types))
(cond
- ((null? specs) (null? types))
+ ((null? specs)
+ (or (null? types) (method-keyword-formals? m)))
((not (pair? specs)) #t)
((null? types) #f)
(else
(and (memq (car specs) (class-precedence-list (car types)))
(lp (cdr specs) (cdr types))))))))))
- (let ((n (length args))
- (types (map class-of args)))
+ (let ((types (map class-of args)))
(let lp ((methods (generic-function-methods gf))
(applicable '()))
(if (null? methods)
@@ -2066,8 +2068,27 @@ function."
(define-syntax method
(lambda (x)
- (define (parse-args args)
- (let lp ((ls args) (formals '()) (specializers '()))
+ ;; parse-formals METHOD-FORMALS
+ ;;
+ ;; return (FORMALS SPECIALIZERS KEYWORD-FORMALS)
+ ;;
+ ;; FORMALS is the possibly improper list of specializable formals.
+ ;;
+ ;; SPECIALIZERS is a proper list of the corresponding specializers.
+ ;; Its last element corresponds to the cdr of the last element in
+ ;; METHOD-FORMALS such that the possibly improper list corresponding
+ ;; to FORMALS can be obtained by applying cons* to SPECIALIZERS.
+ ;; The reason for handling it like this is that the specializers are
+ ;; each evaluated to their values and therefore *must* be provided
+ ;; by a cons* in the (make <method> ...) expression.
+ ;;
+ ;; KEYWORD_FORMALS is the part of METHOD-FORMALS which starts with a
+ ;; keyword and corresponds to the keyword-syntax of lambda*. These
+ ;; are not specializable (which also corresponds to CLOS
+ ;; functionality).
+ ;;
+ (define (parse-formals method-formals)
+ (let lp ((ls method-formals) (formals '()) (specializers '()))
(syntax-case ls ()
(((f s) . rest)
(and (identifier? #'f) (identifier? #'s))
@@ -2079,13 +2100,21 @@ function."
(lp #'rest
(cons #'f formals)
(cons #'<top> specializers)))
+ ((f . rest)
+ (keyword? (syntax->datum #'f))
+ (list (reverse formals)
+ (reverse (cons #''() specializers)) ;to be cons*:ed
+ (cons #'f #'rest)))
+
(()
(list (reverse formals)
- (reverse (cons #''() specializers))))
+ (reverse (cons #''() specializers))
+ '())) ;yes, not #''(); used in tests
(tail
(identifier? #'tail)
(list (append (reverse formals) #'tail)
- (reverse (cons #'<top> specializers)))))))
+ (reverse (cons #'<top> specializers))
+ '())))))
(define (find-free-id exp referent)
(syntax-case exp ()
@@ -2098,43 +2127,87 @@ function."
(and (free-identifier=? #'x id) id)))
(_ #f)))
- (define (compute-procedure formals body)
+ (define (compute-procedure formals keyword-formals body)
(syntax-case body ()
((body0 ...)
- (with-syntax ((formals formals))
- #'(lambda formals body0 ...)))))
-
- (define (->proper args)
- (let lp ((ls args) (out '()))
+ (let ((formals (if (null? keyword-formals)
+ formals
+ (append formals keyword-formals))))
+ (with-syntax ((formals formals))
+ #`(lambda* formals body0 ...))))))
+
+ ;; ->formal-ids FORMALS
+ ;;
+ ;; convert FORMALS into formal-ids format, which is a cell where the
+ ;; car is the list of car:s in FORMALS and the cdr is the cdr of the
+ ;; last cell in FORMALS.
+ ;;
+ ;; The motivation for this format is to determine at low cost if
+ ;; FORMALS is improper or not and to easily be able to generate the
+ ;; corresponding next-method call.
+ ;;
+ (define (->formal-ids formals)
+ (let lp ((ls formals) (out '()))
+ (syntax-case ls ()
+ ((x . xs) (lp #'xs (cons #'x out)))
+ (() (cons (reverse out) '()))
+ (tail (cons (reverse out) #'tail)))))
+
+ ;; keyword-formal-ids KEYWORD-FORMALS
+ ;;
+ ;; return a form corresponding to KEYWORD-FORMALS but with
+ ;; identifiers only (keywords removed) The value returned has the
+ ;; formals-ids format as described above.
+ ;;
+ ;; The output is used in the next-method application form.
+ ;;
+ (define (->keyword-formal-ids keyword-formals)
+ (let lp ((ls keyword-formals) (out '()))
(syntax-case ls ()
- ((x . xs) (lp #'xs (cons #'x out)))
- (() (reverse out))
- (tail (reverse (cons #'tail out))))))
+ (((f val) . rest)
+ (lp #'rest out))
+ ((#:rest f)
+ (cons (reverse out) #'f))
+ ((f . rest)
+ (keyword? (syntax->datum #'f))
+ (lp #'rest out))
+ ((f . rest)
+ (lp #'rest (cons #'f out)))
+ (()
+ (cons (reverse out) '()))
+ (tail
+ (cons (reverse out) #'tail)))))
- (define (compute-make-procedure formals body next-method)
+ (define (compute-make-procedure formals keyword-formals body next-method)
(syntax-case body ()
((body ...)
- (with-syntax ((next-method next-method))
- (syntax-case formals ()
- ((formal ...)
- #'(lambda (real-next-method)
- (lambda (formal ...)
- (let ((next-method (lambda args
- (if (null? args)
- (real-next-method formal ...)
- (apply real-next-method args)))))
- body ...))))
- (formals
- (with-syntax (((formal ...) (->proper #'formals)))
- #'(lambda (real-next-method)
- (lambda formals
- (let ((next-method (lambda args
- (if (null? args)
- (apply real-next-method formal ...)
- (apply real-next-method args)))))
+ (let ((formals (if (null? keyword-formals)
+ formals ;might be improper
+ (append formals keyword-formals)))
+ (formal-ids
+ (if (null? keyword-formals)
+ (->formal-ids formals)
+ (let ((kw-formal-ids (->keyword-formal-ids keyword-formals)))
+ ;; input and result in formals-ids format
+ (cons (append formals (car kw-formal-ids))
+ (cdr kw-formal-ids))))))
+ (with-syntax ((next-method next-method))
+ (syntax-case formals ()
+ (formals
+ #`(lambda (real-next-method)
+ (lambda* formals
+ (let ((next-method
+ (lambda args
+ (if (null? args)
+ #,(if (null? (cdr formal-ids))
+ #`(real-next-method #,@(car formal-ids))
+ #`(apply real-next-method
+ #,@(car formal-ids)
+ #,(cdr formal-ids)))
+ (apply real-next-method args)))))
body ...))))))))))
- (define (compute-procedures formals body)
+ (define (compute-procedures formals keyword-formals body)
;; So, our use of this is broken, because it operates on the
;; pre-expansion source code. It's equivalent to just searching
;; for referent in the datums. Ah well.
@@ -2142,23 +2215,31 @@ function."
(if id
;; return a make-procedure
(values #'#f
- (compute-make-procedure formals body id))
- (values (compute-procedure formals body)
+ (compute-make-procedure formals keyword-formals body id))
+ (values (compute-procedure formals keyword-formals body)
#'#f))))
(syntax-case x ()
- ((_ args) #'(method args (if #f #f)))
- ((_ args body0 body1 ...)
- (with-syntax (((formals (specializer ...)) (parse-args #'args)))
+ ((_ formals) #'(method formals (if #f #f)))
+ ((_ formals body0 body1 ...)
+ (with-syntax (((formals (specializer ...) keyword-formals)
+ (parse-formals #'formals)))
(call-with-values
(lambda ()
- (compute-procedures #'formals #'(body0 body1 ...)))
+ (compute-procedures #'formals
+ #'keyword-formals
+ #'(body0 body1 ...)))
(lambda (procedure make-procedure)
(with-syntax ((procedure procedure)
(make-procedure make-procedure))
- #'(make <method>
- #:specializers (cons* specializer ...)
- #:formals 'formals
+ #`(make <method>
+ #:specializers (cons* specializer ...) ;yes, this
+ ;; The cons* is needed to get at the value of each
+ ;; specializer.
+ #:formals (if (null? 'keyword-formals)
+ 'formals ;might be improper
+ (append 'formals 'keyword-formals))
+ #:keyword-formals? (not (null? 'keyword-formals))
#:body '(body0 body1 ...)
#:make-procedure make-procedure
#:procedure procedure)))))))))
@@ -2281,6 +2362,9 @@ function."
(define-method (method-formals (m <method>))
(slot-ref m 'formals))
+(define-method (method-keyword-formals? (m <method>))
+ (slot-ref m 'keyword-formals?))
+
;;;
;;; Slots
;;;
@@ -2834,6 +2918,7 @@ var{initargs}."
(slot-set! method 'procedure
(get-keyword #:procedure initargs #f))
(slot-set! method 'formals (get-keyword #:formals initargs '()))
+ (slot-set! method 'keyword-formals? (get-keyword #:keyword-formals? initargs #f))
(slot-set! method 'body (get-keyword #:body initargs '()))
(slot-set! method 'make-procedure (get-keyword #:make-procedure initargs #f)))
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-19 16:41 Keywords in GOOPS methods Mikael Djurfeldt
@ 2024-11-21 20:33 ` Mikael Djurfeldt
2024-11-21 20:33 ` Mikael Djurfeldt
2024-11-21 22:00 ` janneke
0 siblings, 2 replies; 10+ messages in thread
From: Mikael Djurfeldt @ 2024-11-21 20:33 UTC (permalink / raw)
To: guile-devel, Ludovic Courtès, guile-user, Andy Wingo
Cc: Mikael Djurfeldt
[-- Attachment #1: Type: text/plain, Size: 2096 bytes --]
Since there are no comments, I'm inclined to apply this patch. I will do
that on Sunday if there are no comments before that.
Maybe I should first give a motivating example: guile-mqtt consists of a
thin wrapper over NYACC-generated code. There, I use a coding pattern that
I tend to use in cases where methods need keyword arguments:
(define-method (subscribe (client <mosquitto-client>) (topic <string>) .
args)
(define* (subscribe #:key (qos 0))
(mosquitto_subscribe (mosq client) %null-pointer topic qos))
(apply subscribe args))
With the change I propose, this can be written:
(define-method (subscribe (client <mosquitto-client>) (topic <string>)
#:key (qos 0))
(mosquitto_subscribe (mosq client) %null-pointer topic qos)))
with the same resulting semantics.
There is one question that I'd like people to think about, though: In my
patch I have adhered to the close relationship with CLOS, where defmethod
takes keyword, optional and rest arguments similar to Guile's define*, and
extended the method syntax itself. As an alternative, we could let the
current method syntax stay as is and implement new define-method* and
method* syntax. In some ways this would be cleaner, for example from a
backward compatibility perspective. On the other hand it might feel like
overkill to have so much syntax. Implementation and performance wise it
shouldn't matter much how we choose to do, except that adding
define-method* and method* of course adds more code to the implementation...
Best regards,
Mikael
On Tue, Nov 19, 2024 at 5:41 PM Mikael Djurfeldt <mikael@djurfeldt.com>
wrote:
> Hi all,
>
> I've implemented support for keyword arguments (corresponding to define*
> and lambda*) in GOOPS. The functionality is similar to that of CLOS (which
> also has keyword in methods) in that dispatch is not done on the keyword
> part.
>
> You can find the changes in the goops-keyword branch at
> https://github.com/mdjurfeldt/guile/tree/goops-keyword or in the included
> patch.
>
> Comments?
>
> Best regards,
> MIkael
>
>
[-- Attachment #2: Type: text/html, Size: 2796 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-21 20:33 ` Mikael Djurfeldt
@ 2024-11-21 20:33 ` Mikael Djurfeldt
2024-11-21 22:00 ` janneke
1 sibling, 0 replies; 10+ messages in thread
From: Mikael Djurfeldt @ 2024-11-21 20:33 UTC (permalink / raw)
To: guile-devel, Ludovic Courtès, guile-user, Andy Wingo
[-- Attachment #1: Type: text/plain, Size: 2332 bytes --]
(I will of course add proper documentation in the manual, etc.)
On Thu, Nov 21, 2024 at 9:33 PM Mikael Djurfeldt <mikael@djurfeldt.com>
wrote:
> Since there are no comments, I'm inclined to apply this patch. I will do
> that on Sunday if there are no comments before that.
>
> Maybe I should first give a motivating example: guile-mqtt consists of a
> thin wrapper over NYACC-generated code. There, I use a coding pattern that
> I tend to use in cases where methods need keyword arguments:
>
> (define-method (subscribe (client <mosquitto-client>) (topic <string>) .
> args)
> (define* (subscribe #:key (qos 0))
> (mosquitto_subscribe (mosq client) %null-pointer topic qos))
> (apply subscribe args))
>
> With the change I propose, this can be written:
>
> (define-method (subscribe (client <mosquitto-client>) (topic <string>)
> #:key (qos 0))
> (mosquitto_subscribe (mosq client) %null-pointer topic qos)))
>
> with the same resulting semantics.
>
> There is one question that I'd like people to think about, though: In my
> patch I have adhered to the close relationship with CLOS, where defmethod
> takes keyword, optional and rest arguments similar to Guile's define*, and
> extended the method syntax itself. As an alternative, we could let the
> current method syntax stay as is and implement new define-method* and
> method* syntax. In some ways this would be cleaner, for example from a
> backward compatibility perspective. On the other hand it might feel like
> overkill to have so much syntax. Implementation and performance wise it
> shouldn't matter much how we choose to do, except that adding
> define-method* and method* of course adds more code to the implementation...
>
> Best regards,
> Mikael
>
> On Tue, Nov 19, 2024 at 5:41 PM Mikael Djurfeldt <mikael@djurfeldt.com>
> wrote:
>
>> Hi all,
>>
>> I've implemented support for keyword arguments (corresponding to define*
>> and lambda*) in GOOPS. The functionality is similar to that of CLOS (which
>> also has keyword in methods) in that dispatch is not done on the keyword
>> part.
>>
>> You can find the changes in the goops-keyword branch at
>> https://github.com/mdjurfeldt/guile/tree/goops-keyword or in the
>> included patch.
>>
>> Comments?
>>
>> Best regards,
>> MIkael
>>
>>
[-- Attachment #2: Type: text/html, Size: 3243 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-21 20:33 ` Mikael Djurfeldt
2024-11-21 20:33 ` Mikael Djurfeldt
@ 2024-11-21 22:00 ` janneke
2024-11-21 22:51 ` Mikael Djurfeldt
1 sibling, 1 reply; 10+ messages in thread
From: janneke @ 2024-11-21 22:00 UTC (permalink / raw)
To: Mikael Djurfeldt
Cc: guile-devel, Ludovic Courtès, guile-user, Andy Wingo
[-- Attachment #1: Type: text/plain, Size: 334 bytes --]
Mikael Djurfeldt writes:
Hi Mikael,
> Since there are no comments, I'm inclined to apply this patch. I will do
> that on Sunday if there are no comments before that.
Just FYI, have you seen the attached patch by Mark Weaver? I've been
carrying that for some time now, functionality like this is very
welcome.
Greetings,
Janneke
[-- Attachment #2: 0001-Add-define-method-for-GOOPS.patch --]
[-- Type: text/x-patch, Size: 2787 bytes --]
From f4d73b5590cd2e6ff30341aa824a3f1005b50220 Mon Sep 17 00:00:00 2001
From: Mark H Weaver <mhw@netris.org>
Date: Fri, 15 Apr 2022 11:11:16 +0200
Subject: [PATCH] Add define-method* for GOOPS.
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset=UTF-8
* dzn/define-method-star.scm: New file.
* dzn/local.mk (dist_%C%_scm_DATA): Add it.
---
dzn/define-method-star.scm | 40 ++++++++++++++++++++++++++++++++++++++
dzn/local.mk | 1 +
2 files changed, 41 insertions(+)
create mode 100644 dzn/define-method-star.scm
diff --git a/dzn/define-method-star.scm b/dzn/define-method-star.scm
new file mode 100644
index 0000000000..a958907e35
--- /dev/null
+++ b/dzn/define-method-star.scm
@@ -0,0 +1,40 @@
+;;; Dezyne --- Dezyne command line tools
+;;;
+;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
+;;;
+;;; This file is part of Dezyne.
+;;;
+;;; Dezyne is free software: you can redistribute it and/or modify it
+;;; under the terms of the GNU Affero General Public License as
+;;; published by the Free Software Foundation, either version 3 of the
+;;; License, or (at your option) any later version.
+;;;
+;;; Dezyne is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; Affero General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Affero General Public
+;;; License along with Dezyne. If not, see <http://www.gnu.org/licenses/>.
+;;;
+;;; Commentary:
+;;;
+;;; Code:
+
+(define-module (8sync define-method-star)
+ #:use-module (oop goops)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-11)
+ #:export (define-method*))
+
+(define-syntax define-method*
+ (lambda (x)
+ (syntax-case x ()
+ ((_ (generic arg-spec ... . tail) body ...)
+ (let-values (((required-arg-specs other-arg-specs)
+ (break (compose keyword? syntax->datum)
+ #'(arg-spec ...))))
+ #`(define-method (generic #,@required-arg-specs . rest)
+ (apply (lambda* (#,@other-arg-specs . tail)
+ body ...)
+ rest)))))))
diff --git a/dzn/local.mk b/dzn/local.mk
index a6cef16408..99d84ae105 100644
--- a/dzn/local.mk
+++ b/dzn/local.mk
@@ -29,6 +29,7 @@ dist_%C%_scm_DATA = \
%D%/code.scm \
%D%/code-util.scm \
%D%/command-line.scm \
+ %D%/define-method-star.scm \
%D%/display.scm \
%D%/explore.scm \
%D%/goops.scm \
--
Jan Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond http://lilypond.org
Freelance IT http://JoyofSource.com | Avatar® http://AvatarAcademy.com
[-- Attachment #3: Type: text/plain, Size: 164 bytes --]
--
Janneke Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond https://LilyPond.org
Freelance IT https://www.JoyOfSource.com | Avatar® https://AvatarAcademy.com
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-21 22:00 ` janneke
@ 2024-11-21 22:51 ` Mikael Djurfeldt
2024-11-22 11:46 ` Tomas Volf
2024-11-22 12:20 ` janneke
0 siblings, 2 replies; 10+ messages in thread
From: Mikael Djurfeldt @ 2024-11-21 22:51 UTC (permalink / raw)
To: janneke
Cc: guile-devel, Ludovic Courtès, guile-user, Andy Wingo,
Mikael Djurfeldt
[-- Attachment #1: Type: text/plain, Size: 1029 bytes --]
Hi Janneke,
That was elegant. :-)
Nope---haven't seen it. (Or, at least I do not remember it.)
Maybe I should have a look at what the optimizer can do about Mark's code.
(As you might have seen, my code is a modification of the method syntax
implementation itself.)
Any opinions on what is best: Having a define-method* or having the
functionality in define-method itself?
Best regards,
Mikael
On Thu, Nov 21, 2024 at 11:00 PM <janneke@gnu.org> wrote:
> Mikael Djurfeldt writes:
>
> Hi Mikael,
>
> > Since there are no comments, I'm inclined to apply this patch. I will do
> > that on Sunday if there are no comments before that.
>
> Just FYI, have you seen the attached patch by Mark Weaver? I've been
> carrying that for some time now, functionality like this is very
> welcome.
>
> Greetings,
> Janneke
>
>
> --
> Janneke Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond
> https://LilyPond.org
> Freelance IT https://www.JoyOfSource.com | Avatar®
> https://AvatarAcademy.com
>
[-- Attachment #2: Type: text/html, Size: 1909 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-21 22:51 ` Mikael Djurfeldt
@ 2024-11-22 11:46 ` Tomas Volf
2024-11-22 12:28 ` Mikael Djurfeldt
2024-11-22 12:20 ` janneke
1 sibling, 1 reply; 10+ messages in thread
From: Tomas Volf @ 2024-11-22 11:46 UTC (permalink / raw)
To: Mikael Djurfeldt
Cc: janneke, guile-devel, Ludovic Courtès, guile-user,
Andy Wingo
[-- Attachment #1: Type: text/plain, Size: 1162 bytes --]
Hello,
opinion from the peanut gallery below.
Mikael Djurfeldt <mikael@djurfeldt.com> writes:
> Any opinions on what is best: Having a define-method* or having the
> functionality in define-method itself?
I do find the symmetry between define-method/define-method* and
define/define* pleasing.
For define and define*, one could argue that procedures produced by the
latter are slower to call (I did measure). Is that an issue here as
well? (I guess one could argue that people writing object oriented code
with run-time dispatch are usually not driven by performance as the main
metric.)
You did mention backwards compatibility, but how serious you expect the
issue would be? I personally did not use GOOPS yet, but I have a hard
time imagining a real-world code that would be broken by this change.
Do you expect there would actually be any?
I personally would probably lean towards two separate procedures (mainly
due to the assumption of there being a performance impact).
Have a nice day,
Tomas Volf
--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 853 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-22 11:46 ` Tomas Volf
@ 2024-11-22 12:28 ` Mikael Djurfeldt
0 siblings, 0 replies; 10+ messages in thread
From: Mikael Djurfeldt @ 2024-11-22 12:28 UTC (permalink / raw)
To: Mikael Djurfeldt, janneke, guile-devel, Ludovic Courtès,
guile-user, Andy Wingo
[-- Attachment #1: Type: text/plain, Size: 3078 bytes --]
Hi Tomas,
Thank you for your feedback. Answers below.
On Fri, Nov 22, 2024 at 12:46 PM Tomas Volf <~@wolfsden.cz> wrote:
> I do find the symmetry between define-method/define-method* and
> define/define* pleasing.
>
Yes, I guess we are free to form GOOPS in our own style regardless of CLOS.
> For define and define*, one could argue that procedures produced by the
> latter are slower to call (I did measure). Is that an issue here as
> well?
Well, the implementation in my patch essentially does the same amount of
work as the old one, and when not using keywords a call is as efficient.
Generic function dispatch should be unaffected, as with Marks macro.
However, there will be the same kind of overhead as for define* in calls
actually using keywords.
I checked what the optimizer can do with the resulting method when using
Marks elegant macro. Unfortunately, such a method would have overhead.
However, I do think that a modified version of Marks macro together with a
small change in peval could have a chance not to introduce such overhead.
All, in all, it currently (and paradoxically) turns out to be simpler to
provide the keyword functionality in define-method rather than also
providing define-method* if one doesn't want to introduce overhead for the
most common types of methods.
Perhaps I could try to learn what is going on in peval and see if a macro
similar to Marks would be the most elegant way to go forward.
You did mention backwards compatibility, but how serious you expect the
> issue would be? I personally did not use GOOPS yet, but I have a hard
> time imagining a real-world code that would be broken by this change.
> Do you expect there would actually be any?
>
I said "backwards compatibility perspective" and was too lazy to spell out
what I meant, which is this: Assume that we *now* want to write backwards
compatible code. Then, if we have *not* modified define-method we can be
certain that as long as we use define-method, we will be backwards
compatible. However, if we introduce keywords in define-method (rather than
define-method*) this is no longer true. If this new functionality is
confined to define-method*, then a future code can test for the presence of
defined-method* in Guile and otherwise provide something similar to Marks
(or Anders Vinjars from April 2003 :-)) macro.
>
> I personally would probably lean towards two separate procedures (mainly
> due to the assumption of there being a performance impact).
>
Well, there is none now that we have Andys remarkable compiler. Now also
Janneke has responded and he prefers to have everything in define-method.
Myself, I'm split. I can see merits in both solutions. Least code bloat
would probably be to just apply my patch. But I think we should think
mostly about style (where I am still split).
Best regards,
Mikael
>
> Have a nice day,
> Tomas Volf
>
> --
> There are only two hard things in Computer Science:
> cache invalidation, naming things and off-by-one errors.
>
[-- Attachment #2: Type: text/html, Size: 4190 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-21 22:51 ` Mikael Djurfeldt
2024-11-22 11:46 ` Tomas Volf
@ 2024-11-22 12:20 ` janneke
2024-11-22 12:29 ` Mikael Djurfeldt
1 sibling, 1 reply; 10+ messages in thread
From: janneke @ 2024-11-22 12:20 UTC (permalink / raw)
To: Mikael Djurfeldt
Cc: guile-devel, Ludovic Courtès, guile-user, Andy Wingo
Mikael Djurfeldt writes:
Hello,
> That was elegant. :-)
:)
> Nope---haven't seen it. (Or, at least I do not remember it.)
>
> Maybe I should have a look at what the optimizer can do about Mark's
> code. (As you might have seen, my code is a modification of the method
> syntax implementation itself.)
Yes, I didn't quite get if this brings something else too, except for
the obvious
> Any opinions on what is best: Having a define-method* or having the
> functionality in define-method itself?
...which I think in your version, not having to use define-method*
feels more elegant/GOOPSy to me, as it's all overloads/generics,
but I have no strong opionion on this. Thanks for working on this!
Greetings,
Janneke
--
Janneke Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond https://LilyPond.org
Freelance IT https://www.JoyOfSource.com | Avatar® https://AvatarAcademy.com
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-22 12:20 ` janneke
@ 2024-11-22 12:29 ` Mikael Djurfeldt
2024-11-22 23:04 ` Mikael Djurfeldt
0 siblings, 1 reply; 10+ messages in thread
From: Mikael Djurfeldt @ 2024-11-22 12:29 UTC (permalink / raw)
To: janneke; +Cc: guile-devel, Ludovic Courtès, guile-user, Andy Wingo
Thanks!
See responses to Tomas.
On Fri, Nov 22, 2024 at 1:20 PM <janneke@gnu.org> wrote:
> Mikael Djurfeldt writes:
>
> Hello,
>
> > That was elegant. :-)
>
> :)
>
> > Nope---haven't seen it. (Or, at least I do not remember it.)
> >
> > Maybe I should have a look at what the optimizer can do about Mark's
> > code. (As you might have seen, my code is a modification of the method
> > syntax implementation itself.)
>
> Yes, I didn't quite get if this brings something else too, except for
> the obvious
>
> > Any opinions on what is best: Having a define-method* or having the
> > functionality in define-method itself?
>
> ...which I think in your version, not having to use define-method*
> feels more elegant/GOOPSy to me, as it's all overloads/generics,
> but I have no strong opionion on this. Thanks for working on this!
>
> Greetings,
> Janneke
>
> --
> Janneke Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond
> https://LilyPond.org
> Freelance IT https://www.JoyOfSource.com | Avatar®
> https://AvatarAcademy.com
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Keywords in GOOPS methods
2024-11-22 12:29 ` Mikael Djurfeldt
@ 2024-11-22 23:04 ` Mikael Djurfeldt
0 siblings, 0 replies; 10+ messages in thread
From: Mikael Djurfeldt @ 2024-11-22 23:04 UTC (permalink / raw)
To: janneke; +Cc: guile-devel, Ludovic Courtès, guile-user, Andy Wingo
[-- Attachment #1: Type: text/plain, Size: 4672 bytes --]
ChatGPT is also split...:
Given the circumstances, I would recommend extending define-method rather
than introducing define-method*.
Here’s why:
1.
*Consistency and Simplicity*: By extending define-method, you avoid
creating a dual system where users have to choose between define-method
and define-method*. This reduces cognitive overhead and makes the API
cleaner.
2.
*Backwards Compatibility*: As long as the extension maintains backwards
compatibility—e.g., methods defined without optional, keyword, or rest
arguments continue to work as before—there’s minimal risk of breaking
existing code.
3.
*Ease of Use*: Users generally prefer fewer forms to remember. Adding
more features to the existing define-method aligns with the principle of
least surprise for anyone using GOOPS.
4.
*Future-Proofing*: Creating a parallel form like define-method* could
set a precedent for duplicating functionality every time a significant
feature is added, leading to API bloat over time.
The counterargument might be that adding optional, keyword, and rest
arguments complicates define-method’s semantics. However, if these features
integrate naturally and don’t break current assumptions, the benefits of a
unified approach outweigh the risks.
My confidence in this recommendation is 85%, as the best decision
ultimately depends on the specifics of your user base and how GOOPS is
typically used. If there's significant risk of confusion or breaking
changes in the extension, introducing define-method* might then become a
better option.
4o
You said:
But Guile already has define* and lambda* forms...
ChatGPT said:
The existence of define* and lambda* in Guile suggests a precedent for
introducing parallel forms to extend functionality. This could justify
introducing define-method* for consistency with Guile's conventions. Here's
a revised analysis based on this:
1.
*Consistency with Guile*: Introducing define-method* aligns GOOPS with
Guile's existing patterns, making it feel more integrated into the Guile
ecosystem. Users familiar with Guile would likely expect an extended form
with a * suffix for enhanced functionality.
2.
*Avoiding Overloading Semantics*: Extending define-method could make its
behavior more complex and potentially harder to understand for users who
only need the basic functionality. Having a separate define-method*
clearly distinguishes the advanced features, keeping both forms
conceptually simpler.
3.
*Backward Compatibility and Clarity*: A new form eliminates any chance
of subtle incompatibilities or ambiguities arising from extending
define-method. For example, users writing macros or tooling around
define-method won't need to account for new syntax or semantics.
4.
*Gradual Adoption*: With define-method*, users can adopt the new
functionality incrementally. This reduces friction for existing users who
may not need or want the added complexity of optional, keyword, and rest
arguments.
Given Guile's * convention, introducing define-method* appears to be the
better option. It respects established practices, avoids over-complicating
existing forms, and provides a clear upgrade path. My confidence in this
recommendation is 90%, given that the Guile precedent is a strong argument
for following suit.
4o
On Fri, Nov 22, 2024 at 1:29 PM Mikael Djurfeldt <mikael@djurfeldt.com>
wrote:
> Thanks!
>
> See responses to Tomas.
>
> On Fri, Nov 22, 2024 at 1:20 PM <janneke@gnu.org> wrote:
>
>> Mikael Djurfeldt writes:
>>
>> Hello,
>>
>> > That was elegant. :-)
>>
>> :)
>>
>> > Nope---haven't seen it. (Or, at least I do not remember it.)
>> >
>> > Maybe I should have a look at what the optimizer can do about Mark's
>> > code. (As you might have seen, my code is a modification of the method
>> > syntax implementation itself.)
>>
>> Yes, I didn't quite get if this brings something else too, except for
>> the obvious
>>
>> > Any opinions on what is best: Having a define-method* or having the
>> > functionality in define-method itself?
>>
>> ...which I think in your version, not having to use define-method*
>> feels more elegant/GOOPSy to me, as it's all overloads/generics,
>> but I have no strong opionion on this. Thanks for working on this!
>>
>> Greetings,
>> Janneke
>>
>> --
>> Janneke Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond
>> https://LilyPond.org
>> Freelance IT https://www.JoyOfSource.com | Avatar®
>> https://AvatarAcademy.com
>>
>
[-- Attachment #2: Type: text/html, Size: 14037 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2024-11-22 23:04 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-19 16:41 Keywords in GOOPS methods Mikael Djurfeldt
2024-11-21 20:33 ` Mikael Djurfeldt
2024-11-21 20:33 ` Mikael Djurfeldt
2024-11-21 22:00 ` janneke
2024-11-21 22:51 ` Mikael Djurfeldt
2024-11-22 11:46 ` Tomas Volf
2024-11-22 12:28 ` Mikael Djurfeldt
2024-11-22 12:20 ` janneke
2024-11-22 12:29 ` Mikael Djurfeldt
2024-11-22 23:04 ` Mikael Djurfeldt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).