unofficial mirror of bug-guile@gnu.org 
 help / color / mirror / Atom feed
* bug#19459: #:export does not honor the merge-generics contract
@ 2014-12-28 18:20 David Pirotte
  2016-06-22 20:21 ` Andy Wingo
  0 siblings, 1 reply; 8+ messages in thread
From: David Pirotte @ 2014-12-28 18:20 UTC (permalink / raw)
  To: 19459

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

Hello,

	debian testing
	  GNU Guile 2.0.11	[ mine is .10-fa1a3-dirty, but it does not matter
					[ i'm stuck in that version until goops is patched
					[ and compiles guile-gnome again...
	    #:export does not honor the merge-generics contract

#:export should be adapted to honor the 'merge-generics user request and 'contract'.

What ever forces the 'system' to create a generic function, #:export in this case,
when 'merge-generics has been set, the system should or use the imported generic and
add the newly locally defined method, or create a new one [if that is necessary for its
internal machinery] _but_ [immediately] merge it with th imported one, since it is
the user request.

Right now, even with the merge-generic setting, #:export calls
module-ensure-local-variable! before anything else, creates a new get-with
var, first unbound, later turned into a generic function with 1
applicable [locally defined] method only _but_ it does _not_not merge it, leading to "No
applicable method..." bugs, as in the following example [simple and stupid, but it
only partially mimics real case situation, see the image.scm attachment of this mail
if you're interested http://www.mail-archive.com/guile-devel@gnu.org/msg12618.html

Happy hacking,
David

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


(define-class <a> ()
  (width #:accessor !width #:init-keyword #:width #:init-value 0))

(define-method (get-width (self <a>))
  (!width self))

(define-method (set-width (self <a>) width)
  (set! (!width self) width))
;;  module a ends here


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


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

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

(define-method (get-width (self <b>))
  (!width self))

(define-method (set-width (self <b>) width)
  (set! (!width self) width))
;;  module b ends here


scheme@(guile-user)> ,use (oop goops)
scheme@(guile-user)> (default-duplicate-binding-handler '(merge-generics replace warn-override-core warn last))
scheme@(guile-user)> ,use (b)
;;; note: source file ./b.scm
;;;       newer than compiled /home/david/.cache/guile/ccache/2.0-LE-8-2.0/usr/alto/projects/guile-tests/goops/export/b.scm.go
;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling ./b.scm
;;; compiled /home/david/.cache/guile/ccache/2.0-LE-8-2.0/usr/alto/projects/guile-tests/goops/export/b.scm.go
scheme@(guile-user)> (make <b>)
ERROR: In procedure scm-error:
ERROR: No applicable method for #<<generic> get-width (1)> in call (get-width #<<a> 2b70880>)

Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [1]> ,bt
In oop/goops.scm:
   1553:4  3 (#<procedure 2854c80 at oop/goops.scm:1551:0 (class . initargs)> #<<class> <b> 28cb8…>)
In guile-tests/goops/export/b.scm:
    17:20  2 (#<procedure 2b708e0 at guile-tests/goops/export/b.scm:14:0 (self initargs)> #<<…> …)
In oop/goops/dispatch.scm:
    239:9  1 (cache-miss #<<generic> get-width (1)> (#<<a> 2b70880>))
In unknown file:
           0 (scm-error goops-error #f "No applicable method for ~S in call ~S" (#<<generic> …> …) …)
scheme@(guile-user) [1]> 


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

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

* bug#19459: #:export does not honor the merge-generics contract
  2014-12-28 18:20 bug#19459: #:export does not honor the merge-generics contract David Pirotte
@ 2016-06-22 20:21 ` Andy Wingo
  2016-06-23 19:23   ` David Pirotte
  0 siblings, 1 reply; 8+ messages in thread
From: Andy Wingo @ 2016-06-22 20:21 UTC (permalink / raw)
  To: David Pirotte; +Cc: 19459

Hi,

On Sun 28 Dec 2014 19:20, David Pirotte <david@altosw.be> writes:

> (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.  I believe this is the source of your problem.  You are
expecting to extend the three generics and re-export them.  However to
do so you should #:re-export !width, get-width, and set-width.

AFAIU there is no bug here.  David WDYT?

Andy





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

* bug#19459: #:export does not honor the merge-generics contract
  2016-06-22 20:21 ` Andy Wingo
@ 2016-06-23 19:23   ` David Pirotte
  2016-06-23 20:06     ` Andy Wingo
  0 siblings, 1 reply; 8+ messages in thread
From: David Pirotte @ 2016-06-23 19:23 UTC (permalink / raw)
  To: Andy Wingo; +Cc: 19459

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

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

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

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

