Re: [PATCH] emacs: Rewrite scheme side in a functional manner.

2014-09-24 Thread Ludovic Courtès
Alex Kost  skribis:

> Ludovic Courtès (2014-09-21 23:27 +0400) wrote:
>
>> Alex Kost  skribis:
>>
>>> Ludovic Courtès (2014-09-20 18:11 +0400) wrote:

[...]

>> I was thinking about things generic enough to be in (guix profiles),
>> things that ‘guix package’ or guix-web could use.
>>
>> That said, it’s also fine do to things incrementally: write things
>> specifically for guix.el’s need, and generalize them later when we have
>> a clearer understanding of the situation.  I just thought it’s worth
>> keeping in mind.
>
> Thanks, I keep it in mind.  Such incremental approach is the only way I
> can write code.  I'm not able to construct a proper thing from the very
> beginning: I always make big changes when something new is added and I
> notice that a new wave of generalization is required.

Understandably, that’s pretty much the same for me.

[...]

>>> To get information about packages/outputs, different “search-types” are
>>> used (like ‘name’, ‘all-available’, ‘installed’).  These “search-types +
>>> search-vals” are transformed into a list of package/output patterns.
>>>
>>> Package patterns are:
>>>
>>> - package record;
>>> - list of name, version;
>>> - list of name, version, manifest entries;
>>> - list of name, version, manifest entries, packages.
>>
>> Oh, OK.  Do remove any ambiguity, an option would be to call them
>> “matches”, “search results”, “descriptors”, or “specifications”,
>> perhaps.  WDYT?
>>
>> (Maybe this is just bikeshedding, so your call.)
>
> I leave a “pattern” name for now.

OK.

>>> Output patterns are:
>>>
>>> - package record;
>>> - list of package record, output;
>>> - manifest entry record;
>>> - list of manifest entry record, packages;
>>> - list of name, version, output.
>>>
>>> Then these patterns are transformed into entries (alists with
>>> parameters/values) using “pattern->entries” functions created by
>>> ‘package-pattern-transformer’ and ‘output-pattern-transformer’.
>>
>> What about s/entries/sexps/?  That would make it clearer that the intent
>> is to serialize things, not to manipulate them internally.
>
> Yes, it is better, thanks.  I settled to “sexp” for this thing.

OK.

> Thanks for all your recommendations, I tried to follow them.
> I'm attaching the improved patch.  Is it good enough now?

Yes, please push.  Thanks for taking the time!

Ludo’.



Re: [PATCH] emacs: Rewrite scheme side in a functional manner.

2014-09-23 Thread Alex Kost
Ludovic Courtès (2014-09-21 23:27 +0400) wrote:

> Alex Kost  skribis:
>
>> Ludovic Courtès (2014-09-20 18:11 +0400) wrote:

[...]

>>> Overall it looks OK.  I’m concerned with code duplication between emacs/
>>> and guix/, though, and there are also a few stylistic issues, and
>>> missing or terse docstrings.
>>
>> I don't understand what duplication you mean.
>
> I was thinking about things generic enough to be in (guix profiles),
> things that ‘guix package’ or guix-web could use.
>
> That said, it’s also fine do to things incrementally: write things
> specifically for guix.el’s need, and generalize them later when we have
> a clearer understanding of the situation.  I just thought it’s worth
> keeping in mind.

Thanks, I keep it in mind.  Such incremental approach is the only way I
can write code.  I'm not able to construct a proper thing from the very
beginning: I always make big changes when something new is added and I
notice that a new wave of generalization is required.

[...]

>> We discussed using vhash, but I don't see how it can replace hash
>> table.  Here is the excerpt from the earlier message:
>
> Right, I remember that.  My point was just that either this optimization
> is not strictly needed, or it could go in (guix profiles), whether it
> uses a vhash or a hash table actually.
>
> But I think we actually agree on that, so that can still be addressed at
> a later stage.

Thanks.

[...]

>> To get information about packages/outputs, different “search-types” are
>> used (like ‘name’, ‘all-available’, ‘installed’).  These “search-types +
>> search-vals” are transformed into a list of package/output patterns.
>>
>> Package patterns are:
>>
>> - package record;
>> - list of name, version;
>> - list of name, version, manifest entries;
>> - list of name, version, manifest entries, packages.
>
> Oh, OK.  Do remove any ambiguity, an option would be to call them
> “matches”, “search results”, “descriptors”, or “specifications”,
> perhaps.  WDYT?
>
> (Maybe this is just bikeshedding, so your call.)

I leave a “pattern” name for now.

>> Output patterns are:
>>
>> - package record;
>> - list of package record, output;
>> - manifest entry record;
>> - list of manifest entry record, packages;
>> - list of name, version, output.
>>
>> Then these patterns are transformed into entries (alists with
>> parameters/values) using “pattern->entries” functions created by
>> ‘package-pattern-transformer’ and ‘output-pattern-transformer’.
>
> What about s/entries/sexps/?  That would make it clearer that the intent
> is to serialize things, not to manipulate them internally.

Yes, it is better, thanks.  I settled to “sexp” for this thing.

 +(define (get-package/output-entries profile params entry-type
 +search-type search-vals)
 +  "Return list of package or output entries."
>>>
>>> Never ‘get’.
>>
>> Do you mean I need to remove "get-" from the procedures' names?
>
> Yes.

Done.

[...]

>>> The docstring is too terse.
>>
>> The concept of "entry" is too common for the code in “guix-main.scm” to
>> write about it in every docstring.
>
> Yes, but still: there are 5 parameters.  I can tell what ‘profile’ is,
> but I have no clear idea about the type and meaning of the others at
> first glance.  Presumably ‘search-type’ and ‘entry-type’ are symbols,
> but then that still leaves a lot to be found out.

I improved some docstrings.

[...]

Thanks for all your recommendations, I tried to follow them.
I'm attaching the improved patch.  Is it good enough now?

>From f93af8a3a0b4d119a0e7ff23ce81bc7b28a31b60 Mon Sep 17 00:00:00 2001
From: Alex Kost 
Date: Thu, 18 Sep 2014 16:24:02 +0400
Subject: [PATCH 1/2] emacs: Rewrite scheme side in a functional manner.

* emacs/guix-main.scm: Rewrite in a functional way.  Add support for output
  entries.
  (%current-manifest, %current-manifest-entries-table,
  set-current-manifest-maybe!): Replace with...
  (manifest-entries->hash-table, manifest->hash-table): ... this.
  (manifest-entries-by-name+version): Replace with...
  (manifest-entries-by-name): ... this.
  (fold-manifest-entries): Rename to...
  (fold-manifest-by-name): ... this.
  (package-installed-param-alist): Rename to...
  (%manifest-entry-param-alist): ... this.
  (package-param-alist): Rename to...
  (%package-param-alist): this.
  (manifest-entry->installed-entry): Rename to...
  (manifest-entry->sexp): ... this.
  (manifest-entries->installed-entries): Rename to...
  (manifest-entries->sexps): ... this.
  (matching-generation-entries): Replace with...
  (matching-generations): ... this.
  (last-generation-entries): Replace with...
  (last-generations): ... this.
  (get-entries): Rename to...
  (entries): ... this.
  (installed-entries-by-name+version, installed-entries-by-package,
  matching-package-entries, fold-object, package-entries-by-name+version,
  package-entries-by-spec, package-entries-by-regexp, package-entries-by-ids,
  newest-available-package-entries, all-

Re: [PATCH] emacs: Rewrite scheme side in a functional manner.

2014-09-21 Thread Ludovic Courtès
Alex Kost  skribis:

> Ludovic Courtès (2014-09-20 18:11 +0400) wrote:

[...]

>> There are still a bunch of ‘set!’ and hash tables, though.  ;-)
>
> There are only 2 ‘set!’s in:
>
> (define manifest->hash-table
>   (let ((current-manifest #f)
> (current-table #f))
> (lambda (manifest)
>   "Return hash table of name keys and lists of matching MANIFEST entries."
>   (unless (manifest=? manifest current-manifest)
> (set! current-manifest manifest)
> (set! current-table (mentries->hash-table
>  (manifest-entries manifest
>   current-table)))
>
> and I think they are unavoidable.
>
> As for the hash tables, I use them because I don't see how they can be
> removed without a loss in efficiency.

In theory they could be hidden behind something like ‘memoize’, but
yeah, that’s fine.

>> Overall it looks OK.  I’m concerned with code duplication between emacs/
>> and guix/, though, and there are also a few stylistic issues, and
>> missing or terse docstrings.
>
> I don't understand what duplication you mean.

I was thinking about things generic enough to be in (guix profiles),
things that ‘guix package’ or guix-web could use.

That said, it’s also fine do to things incrementally: write things
specifically for guix.el’s need, and generalize them later when we have
a clearer understanding of the situation.  I just thought it’s worth
keeping in mind.

> I didn't write much docstrings (but I'll add more), because:
>
> - Most functions are trivial and self-descriptive by their names.
> - All functions are used only inside “guix-main.scm” anyway.

Fair enough. I don’t completely buy the “self-descriptive by their
names” thing, but okay, no big deal.

>> Some remarks:
>>
>>> +(define (mentries->hash-table mentries)
>>
>> For consistency I’d make it ‘manifest-entries->hash-table entries’.
>
> OK, I'll rename "mentries" to "manifest-entries" in the procedures'
> names, but I leave "mentry"/"mentries" for the local variables if you
> don't mind.
>
> The thing is, I use the term "entry" to name an alist of
> package/output/generation parameters that is passed to the elisp side.
> It looks like this:
>
> ((name . "guile")
>  (version . "2.0.11")
>  (outputs "out" "debug")
>  ...)
>
> So I shortened "manifest-entry" to "mentry" to distinguish these
> entities.

Oh, OK.

>>> +(define manifest->hash-table
>>> +  (let ((current-manifest #f)
>>> +(current-table #f))
>>> +(lambda (manifest)
>>> +  "Return hash table of name keys and lists of matching MANIFEST 
>>> entries."
>>> +  (unless (manifest=? manifest current-manifest)
>>> +(set! current-manifest manifest)
>>> +(set! current-table (mentries->hash-table
>>> + (manifest-entries manifest
>>> +  current-table)))
>>
>> What about:
>>
>>   (define manifest->hash-table
>> (memoize
>>   (compose manifest-entries->hash-table
>>manifest-entries)))
>
> I should have written that I tried to use ‘memoize’ there but it was
> significantly slower.  I tried a test manifest of 1200 entries and on my
> (very slow) computer it took in average:
>
> - about 3 seconds for the variant I suggest now;
> - about 15 seconds for the variant with ‘memoize’
>
> to get a list buffer with all packages.
>
> I think that happens because hash table procedures in ‘memoize’ use
> ‘equal?’ to compare arguments.  And ‘equal?’-ing big manifests for every
> package is slow.  That's why I made ‘manifest?=’ with ‘eq?’ at first
> place.
>
> Here is what happens when information about all packages is "requested":
> we need to check every package if it's installed or not, i.e. to lookup
> the same manifest for each package.  I don't see a better way then using
> an additional hash table.

Indeed, I stand corrected.

>> But honestly I think this is premature optimization (I mean, there are
>> 177 packages in my profile, so 10,000 ;-)), and should that optimization
>> be needed, it should be done transparently in (guix profiles), as we
>> discussed some time ago.
>
> I think this optimization is premature for putting it into (guix
> profiles) but I believe it is necessary for the special needs of
> "guix.el".

OK, I see now.

> Sorry, I don't understand what «10,000» means.

Sorry, typo: I meant there’s much less than 10 thousands packages in my
profile.

> We discussed using vhash, but I don't see how it can replace hash
> table.  Here is the excerpt from the earlier message:

Right, I remember that.  My point was just that either this optimization
is not strictly needed, or it could go in (guix profiles), whether it
uses a vhash or a hash table actually.

But I think we actually agree on that, so that can still be addressed at
a later stage.

>>> +(define* (mentries-by-name manifest name #:optional version output)
>>> +  "Return list of MANIFEST entries matching NAME, VERSION and OUTPUT."
>>> +  (let ((mentries (or (hash-ref (manifest->hash-table manifest)

Re: [PATCH] emacs: Rewrite scheme side in a functional manner.

2014-09-21 Thread Alex Kost
Ludovic Courtès (2014-09-20 18:11 +0400) wrote:

> Alex Kost  skribis:
>
>> From d42829fe03271e633e43cc35cf277705203e6080 Mon Sep 17 00:00:00 2001
>> From: Alex Kost 
>> Date: Thu, 18 Sep 2014 16:24:02 +0400
>> Subject: [PATCH 2/3] emacs: Rewrite scheme side in a functional manner.
>
> Good idea!

It was your idea actually :-)

> There are still a bunch of ‘set!’ and hash tables, though.  ;-)

There are only 2 ‘set!’s in:

--8<---cut here---start->8---
(define manifest->hash-table
  (let ((current-manifest #f)
(current-table #f))
(lambda (manifest)
  "Return hash table of name keys and lists of matching MANIFEST entries."
  (unless (manifest=? manifest current-manifest)
(set! current-manifest manifest)
(set! current-table (mentries->hash-table
 (manifest-entries manifest
  current-table)))
--8<---cut here---end--->8---

and I think they are unavoidable.

As for the hash tables, I use them because I don't see how they can be
removed without a loss in efficiency.

> Overall it looks OK.  I’m concerned with code duplication between emacs/
> and guix/, though, and there are also a few stylistic issues, and
> missing or terse docstrings.

I don't understand what duplication you mean.

I didn't write much docstrings (but I'll add more), because:

- Most functions are trivial and self-descriptive by their names.
- All functions are used only inside “guix-main.scm” anyway.

> Some remarks:
>
>> +(define (mentries->hash-table mentries)
>
> For consistency I’d make it ‘manifest-entries->hash-table entries’.

OK, I'll rename "mentries" to "manifest-entries" in the procedures'
names, but I leave "mentry"/"mentries" for the local variables if you
don't mind.

The thing is, I use the term "entry" to name an alist of
package/output/generation parameters that is passed to the elisp side.
It looks like this:

((name . "guile")
 (version . "2.0.11")
 (outputs "out" "debug")
 ...)

So I shortened "manifest-entry" to "mentry" to distinguish these
entities.

>> +(define manifest->hash-table
>> +  (let ((current-manifest #f)
>> +(current-table #f))
>> +(lambda (manifest)
>> +  "Return hash table of name keys and lists of matching MANIFEST 
>> entries."
>> +  (unless (manifest=? manifest current-manifest)
>> +(set! current-manifest manifest)
>> +(set! current-table (mentries->hash-table
>> + (manifest-entries manifest
>> +  current-table)))
>
> What about:
>
>   (define manifest->hash-table
> (memoize
>   (compose manifest-entries->hash-table
>manifest-entries)))

I should have written that I tried to use ‘memoize’ there but it was
significantly slower.  I tried a test manifest of 1200 entries and on my
(very slow) computer it took in average:

- about 3 seconds for the variant I suggest now;
- about 15 seconds for the variant with ‘memoize’

to get a list buffer with all packages.

I think that happens because hash table procedures in ‘memoize’ use
‘equal?’ to compare arguments.  And ‘equal?’-ing big manifests for every
package is slow.  That's why I made ‘manifest?=’ with ‘eq?’ at first
place.

Here is what happens when information about all packages is "requested":
we need to check every package if it's installed or not, i.e. to lookup
the same manifest for each package.  I don't see a better way then using
an additional hash table.

> But honestly I think this is premature optimization (I mean, there are
> 177 packages in my profile, so 10,000 ;-)), and should that optimization
> be needed, it should be done transparently in (guix profiles), as we
> discussed some time ago.

I think this optimization is premature for putting it into (guix
profiles) but I believe it is necessary for the special needs of
"guix.el".

Sorry, I don't understand what «10,000» means.

We discussed using vhash, but I don't see how it can replace hash
table.  Here is the excerpt from the earlier message:


Ludovic Courtès (2014-09-03 11:09 +0400) wrote:

> Alex Kost  skribis:
>
>> Ludovic Courtès (2014-09-01 16:10 +0400) wrote:
>
> [...]
>
>>> Would ‘vlist-fold’ work?
>>>
>>> scheme@(guile-user)> (vhash-cons 'a 1 (vhash-cons 'b 2 (vhash-cons 'a 3 
>>> vlist-null)))
>>> $2 = #
>>> scheme@(guile-user)> (vlist-fold cons '() $2)
>>> $3 = ((a . 3) (b . 2) (a . 1))
>>
>> Sorry, I don't see how it could work.  Here is an example:
>>
>> ;; What I currently have is a hash-table like this one:
>> (define table (make-hash-table 3))
>>
>> (hash-set! table 'a '(1 2 3))
>> (hash-set! table 'b '(4))
>> (hash-set! table 'c '(5 6))
>>
>> ;; And I can easily fold through unique keys like this:
>> (hash-fold (lambda (key entries res)
>>  (cons (cons key (apply + entries)) res))
>>'()
>>table) ; => ((c . 11) (b . 4) (a . 6))
>>
>> ;; Wh

Re: [PATCH] emacs: Rewrite scheme side in a functional manner.

2014-09-20 Thread Ludovic Courtès
Alex Kost  skribis:

> From d42829fe03271e633e43cc35cf277705203e6080 Mon Sep 17 00:00:00 2001
> From: Alex Kost 
> Date: Thu, 18 Sep 2014 16:24:02 +0400
> Subject: [PATCH 2/3] emacs: Rewrite scheme side in a functional manner.

Good idea!

There are still a bunch of ‘set!’ and hash tables, though.  ;-)

Overall it looks OK.  I’m concerned with code duplication between emacs/
and guix/, though, and there are also a few stylistic issues, and
missing or terse docstrings.

Some remarks:

> +(define (mentries->hash-table mentries)

For consistency I’d make it ‘manifest-entries->hash-table entries’.

> +(define manifest->hash-table
> +  (let ((current-manifest #f)
> +(current-table #f))
> +(lambda (manifest)
> +  "Return hash table of name keys and lists of matching MANIFEST 
> entries."
> +  (unless (manifest=? manifest current-manifest)
> +(set! current-manifest manifest)
> +(set! current-table (mentries->hash-table
> + (manifest-entries manifest
> +  current-table)))

What about:

  (define manifest->hash-table
(memoize
  (compose manifest-entries->hash-table
   manifest-entries)))

But honestly I think this is premature optimization (I mean, there are
177 packages in my profile, so 10,000 ;-)), and should that optimization
be needed, it should be done transparently in (guix profiles), as we
discussed some time ago.

> +(define* (mentries-by-name manifest name #:optional version output)
> +  "Return list of MANIFEST entries matching NAME, VERSION and OUTPUT."
> +  (let ((mentries (or (hash-ref (manifest->hash-table manifest) name)
> +  '(
> +(if (or version output)
> +(filter (lambda (mentry)
> +  (and (or (not version)
> +   (equal? version (manifest-entry-version mentry)))
> +   (or (not output)
> +   (equal? output  (manifest-entry-output mentry)
> +mentries)
> +mentries)))

What about using ‘manifest-lookup’ instead?

> +(define (mentry-by-output mentries output)
> +  (find (lambda (mentry)
> +  (string= output (manifest-entry-output mentry)))
> +mentries))

Likewise.

>  (define* (object-transformer param-alist #:optional (params '()))
> -  "Return function for transforming an object into alist of 
> parameters/values.
> +  "Return function for transforming objects into alist of parameters/values.

“Return a procedure transforming an object into an list of
parameter/value pairs.”

> -(lambda (object)
> +(lambda objects
>(map (match-lambda
>  ((param . fun)
> - (cons param (fun object
> + (cons param (apply fun objects
> alist

s/fun/proc/ (yeah, this is Scheme. ;-))

May be worth considering moving it to (guix records), which already has
‘alist->record’.

> +(define (spec->package-pattern spec)
> +  (call-with-values
> +  (lambda () (full-name->name+version spec))
> +list))

s/spec/specification/g

However, the result is not a “package pattern”, right?  I find the name
a bit confusing.

Is there any reason to use lists instead of multiple values?

> +(define (manifest-package-patterns manifest)
> +  "Return list of package patterns for all MANIFEST entries."
> +  (fold-manifest-by-name manifest
> + (lambda (name version mentries res)
> +   (cons (list name version mentries) res))
> + '()))

Likewise I’m unclear why this “package pattern” abstraction is needed
here.

Actually, is it just for serialization between Guile and Emacs?  If that
is the case, then ‘package->sexp’ would seem more appropriate.

> +(define (package-pattern-transformer manifest params)
> +  "Return 'package-pattern->package-entries' function."

Damn, what does this mean?  :-)

> +(define (get-package/output-entries profile params entry-type
> +search-type search-vals)
> +  "Return list of package or output entries."

Never ‘get’.  The docstring is too terse.

> +;;; XXX move to (guix profiles) ?
>  (define (profile-generations profile)

Definitely worth moving there (in a separate patch.)

Thanks,
Ludo’.