unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* [ANN] guile-file-names 0.2
@ 2019-05-16  9:06 Brandon Invergo
  2019-05-16 20:01 ` Mark H Weaver
  0 siblings, 1 reply; 7+ messages in thread
From: Brandon Invergo @ 2019-05-16  9:06 UTC (permalink / raw)
  To: Guile User

Hello,

I'm happy to announce the release of guile-file-names 0.2.

The (file-names) module provides methods for manipulating file names.
Its design distinguishes between the human-friendly string format of
filenames ("/usr/bin/guile") and a more Scheme-friendly representation
to take out all the little nuisances of working with file names.

This release sees bugs fixed, under-the-hood improvements, and new
features added.  See below for an excerpt from the NEWS file.

Download:
http://brandon.invergo.net/software/download/guile-file-names/guile-file-names-0.2.tar.gz
http://brandon.invergo.net/software/download/guile-file-names/guile-file-names-0.2.tar.gz.sig

guile-file-names-0.2.tar.gz sha256sum:
28d63bec3e484ae5c98431b77f0a1bcc50e2f351cf432b383009106a152c9dcf

If you use the library, please consider reporting any bugs you encounter
or feature requests you might have on the issue tracker on Gitlab:

https://gitlab.com/brandoninvergo/guile-file-names

Direct email to me is also fine, if you don't have a Gitlab account.

NEWS:

** Features

*** Native file-globbing sub-module
A new sub-module (file-names glob) has been added.  This module
provides the <file-name> method glob, which is used to perform
"file-globbing."   File-globbing is the method of finding multiple
files, e.g. at the shell, by matching wildcards
(e.g. "/usr/lib/*.so").

glob supports shell-style wildcards (*, ?, [] and **) as
well as full regular expressions.

*** string->file-name now takes optional keyword arguments
This allows you to override system- or parsed- defaults for the
volume, separator and case-sensitivity.

*** New <file-name> methods remove-prefix and remove-prefix!
These new methods convert an absolute file name to a relative one by
removing a leading directory prefix.

*** Specialized write and display methods for <file-name> objects

Running write or display on a <file-name> object now prints a
recognizable string representation of the file name rather than the
default cryptic GOOPS-object format:

#+BEGIN_EXAMPLE
scheme@(guile-user)> (use-modules (file-names))
scheme@(guile-user)> (define f (string->file-name "/usr/bin/guile"))
scheme@(guile-user)> (simple-format #t "~a\n" f)
#<<file-name> /usr/bin/guile>
#+END_EXAMPLE

** Bugs

*** Resolving an absolute file name
It is now an explicit error if there are double-dots at the root of
the file name (e.g. "/../foo/bar").  Also, a bug was fixed where
sequential double-dots ("../../../blah.txt") were not handled correctly.

*** file-name=? now behaves correctly for relative file names.

--
-brandon



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

* Re: [ANN] guile-file-names 0.2
  2019-05-16  9:06 [ANN] guile-file-names 0.2 Brandon Invergo
@ 2019-05-16 20:01 ` Mark H Weaver
  2019-05-16 20:06   ` Mark H Weaver
  2019-05-22 22:03   ` exporting GOOPS generic functions, was: " Brandon Invergo
  0 siblings, 2 replies; 7+ messages in thread
From: Mark H Weaver @ 2019-05-16 20:01 UTC (permalink / raw)
  To: Brandon Invergo; +Cc: guile-user

Hi Brandon,

Brandon Invergo <brandon@invergo.net> writes:

> I'm happy to announce the release of guile-file-names 0.2.
>
> The (file-names) module provides methods for manipulating file names.
> Its design distinguishes between the human-friendly string format of
> filenames ("/usr/bin/guile") and a more Scheme-friendly representation
> to take out all the little nuisances of working with file names.
>
> This release sees bugs fixed, under-the-hood improvements, and new
> features added.  See below for an excerpt from the NEWS file.

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.

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.