* bug#19459: #:export does not honor the merge-generics contract
  2016-06-23 19:23   ` David Pirotte
@ 2016-06-23 20:06     ` Andy Wingo
  2016-06-23 21:11       ` David Pirotte
  0 siblings, 1 reply; 8+ messages in thread
From: Andy Wingo @ 2016-06-23 20:06 UTC (permalink / raw)
  To: David Pirotte; +Cc: 19459

On Thu 23 Jun 2016 21:23, David Pirotte <david@altosw.be> writes:

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

I see.  You were expecting for the dynamically setting the
default-duplicate-binding-handler parameter to make a difference.
However I think this is maybe not the right way to set this up; see
reasoning in http://debbugs.gnu.org/cgi/bugreport.cgi?bug=20093.

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.

Did I get it right this time? :)

Andy





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

* bug#19459: #:export does not honor the merge-generics contract
  2016-06-23 20:06     ` Andy Wingo
@ 2016-06-23 21:11       ` David Pirotte
  2016-06-24  5:02         ` Andy Wingo
  0 siblings, 1 reply; 8+ messages in thread
From: David Pirotte @ 2016-06-23 21:11 UTC (permalink / raw)
  To: Andy Wingo; +Cc: 19459

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


> > 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.  
> 
> I see.  You were expecting for the dynamically setting the
> default-duplicate-binding-handler parameter to make a difference.

Yes, and it has to: with that setting, at any time in any module, there can be 1
and only 1 generic function for a given name:

	but as it is, right now, even with that setting, Guile is exporting
	'something' I did not ask to export, Guile is exporting a new binding,
	that I never asked to create [unless it would not be imported]

	from the (b) module perspective, I ask Guile to import (a), fill the generic
	functions with new methods, and export it, these generic functions, not new
	other ones

The symptom of the actual anomaly is very well expressed by my last sentence:

	"... 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 they can
	be only 1 generic function, with the method of (a) and the method of (b)..."

This should never raise an exception:

	scheme@(guile-user)> ,use (b)
	scheme@(guile-user)> (make <b>)

Currently it does if the user used #:export, it does not if he/she does not export

The problem is guile's module system, not GOOPS here

> However I think this is maybe not the right way to set this up; see
> reasoning in http://debbugs.gnu.org/cgi/bugreport.cgi?bug=20093.

	That is/was a totally different bug, but thanks for the fix! I disagree with
	the comment though, but I will pull, compile test it will take 1 or 2
	days... I hope it fixes it!

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

David




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

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

* bug#19459: #:export does not honor the merge-generics contract
  2016-06-23 21:11       ` David Pirotte
@ 2016-06-24  5:02         ` Andy Wingo
  2016-06-27  2:54           ` David Pirotte
  0 siblings, 1 reply; 8+ messages in thread
From: Andy Wingo @ 2016-06-24  5:02 UTC (permalink / raw)
  To: David Pirotte; +Cc: 19459

On Thu 23 Jun 2016 23:11, David Pirotte <david@altosw.be> writes:

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

Andy





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

* bug#19459: #:export does not honor the merge-generics contract
  2016-06-24  5:02         ` Andy Wingo
@ 2016-06-27  2:54           ` David Pirotte
  2016-06-27  7:47             ` Andy Wingo
  0 siblings, 1 reply; 8+ messages in thread
From: David Pirotte @ 2016-06-27  2:54 UTC (permalink / raw)
  To: Andy Wingo; +Cc: 19459

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

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>

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.

Then you tell me 'yep David, internally the system faced an import situation, and
ok, let's merge, _but_ #:export must export a fresh new ... I can buy that, but
do we have the technology do implement this senario? 

I believe, still referring to your last sentence, the above sentence as well and
respecting your position, that we should rather create/have global setting
'export-rexport-imported-gf' [or a better name.], WDYT?

Happy Hacking,
David

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

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

* bug#19459: #:export does not honor the merge-generics contract
  2016-06-27  2:54           ` David Pirotte
@ 2016-06-27  7:47             ` Andy Wingo
  0 siblings, 0 replies; 8+ messages in thread
From: Andy Wingo @ 2016-06-27  7:47 UTC (permalink / raw)
  To: David Pirotte; +Cc: 19459

On Mon 27 Jun 2016 04:54, David Pirotte <david@altosw.be> 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





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

end of thread, other threads:[~2016-06-27  7:47 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-12-28 18:20 bug#19459: #:export does not honor the merge-generics contract David Pirotte
2016-06-22 20:21 ` Andy Wingo
2016-06-23 19:23   ` David Pirotte
2016-06-23 20:06     ` Andy Wingo
2016-06-23 21:11       ` David Pirotte
2016-06-24  5:02         ` Andy Wingo
2016-06-27  2:54           ` David Pirotte
2016-06-27  7:47             ` Andy Wingo

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