Subject: Re: bug#31210: Perl graft breakage

From: Mark H Weaver <[email-masked]>
Date: Thu, 19 Apr 2018 06:28:06 -0400

Hi again,

I wrote:
> The problem is that you should never use 'package/inherit' to create a
> graft.  That leads to an infinite tower of grafts.

I should explain why this is.  We cannot use (package/inherit P ...) to
define the replacement of package P, because it leads to a circular
definition.  Recall that (package/inherit P ...) applies the given
transformation to both P and its replacement.  In this case, P's
replacement is the thing we're trying to define.  See the circularity?

This would lead to an infinite recursion if not for the fact that
'replacement' is a "thunked" field, i.e. its evaluation is delayed.
Instead it leads to an infinite series of replacements, each one based
on the previous one, but with the transformation applied one more time.

In this example with 'perl', the transformation involved adding a new
phase.  Let's call that transformation 'f', so that (f perl) is the
replacement you wanted.  Since you used (package/inherit perl ...) to
define perl's replacement, it led to an infinite series of replacements:

  perl
  (f perl)
  (f (f perl))
  (f (f (f perl)))
  ...

I would have expected the grafting machinery to iterate until reaching
the end of this series of replacements.  I guess it didn't because the
(f (f perl)) build failed, since it added the phase twice.

Also note that since 'package/inherit' implicitly adds its own
'replacement' field, if you manually override 'replacement' within
'package/inherit' as you did in your first proposed patch, it expands
into a package definition with two 'replacement' overrides, like this:

  (package (inherit P)
    (replacement #f)
    ...
    (replacement ...))

Ideally, guix records would report an error in this case.

     Regards,
       Mark