unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Problem merging generics across modules
@ 2017-10-13  0:01 Andrew Erlanger
  2017-10-13  0:03 ` Andrew Erlanger
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Andrew Erlanger @ 2017-10-13  0:01 UTC (permalink / raw)
  To: guile-user

Hello, Schemers,

I am trying to extend the definition of a few primitives,
including equal?, in a project of mine. Let's say that a.scm contains:

(define-module (project a)
  #:use-module (oop goops)  
  #:export (equal?)

...

(define-method (equal? (c1 <color>) (c2 <color>))
   (same-rgb? c1 c2))

Now I go to the REPL, and this is what I see:

scheme@(guile-user)> equal?
WARNING: (guile-user): `equal?' imported from both (ice-9 r5rs) and (capital base)
$2 = #<<generic> equal? (2)>
scheme@(guile-user)> (equal? 2 3)
ERROR: In procedure scm-error:
ERROR: No applicable method for #<<generic> equal? (2)> in call (equal? 2 3)

Now I'm aware of "#:duplicates (merge-generics)", but I don't understand
how to apply it in this case. I've tried replacing the module definition with:

(define-module (project a)
  #:use-module (oop goops)  
  #:use-module (ice-9 r5rs)
  #:duplicates (merge-generics)
  #:export (equal?)

but this doesn't seem to do anything.

Any advice? Thank you.

- Andrew



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Problem merging generics across modules
  2017-10-13  0:01 Problem merging generics across modules Andrew Erlanger
@ 2017-10-13  0:03 ` Andrew Erlanger
  2017-10-13 14:12 ` David Pirotte
  2017-10-13 14:35 ` David Pirotte
  2 siblings, 0 replies; 6+ messages in thread
From: Andrew Erlanger @ 2017-10-13  0:03 UTC (permalink / raw)
  To: guile-user

Andrew Erlanger <andrew.erlanger@gmail.com> writes:

> Now I go to the REPL, and this is what I see:
>
> scheme@(guile-user)> equal?
> WARNING: (guile-user): `equal?' imported from both (ice-9 r5rs) and (capital base)
> $2 = #<<generic> equal? (2)>
> scheme@(guile-user)> (equal? 2 3)
> ERROR: In procedure scm-error:
> ERROR: No applicable method for #<<generic> equal? (2)> in call (equal? 2 3)
>

I should have mentioned that these are called after invoking:

> scheme@(guile-user)> (use-modules (project a))

- Andrew



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Problem merging generics across modules
  2017-10-13  0:01 Problem merging generics across modules Andrew Erlanger
  2017-10-13  0:03 ` Andrew Erlanger
@ 2017-10-13 14:12 ` David Pirotte
  2017-10-13 15:14   ` David Pirotte
  2017-10-13 14:35 ` David Pirotte
  2 siblings, 1 reply; 6+ messages in thread
From: David Pirotte @ 2017-10-13 14:12 UTC (permalink / raw)
  To: Andrew Erlanger; +Cc: guile-user

[-- Attachment #1: Type: text/plain, Size: 2403 bytes --]

Hello Andrew,

> I am trying to extend the definition of a few primitives,
> including equal?, in a project of mine. Let's say that a.scm contains:

> (define-module (project a)
>   #:use-module (oop goops)  
>   #:export (equal?)

When you extend a guile primitive, don't #:export it in your module definition,
because that creates a new binding, hiding guile's core primitive. You should use
#:re-export instead, though in this very specific case of extending a guile core
primitive it is not even necessary, because when guile 'encounter' your
define-method, it does this:

	it creates a generic function for the method name, if it does not already
	exists, by calling define-generic [1]:

		define-generic checks that the name was (or not) previously bound to
		a scheme procedure, in which case it incorporates it into the new
		generic function as its default procedure

	it adds your methods to the generic function...

David

[1]	unless you really know what you are doing, you don't want to call
	define-generic yourself, because if it existed already, then that previous
	generic function would discarded and replaced by a new, empty generic
	function. goops calls define-generic for you, as part ofthe define-method
	protocol, iff there is no generic function for that name, so you are always
	on the safe side to rely on goops here.

;;;
;;; Extending equal?
;;;

(define-module (extending-equal)
  #:use-module (oop goops)

  #:export (<color>
            !r
            !g
            !b))

(define-class <color> ()
  (r #:accessor !r #:init-keyword #:r #:init-value 0)
  (g #:accessor !g #:init-keyword #:g #:init-value 0)
  (b #:accessor !b #:init-keyword #:b #:init-value 0))

(define-method (equal? (c1 <color>) (c2 <color>))
  (pk "in extended equal? ...")
  (and (= (!r c1) (!r c2))
       (= (!g c1) (!g c2))
       (= (!b c1) (!b c2))))

;;;
;;; Let's try it
;;;

GNU Guile 2.2.2.3-0c102

scheme@(guile-user)> (add-to-load-path (getcwd))
scheme@(guile-user)> ,use (oop goops)
scheme@(guile-user)> ,use (extending-equal)
;;; note: source file /usr/alto/projects/guile/goops/extending-equal.scm
;;; ...
scheme@(guile-user)> (make <color>)
$2 = #<<color> 5610ca6b2ba0>
scheme@(guile-user)> (make <color>)
$3 = #<<color> 5610ca7480f0>
scheme@(guile-user)> (equal? $2 $3)

;;; ("in extended equal? ...")
$4 = #t

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Problem merging generics across modules
  2017-10-13  0:01 Problem merging generics across modules Andrew Erlanger
  2017-10-13  0:03 ` Andrew Erlanger
  2017-10-13 14:12 ` David Pirotte
@ 2017-10-13 14:35 ` David Pirotte
       [not found]   ` <874lqxbgnf.fsf@ateguix.i-did-not-set--mail-host-address--so-tickle-me>
  2 siblings, 1 reply; 6+ messages in thread
From: David Pirotte @ 2017-10-13 14:35 UTC (permalink / raw)
  To: Andrew Erlanger; +Cc: guile-user

[-- Attachment #1: Type: text/plain, Size: 1529 bytes --]

Hi again,

> (define-module (project a)
>   #:use-module (oop goops)  
>   #:use-module (ice-9 r5rs)
>   #:duplicates (merge-generics)
>   #:export (equal?)

> but this doesn't seem to do anything.

Because that was not the source of your problem, see my previous email.

Although in theory merge-generics is only needed when you import more then one module
that define the 'same' generic function, I personally recommend you to add the option
in any modules that use goops, unless you really know what you are doing:

	this is because as time goes, you may rearrange your modules, import
	others ... and forget that more then one defined a generic function, and ...
	bang! (and these bugs are among the most diffcult to track down and debug

Note that you probably want more then just 'merge-generics, and prefer to extend the
default set:

	scheme@(guile-user)> (default-duplicate-binding-handler)
	$10 = (replace warn-override-core warn last)

So this gives you:

  #:duplicates (merge-generics
		replace
		warn-override-core
		warn
		last)

Also note that you have to set this in the repl as well, if you import more then one
module defining the 'same' generics ...:

	(default-duplicate-binding-handler
	   '(merge-generics replace warn-override-core warn last))
	$11 = (#<<generic> merge-generics (3)> #<procedure replace (module name int1 val…> …)
	scheme@(guile-user)> (default-duplicate-binding-handler)
	$12 = (merge-generics replace warn-override-core warn last)

David

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Problem merging generics across modules
  2017-10-13 14:12 ` David Pirotte
@ 2017-10-13 15:14   ` David Pirotte
  0 siblings, 0 replies; 6+ messages in thread
From: David Pirotte @ 2017-10-13 15:14 UTC (permalink / raw)
  To: Andrew Erlanger; +Cc: guile-user

[-- Attachment #1: Type: text/plain, Size: 1468 bytes --]

Hi again,

> ... You should use #:re-export instead

Note that #:re-export fails if the name is not already defined ... which is ok for
small and individual projects, but less then optimal and subject to introduce bugs
for long term projects, involving multiple developers, with hundreds of modules, and
where module reorganization is (sometimes) part of the game ...  So I defined a macro
which checks if a name exists (as in imported) or not, and appropriately calls
export or re-export, that you should onlu use for getters, setters, accessors
and methods):

	https://git.savannah.gnu.org/cgit/guile-cv.git/tree/cv/support/g-export.scm

Just as an example, your module definition would be (see below)... Then if this
module later imports another module, that defines !r, !g and/or !b, you still would
be fine...

Cheers,
David

;;;
;;; Extending equal?
;;;

(define-module (extending-equal)
  #:use-module (oop goops)
  ;; just changed the module 'path' to what ever ...
  #:use-module (g-export)

  #:export (<color>)

(g-export !r
            !g
            !b)

(define-class <color> ()
  (r #:accessor !r #:init-keyword #:r #:init-value 0)
  (g #:accessor !g #:init-keyword #:g #:init-value 0)
  (b #:accessor !b #:init-keyword #:b #:init-value 0))

(define-method (equal? (c1 <color>) (c2 <color>))
  (pk "in extended equal? ...")
  (and (= (!r c1) (!r c2))
       (= (!g c1) (!g c2))
       (= (!b c1) (!b c2))))

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Problem merging generics across modules
       [not found]   ` <874lqxbgnf.fsf@ateguix.i-did-not-set--mail-host-address--so-tickle-me>
@ 2017-10-17 23:23     ` David Pirotte
  0 siblings, 0 replies; 6+ messages in thread
From: David Pirotte @ 2017-10-17 23:23 UTC (permalink / raw)
  To: Andrew Erlanger, guile-user

[-- Attachment #1: Type: text/plain, Size: 2180 bytes --]

Hi Andrew,

	Lets try to keep our conversation about this o the list, others may benefit
	from it, and/or help us ...

> Thanks for your feedback on this. It certainly resolved my issue.

Welcome.

> Can you point me to documentation on this? I read through the Guile info
> pages when trying to figure this out, but I couldn't figure it out.
> ...

No, there is no 'good' documentation neither recommendation(s) about 'generic
functions and the module system' in guile's manual - I mean 'no good' in my opinion,
but this is 'relative', and besides, some guilers are opposed to these
recommendations I always 'offer', but here they are:

1]	better preventing then curing:  when you define a module that use goops,
	always use

  #:duplicates (merge-generics
		replace
		warn-override-core
		warn
		last)

2]	unless you really know what you are doing, never call define-generic
	yourself

3]	always use #:export for class names, and make sure class names are
	unique (guile will warn you if not, at import time, so rename if you
	have conflict, never use twice the same class name in diff modules (unless
	you really know what you're doing))

4]	always use g-export for (and only for) getters, setters, accessors and
	methods

	https://git.savannah.gnu.org/cgit/guile-cv.git/tree/cv/support/g-export.scm

5]	remember, while you are developing and testing your code, you have to add
	'merge-generics to the default-duplicate-binding-handler in the repl as well,
	_after_ you imported goops (in the repl):

	,use (oop goops)
	(default-duplicate-binding-handler
	   '(merge-generics replace warn-override-core warn last))
	(use-modules (yourmod1) (yourmod2))

With these recommendations, you actually 'mimic' [1] the CLOS specification, which
states that anything related to CLOS lands in a specific package, visible to all
others.

David

[1]	mimic because unlike CLOS, a generic function in a guile module will
	only contain methods that comes from that module and the one it
	imports, but not (necessarily) all methods for that name (unless the module
	imports all the others that define a method for that name of course)

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-10-17 23:23 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-10-13  0:01 Problem merging generics across modules Andrew Erlanger
2017-10-13  0:03 ` Andrew Erlanger
2017-10-13 14:12 ` David Pirotte
2017-10-13 15:14   ` David Pirotte
2017-10-13 14:35 ` David Pirotte
     [not found]   ` <874lqxbgnf.fsf@ateguix.i-did-not-set--mail-host-address--so-tickle-me>
2017-10-17 23:23     ` David Pirotte

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).