I can understand your decision to overload Guile's primitives that
accept file names.  However, I disagree with your decision to overload
'append' and 'append!'.  Those are not intended to be generic functions
in Scheme.  If they were, we wouldn't have 'string-append' and
'symbol-append'.

     Regards,
       Mark



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

* Re: [ANN] guile-file-names 0.2
  2019-05-16 20:01 ` Mark H Weaver
@ 2019-05-16 20:06   ` Mark H Weaver
  2019-05-17  8:36     ` Brandon Invergo
  2019-05-22 22:03   ` exporting GOOPS generic functions, was: " Brandon Invergo
  1 sibling, 1 reply; 7+ messages in thread
From: Mark H Weaver @ 2019-05-16 20:06 UTC (permalink / raw)
  To: Brandon Invergo; +Cc: guile-user

Hi again,

I wrote:
> 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.

Sorry, I meant to give an example of this.  Please see srfi/srfi-45.scm
in the Guile source distribution for an example of the preferred way to
override core bindings, such that only modules that import your module
are affected.

      Thanks,
        Mark



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

* Re: [ANN] guile-file-names 0.2
  2019-05-16 20:06   ` Mark H Weaver
@ 2019-05-17  8:36     ` Brandon Invergo
  0 siblings, 0 replies; 7+ messages in thread
From: Brandon Invergo @ 2019-05-17  8:36 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guile-user

Hi Mark,

Mark H Weaver writes:

> Sorry, I meant to give an example of this.  Please see srfi/srfi-45.scm
> in the Guile source distribution for an example of the preferred way to
> override core bindings, such that only modules that import your module
> are affected.

Thanks for the example.  I will make that change for the next release.
Actually, it's good that you mentioned it now because I want to go back
and make file-name-specialized variants of the rest of the core
procedures that normally take file-name strings (e.g. file ports).

I'll admit this wasn't a carefully thought-out strategy.  I was going by
the example in the GOOPS "Generic Function and Method Examples" section
of the manual (where + is specialized to handle <my-complex> objects).
I was having trouble figuring out how to do this such that the code
could be distributed as a module, and that was the only section that I
found that gave any clue.  I missed, however, the "#:replace"
functionality of define-module.

I would suggest that this section of the manual be updated to
demonstrate the preferred way to do it.

I also have no problem with replacing my specialized append and append!
methods with file-name-append and file-name-append! (in fact, that's
what they were originally, before I de-namespaced the method symbols).
It's still early days, so few people, if anybody, will be affected by
such a change.

Thanks for the tip!

--
-brandon



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

* Re: exporting GOOPS generic functions, was: [ANN] guile-file-names 0.2
  2019-05-16 20:01 ` Mark H Weaver
  2019-05-16 20:06   ` Mark H Weaver
@ 2019-05-22 22:03   ` Brandon Invergo
  2019-05-25  1:21     ` Mark H Weaver
  1 sibling, 1 reply; 7+ messages in thread
From: Brandon Invergo @ 2019-05-22 22:03 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guile-user

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?



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

