Subject: Re: bug#19459: #:export does not honor the merge-generics contract

From: David Pirotte <[email-masked]>
Date: Thu, 23 Jun 2016 16:23:21 -0300

Hi Andy,

> > (define-module (a)
> >   #:use-module (oop goops)
> >   #:export (<a>
> > 	    !width
> > 	    get-width
> > 	    set-width))  

> Here you export four bindings: one class and three generics.  Those
> three generics have methods on <a>.

> > (define-module (b)
> >   #:use-module (oop goops)
> >   #:use-module (a)  

> Here you import the previous four bindings.

> >   #:export (<b>
> > 	    !width
> > 	    get-width
> > 	    set-width))  

> However here you declare that you are going to export four new
> bindings.
> ...

Under the exact circumstances of the original email, I disagree, see below.

	From the original email, you only kept the module defs and explains here in your
	answer the expected behavior in normal circumstances: the behavior one can
	expect from the default Guile configuration. Fine, but that's not the what
	the original email was complaining about :)

> AFAIU there is no bug here.  David WDYT?

IMO it is a bug, and to be honest, IMO it is a serious one: a user should never have
to use #:re-export for generic functions once he/she did ask to merge duplicate
generics, because under this setting, there can be 1 and only 1 generic function, at
any time in any module. Under these circumstances, it is the generic function as the
module 'sees it' that the user export, not the generic function as the module sees
it before import(s)

	if imported, then that generic is 'filled in' with new methods, it is _not_
	created [should not be created], hence #:export is the 'culprit', because as
	it is it does not look if a generic exists and create a new one arbitrarily,
	against the user 'wish'. It can only do so if there is no imported one.

So, IMO, under the '(merge-generics ...) setting of the original email (b) exports 1
new binding and 3 generic functions that were already defined by (a), but/and
'filled' with 3 additional methods. The module (b) exports the generic function 'as
it has it'.

There is another way, maybe, to look at this anomaly: if you comment the export for
get-width and set-width from the (b) module, it works:

	scheme@(guile-user)> ,use (b)
	scheme@(guile-user)> (make <b>)
	$4 = #<<b> 2051a40>

because within the module (b), the generics set-width and get-width were imported
from (a), not created [or 'immediately' merged, implementation detail...], and filed
with the (b) methods. #:export however dismantle this to export a new generic [but
the user ask to merge them, so it breaks the user 'contract'] only containing the (b)
methods, which is a bug.

Last but not least, when a user imports (b) and run (make <b>), he is not himself
calling set-width or get-width: exported or not, the code being run by make should
run within the (b) module, and in the (b) module the generic functions have been
merged,, so can not [should] raise an error. If you follow your idea/position, it
should only raise an error if I do (get-width (make <a>)) [but then the system might
raise the undefined <a> class first..., but you get the idea I guess.

FIWIW, It's been years that I don't use #:export because of this bug, I defined my
own g-export syntax, based on guile's module source code, here:

	http://git.savannah.gnu.org/cgit/grip.git/tree/grip/g-export.scm

It would be really good if it be fixed in 2.0.12 and master though.

Cheers,
David