Uploaded image for project: 'Puppet'
  1. Puppet
  2. PUP-6046

improve getting nested keys from hash

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Normal
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: PUP 4.5.0
    • Component/s: None
    • Labels:
    • Template:
    • Acceptance Criteria:
      Hide

      Function dig

      • That dig returns the result of a sequence of accesses via hash-keys/array indexes applied to a Collection (or an Undef value).
      • If at any point, the given container or the given key/index is undef, the result is undef.
      • Else, at any point if the given container is not a Collection, an error is raised

      Function then

      • That then calls the lambda given to it if it is given an arg that matches NotUndef and returns the result of the lambda, or if the value given to it is undef, that it returns undef
      • That then errors if the lambda given to it does not accept one argument, or if it is not given a lambda.

      Function lest

      • That lest calls the lambda given to it if it is given an arg that is undef and returns the result of the lambda, or if the value give to it matches NotUndef, that it returns that value.
      • That lest errors if the lambda given to it accepts arguments, or if it is not given a lambda
      Show
      Function dig That dig returns the result of a sequence of accesses via hash-keys/array indexes applied to a Collection (or an Undef value). If at any point, the given container or the given key/index is undef , the result is undef . Else, at any point if the given container is not a Collection, an error is raised Function then That then calls the lambda given to it if it is given an arg that matches NotUndef and returns the result of the lambda, or if the value given to it is undef , that it returns undef That then errors if the lambda given to it does not accept one argument, or if it is not given a lambda. Function lest That lest calls the lambda given to it if it is given an arg that is undef and returns the result of the lambda, or if the value give to it matches NotUndef , that it returns that value. That lest errors if the lambda given to it accepts arguments, or if it is not given a lambda
    • Epic Link:
    • Story Points:
      1
    • Sprint:
      Language 2016-05-18
    • Release Notes:
      New Feature
    • Release Notes Summary:
      Hide
      Accessing data in nested structures could something be difficult and required conditional logic to test for missing / optional parts of a structure. Three functions have been added to make such tasks easier; 'dig', 'then' and 'lest', which also combine nicely with the existing 'with' and 'assert_type' functions.
      The functionality of the dig function is similar to the "dot notation" used to access details in lookups.

      (Also see link in this ticket to henrik's blog post if a longer text is wanted).
      Show
      Accessing data in nested structures could something be difficult and required conditional logic to test for missing / optional parts of a structure. Three functions have been added to make such tasks easier; 'dig', 'then' and 'lest', which also combine nicely with the existing 'with' and 'assert_type' functions. The functionality of the dig function is similar to the "dot notation" used to access details in lookups. (Also see link in this ticket to henrik's blog post if a longer text is wanted).

      Description

      In a blog post, R.I.Pienaar writes:

      This [structured facts] makes everything better. It’s also structured data but this is still a bit awkward in Puppet:

      $x = $facts["foo"]["bar"]["baz"]
      

      There seems to be no elegant way to handle a missing ‘foo’ or ‘bar’ key, things just fail badly in ways you can’t catch or recover from. On the CLI you can do facter foo.bar.baz so we’re already careful to not have “.” in a key.

      We can actually use multiple keys in an access operator (you can directly slice an array with two arguments (from, to). We could use this for hash as well to mean a progression of key lookups producing undef if one key produces undef.

      The example would then be written:

      $x = $facts[foo, bar, baz]

      it would also allow getting a value from an array in by using a numeric key. Slicing can also be supported if the key is an array. Example, 'baz' is an array and we want the last 2 elements.

      $x = $facts[foo, bar, baz, [-2, 2]]

      UPDATE


      The original proposal would only work for hashes, as the access operator for array (and string) performs a slicing operation.
      A better solution is therefore to add a 'dig' function that performs a series of access operations and that stops with undef result if one access in the chain produces undef.

      To complete the support for "chain of operations" the accompanying functions 'then' and 'lest' round out the functionality.
      The PR for this ticket therefore contains the three functions 'dig', 'then' and 'lest'.

      The dig function performs a sequence of access operations on a complex collection similar to the 'dot-notation' used in hiera and facter.
      For example:

      $some_data.dig(somekey, 1, otherkey)
      

      If, at any point, the intermediate result is undef the operation stops and returns undef. It is an error to attempt to access something in a value that is neither Array nor Hash.

      The then function, accepts a single argument, and if it is not undef if calls a given block with the value. If given undef the block is not called and undef is returned.

      The lest function is the reverse of then. If given undef it calls the given block (without arguments), and returns what the block produced. If given something that is not undef it returns that value.

      Example:

      $some_structure.process()
        .then |$x| { $x.process_some_more() }
        .then |$x| { $x.process_further() }
        .lest || { fail("processing of structure resulted in undef value") }
      

      Or if a default value is wanted - in the middle of the chain if undef was the temporary result:

      $some_structure.process()
        .then |$x| { $x. process_some_more() }
        .lest || { 42 }
        .then |$x| { $x.process_further() }
      

      Naturally, for simple cases, a regular if/then/else expression or case expression is clearer - but for any chain of lookups, transformations, etc. where intermediate results are obtained and those can be undef the functional composition chain is a valuable tool as it results in more readable logic.

      In practice, the 'dig' function can produce the entire chain of access operations unless something needs to be done to a value in the middle of the chain (such as using it to lookup in hiera) before continuing. These are the cases when 'then' and 'lest' come into play.

        Attachments

          Issue Links

            Activity

              jsd-sla-details-panel

                People

                • Assignee:
                  Unassigned
                  Reporter:
                  henrik.lindberg Henrik Lindberg
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  5 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved: