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

From: Andy Wingo <[email-masked]>
Date: Mon, 27 Jun 2016 09:47:01 +0200

On Mon 27 Jun 2016 04:54, David Pirotte <[email-masked]> writes:

> Hello Andy,
>
>> >> However... I believe merge-generics is intended to merge duplicate
>> >> imported bindings.  It does not provide a copy-on-write version of an
>> >> imported generic, if that generic was not duplicated in the imports.
>> >> There is no facility in GOOPS to do that, AFAIU.  
>
>> > It is a module bug, not a GOOPS bug, see my 'personal/local' fix: the problem is
>> > that once the user uses #:export, guile's module system create a new binding,
>> > and it should not ... [hence this confusion as well: as it is: the module must
>> > merge its definition with the imported ones, even if it imported only 1
>> > generic ... because of a module bug...]  
>
>> I... I just think you're wrong here, sorry :/ That's just not how the
>> system works.  If you #:export an identifier in a module, you create a
>> fresh local binding, and that binding doesn't implicitly extend an
>> imported binding, merge-generics or no.  Merge-generics only operates on
>> the import interface of a module.
>
> I don't think so, and I feel sorry too ;/. We disagree, which is different, and
> nobody is 'right' or 'wrong' here. [and I know 'how the system works, I described
> it that in the original email, I'd like to change that, hence this thread ...]
>
> IMO, this should never fail:
>
> 	,use (b)
> 	make <b>

There are plenty of reasons for (make foo) to fail in the abstract --
from bad logic in the initializers, to the initializer using unbound
variables, to many other things.  In this case your initializer is:

(define-method (initialize (self <b>) initargs)
  (next-method)  
  (let ((a (make <a>)))
    (set-width self (get-width a))
    #;(add-child b a)))

However in this module you have done an #:export get-width on B, and so
the get-width that you are calling in module (b) has no methods for
values of type A.

> Your last sentence states that merge-generics only operates on the import interface
> of a module: that is the feature I'm referring to to claim the above:
>
>   [ using #:export ]
>
> 	t0	the system creates new 'empty' binding
> 		at some point 'it' knows it is a gf
> 	ti	it imports (a)
>
> 	now the (b) module interface is facing a duplicate gf binding, and according
> 	to the user settings wrt to this, it has to merge.

I'm not sure from this whether you think the current code has a bug or a
feature limitation.

For me :) I have it clear in my mind that there does not appear to be a
bug here.  merge-generics only operates over bindings that are imported
in a module: module A imports modules B and C, and B and C both export a
generic `foo'.  Or one is a generic and one is not, or something like
that.  merge-generics *does not* merge an imported binding and a local
definition.

It does seem like you want the system to work in a different way, and
that position is understandable -- however designing and implementing
such a thing is very low on my priority list right now and I doubt I
will get to it any time soon if at all.  If you or someone else wants to
implement this, the first step would be to come up with a design
including a patch to the documentation and mail the list.  I think I
would want the design before seeing a patch.

Regards,

Andy