* Re: exporting GOOPS generic functions, was: [ANN] guile-file-names 0.2
  2019-05-22 22:03   ` exporting GOOPS generic functions, was: " Brandon Invergo
@ 2019-05-25  1:21     ` Mark H Weaver
  2019-05-25  2:14       ` Mark H Weaver
  0 siblings, 1 reply; 7+ messages in thread
From: Mark H Weaver @ 2019-05-25  1:21 UTC (permalink / raw)
  To: Brandon Invergo; +Cc: guile-user

Hi Brandon,

Brandon Invergo <brandon@invergo.net> writes:

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

It's because you tried to export a binding that doesn't exist at the
top-level of your module.  That's because the definitions you gave above
are not in a top-level context.  They are local variables.

One easy solution is to use Guile's (@ module var) syntax to explicitly
reference a variable from a particular module.  For example:

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

Here, the reference (@ (guile) absolute-file-name?) explicitly asks for
the binding from the (guile) module.

Another option is to *rename* bindings while importing them, using
either #:prefix or #:select.  For example, see
module/system/repl/command.scm in Guile's source, which includes the
following lines in its 'define-module' form:

  #:use-module ((ice-9 pretty-print) #:select ((pretty-print . pp)))
  #:use-module ((system vm inspect) #:select ((inspect . %inspect)))

Those modules include 'pretty-print' and 'inspect' among their exported
bindings, but the bindings will be visible as 'pp' and '%inspect' within
command.scm.

When using #:select, the imports are limited to the bindings you
specifically asked for.  Sometimes it's more convenient to add a prefix
to every binding imported from a given module, like the following
example from Guile's module/ice-9/streams.scm:

(define-module (ice-9 streams)
  #:use-module ((srfi srfi-41) #:prefix srfi-41:)

Bindings can also be renamed when they are exported, and (ice-9 streams)
does this.

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

Yes, you're right.  If a binding is listed in the #:replace list,
there's no need to add it to the #:export list.

* * *

Finally, I wanted to mention that, in my opinion, the degree to which
this library modifies existing Guile bindings is regrettable.  I'm glad
that Guile is hackable in this way, but I generally prefer to avoid
libraries that do these kinds of hacks, unless there is a compelling
reason to justify it.

In my opinion, it would be better to use plain strings as the
representation for file names, and instead provide procedures to
conveniently manipulate those strings.

Consider:

(1) Your <file-name> objects are much larger than plain strings in terms
of their memory use.

(2) Every time you pass a <file-name> object to a file system primitive,
it needs to be converted back into a string, which makes them slower to
use as well.

(3) If Guile ever extends our file system primitives in the future,
e.g. by adding additional keyword or optional arguments, your library
will likely cause problems with that.  For example, a few years ago we
added the #:guess-encoding and #:encoding keyword arguments to
'open-file' and several other similar procedures.

Also, I had a question: a big part of your library seems to be aimed at
supporting Windows drive letters and separators.  Have tested your
library on Guile running on Windows?

One thing I know is that there's no need for us to use backslash as the
file separator on Windows.  My understanding is that the POSIX
compatibility layer, used in Guile's Windows port, automatically handles
interpreting forward slashes as a directory separator.  Guile already
includes code to try to ensure that from the perspective of Guile code,
we will see and use forward slashes uniformly, regardless of platform.
For example, see 'scm_i_mirror_backslashes' in libguile/load.c and its
associated comment.

      Regards,
        Mark



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

* Re: exporting GOOPS generic functions, was: [ANN] guile-file-names 0.2
  2019-05-25  1:21     ` Mark H Weaver
@ 2019-05-25  2:14       ` Mark H Weaver
  0 siblings, 0 replies; 7+ messages in thread
From: Mark H Weaver @ 2019-05-25  2:14 UTC (permalink / raw)
  To: Brandon Invergo; +Cc: guile-user

Hello again,

> Brandon Invergo <brandon@invergo.net> writes:
>
>> 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?
>
> It's because you tried to export a binding that doesn't exist at the
> top-level of your module.

I should explain more clearly what happened here.  The short answer is:
When you export a variable, it immediately shadows any imported bindings
with the same name.

The longer answer is that before a variable can be exported, we first
need a variable object to add to the public interface.  If you export a
variable before it has been defined (the usual case), Guile allocates a
fresh variable object and immediately adds it to the local module table,
although it marks the variable as "unbound", which essentially means
that the variable pretends not to exist.  However, the existence of this
"unbound" variable *does* have the effect of hiding any imports with the
same name.

       Mark



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

end of thread, other threads:[~2019-05-25  2:14 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-16  9:06 [ANN] guile-file-names 0.2 Brandon Invergo
2019-05-16 20:01 ` Mark H Weaver
2019-05-16 20:06   ` Mark H Weaver
2019-05-17  8:36     ` Brandon Invergo
2019-05-22 22:03   ` exporting GOOPS generic functions, was: " Brandon Invergo
2019-05-25  1:21     ` Mark H Weaver
2019-05-25  2:14       ` Mark H Weaver

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