guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: exporting GOOPS generic functions, was: [ANN] guile-file-names 0.2


From: Brandon Invergo
Subject: Re: exporting GOOPS generic functions, was: [ANN] guile-file-names 0.2
Date: Wed, 22 May 2019 23:03:03 +0100
User-agent: mu4e 1.2.0; emacs 26.2

Hi Mark or anyone else able to help,

Thanks again for raising this issue with my guile-file-names module:

Mark H Weaver writes:

> I see that you are using 'set!' to mutate several core bindings in the
> (guile) module to much slower GOOPS generic functions.  For example, you
> 'set!' the core 'append' procedure to a GOOPS generic function that adds
> support for appending together your <file-name> objects.

I've renamed the <file-name> append methods to something else that fits
well with the names of other methods.

However, I would still like to provide versions of all of the various
file-system procedures specialized on <file-name> objects.

Herein lies the rub: naturally, none of the examples that I see in the
SRFIsn use GOOPS.  I can't figure out how to export from my module
methods that are derived from GOOPS generic functions of built-in
procedures.

Take, for example, providing absolute-file-name? for <file-name> objects
without clobbering the ability to call it on strings.

>From what I understand, this should simply entail calling define-method
and adding the symbol to the define-module #:replace list.
define-method will first call define-generic an absolute-file-name? and
it will use the original procedure as the default of the generic
function.

So, as simple as:

    (define-module (file-names)
      ...
      #:replace (absolute-file-name?))

    (define-method (absolute-file-name? (f <file-name>)) ...)

When using the module, absolute-file-name? now works for <file-name>
objects, however it no longer works for strings:

    scheme@(guile-user)> (absolute-file-name? "/home/brandon/.guile")
    ERROR: In procedure scm-error:
    No applicable method for #<<generic> absolute-file-name? (1)> in call 
(absolute-file-name? "/home/brandon/.guile")

So, the original procedure isn't being used as the default for the
generic function?  How should I handle this?

Obviously I can't just do

    (define-method (absolute-file-name? (f <file-name>)) ...)
    (define-method (absolute-file-name? (f <string>))
      (absolute-file-name? f))

Because of infinite recursion.

I can try something like this:

    (let ((old-absolute-file-name? absolute-file-name?))
      (define-generic absolute-file-name?)
      (define-method (absolute-file-name? (f <file-name>))
        (proper-list? (route f)))
      (define-method (absolute-file-name? (f <string>))
        (old-absolute-file-name? f)))

But that strangely gives me this upon compiling the module:

    While compiling expression:
    Unbound variable: absolute-file-name?

I'm not sure what to make of that.  A compile-time error, but why?

That last attempt, of course, looks a lot like what's recommended in the
GOOPS manual ("8.6.5 Generic Function and Method Examples"), which would
be like this:

    (define-generic new-absolute-file-name?)
    (let ((old-absolute-file-name? absolute-file-name?))
      (define-method (new-absolute-file-name? (f <file-name>))
        (proper-list? (route f)))
      (define-method (new-absolute-file-name? (f <string>))
        (old-absolute-file-name? f)))
    (set! absolute-file-name? new-absolute-file-name?)

That works, but as you rightly point out, that rebinds the symbol
outright and thus doesn't even require it to be exported from the
module.  So, that won't play nicely with others.

I can try to take what you wrote very literally:

> If you must override core procedures, then please use #:export and
> #:replace in the 'define-module' form, and simply 'define' the new
> binding in your module instead of using 'set!'.  That way, the bindings
> in (guile) will be left unchanged, and your new bindings will only be
> used in modules that import your module.

    (define-module (file-names)
      ...
      #:export (absolute-file-name? ...)
      #:replace (absolute-file-name?))

    (define-generic new-absolute-file-name?)
    (let ((old-absolute-file-name? absolute-file-name?))
      (define-method (new-absolute-file-name? (f <file-name>))
        (proper-list? (route f)))
      (define-method (new-absolute-file-name? (f <string>))
        (old-absolute-file-name? f)))
    (define absolute-file-name? new-absolute-file-name?)

...i.e. just changing set! to define.  However, that gives me the same
"Unbound variable" compile-time error as above.

I'm clearly missing something obvious and I have no idea how to proceed.
GOOPS isn't behaving the way I would expect based on the manual:

 -- syntax: define-generic symbol
     Create a generic function with name SYMBOL and bind it to the
     variable SYMBOL.  If SYMBOL was previously bound to a Scheme
     procedure (or procedure-with-setter), the old procedure (and
     setter) is incorporated into the new generic function as its
     default procedure (and setter).  Any other previous value,
     including an existing generic function, is discarded and replaced
     by a new, empty generic function.

The procedure is written in Scheme, not C (in ice-9/boot-9.scm), so I
don't expect to have to treat it specially in any way like primitives
might require.

So, I feel like I'm blindly trying to get things to work without
understanding what's happening under the hood.  That's not the right way
to go about things.

So, how does one provide a module that replaces symbols for built-in
procedures with generic functions, such that these generics provide both
specialized methods and a default function using the original
procedure?

Thanks for your help,

-brandon

ps - You say that I should use #:export and #:replace, but according to
the manual, #:replace "[exports] all identifiers in LIST ... and mark[s]
them as “replacing bindings”."  So, shouldn't be unnecessary to put the
symbol in both lists?



reply via email to

[Prev in Thread] Current Thread [Next in Thread]