Skip to content

Fix given_or_derived behavior in object initialization #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

seanmil
Copy link
Contributor

@seanmil seanmil commented Apr 18, 2025

Currently, a given_or_derived attribute kind is treated as required in both the hash and positional argument initialization. As a required argument a value must always be given, thus preventing the "derived" half ever being used. This patch changes the behavior so that given_or_derived attributes are marked as optional in the initialization hash/arguments.

@seanmil
Copy link
Contributor Author

seanmil commented Apr 18, 2025

I spent quite a bit of time reading through the code and Henrik's series of blog posts before I came to the decision this PR was the right direction. I'd love for @hlindberg to comment as to his thoughts, if he's so inclined.

@hlindberg
Copy link
Contributor

Oh dear, it was ages ago since I had all that current in my head.
Could you help me with an example of usage as it at the moment is a bit too abstract to grok - especially what any possible downsides of the change could be.

@hlindberg
Copy link
Contributor

Byw, do you have access to the (very long) document about PCore?
If not I will go find it for you.

@hlindberg
Copy link
Contributor

Here is the PCore design document, which was the presentation and motivation for the type system and PCore. Much of this was later turned into specs and implementation. I think the document is still a good read for getting the background and ideas behind the type system. The doc is open to anyone with the link: https://docs.google.com/document/d/1Oq4VFISrhU44nnKWsYNB_Q_OLnPnodJz67pB8d53jys/edit?tab=t.0

The PCore specification states "The given_or_derived
attribute kind states that if the attribute is given
when the object is instantiated this value is used,
otherwise it is a computed value." and "It is
allowed to give the value when instantiating in
which case the given value overrides what would
be the computed value."

The current implementation treats an attribute
with the kind given_or_derived as a required
parameter during initialization (in the
init_hash used in from_asserted_hash or in the
generated `initialize` method).

This change enforces an implicit `value` parameter
of `nil` which causes the generated init_hash
type check and the generated initialize method
to treat given_or_derived attributes as optional.
@seanmil seanmil force-pushed the pops_init_hash_given_or_derived_fix branch from a516476 to 74012d4 Compare April 21, 2025 20:19
@seanmil seanmil changed the title Fix given_or_derived behavior in Pops object init_hash Fix given_or_derived behavior in object initialization Apr 21, 2025
@seanmil
Copy link
Contributor Author

seanmil commented Apr 21, 2025

@hlindberg sure, thanks for responding! Thanks for the link to the PCore doc - I had it, somewhere, but forgot I did. I put together a simple (but dumb) example here: https://github.com/seanmil/example_given_or_derived

Without this patch the given_or_derived argument is treated as required during initialization, which means that a conforming implementation would never be able to use the "derived" part. Given the PCore document (which is the clearest reference I've found) I believe this is the correct behavior.

Here is the output of the example I linked without the patch:

Error: Evaluation Error: Error while evaluating a Method call, The function 'new_Example' was called with arguments it does not accept. It expects one of:
  (Struct[{'int' => Integer, 'maybe_half' => Numeric}] hash)
    rejected: parameter 'hash' expects a value for key 'maybe_half'
  (Integer int, Numeric maybe_half)
    rejected: expects 2 arguments, got 1 (file: modpath/example_given_or_derived/manifests/init.pp, line: 2, column: 21)

And here it is with the patch:

Notice: Scope(Class[Example_given_or_derived]): Ex1.int: 44
Notice: Scope(Class[Example_given_or_derived]): Ex1.maybe_half: 22
Notice: Scope(Class[Example_given_or_derived]): ex2.int: 44
Notice: Scope(Class[Example_given_or_derived]): ex2.maybe_half: 10.0
Notice: Scope(Class[Example_given_or_derived]): ex3.int: 12
Notice: Scope(Class[Example_given_or_derived]): ex3.maybe_half: 6
Notice: Scope(Class[Example_given_or_derived]): ex4.int: 12
Notice: Scope(Class[Example_given_or_derived]): ex4.maybe_half: 24.3
Notice: Compiled catalog for xxxx in environment production in 0.02 seconds
Notice: Applied catalog in 0.00 seconds

Note that I just pushed an update that simplifies the fix and ensures it works with both hash and positional argument initialization (I had only considered hash initialization in the first version).

I also tinkered with a patch that would have PCore enforce that a given value was returned in the event the Object attribute's Ruby method didn't, but while it was a good further learning experience with the code it wasn't quite as elegant as I would have liked and, more importantly, I'm not sure it is necessary. I couldn't find anywhere in the spec where the PCore implementation is required to make sure the given value is returned so I think it is okay to trust the method author to return the value given (if given).

@hlindberg
Copy link
Contributor

Thanks for the example! Agree that this is clearly a bug and that treating it as optional is the right thing to do.
The checking of the datatype of returned values from the derived value method would be a nice guard rail, but could get quite expensive if the datatype is complex (i.e calling the type system to check) - otoh, would not be more expensive than if the value was given.

@hlindberg
Copy link
Contributor

Thought a bit more, I am not sure that "given or derived" is stated to guarantee that the given value is returned - imagine an attribute where you want it to be derived from the given value. No idea if that is very useful or if it needs to guarantee the return of a given.

@seanmil
Copy link
Contributor Author

seanmil commented Apr 23, 2025

Thought a bit more, I am not sure that "given or derived" is stated to guarantee that the given value is returned - imagine an attribute where you want it to be derived from the given value. No idea if that is very useful or if it needs to guarantee the return of a given.

That's an excellent point - okay, I'll shelve the other branch but it sounds like this fix is good to go. Thanks so much for your review & feedback! Cheers!

@hlindberg
Copy link
Contributor

Actually- after more thought, the behavior of changing a given value is like having a "setter" method for an attribute (e.g. making a string all upper case, trimming white space, etc). The "given or derived" is more of a "getter" style method.
I therefore think it is a good idea to check that a given value is indeed returned and not some other getter/setter style computed value as that would be confusing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants