Making functions *really* read-only

Home  >>  Odd  >>  Making functions *really* read-only

Making functions *really* read-only

On January 26, 2007, Posted by , In Odd,PowerShell, With 2 Comments

Over at the excellent PowerShell Team Blog, Bruce Payette wrote about Controlling PowerShell Function (Re)Definition.

However, making a function constant does not prevent it from being shadowed in child scopes. Given that a child scope is created in any script, new function definition or script block, this makes it very unconstant.

Consider this:

# define a constant function, one that cannot be replaced
set-item function:/funca -option constant {
  write-host "this is the outer funca"
  # redefine the function in this new scope
  function funca {
    write-host "this is the inner funca"
  }
  # call this new funca
  funca 
}

# call the outer one
funca

If you run it, you will see that the inner funca is defined and executed. So much for being constant.

You can, however, make it a bit more permanent by adding the allscope option to the outer function, like this:

# define a constant function, one that cannot be replaced
set-item function:/funca -option constant,allscope {
  write-host "this is the outer funca"
  # redefine the function in this new scope - this will fail!
  function funca {
    write-host "this is the inner funca"
  }
}

# call the outer one
funca

When we run this, we get an error:

PS> funcs
this is the outer funca
Cannot write to function funca because it is read-only or constant.
At C:/Documents and Settings/adrian/My Documents/proj/ps1/funcs.ps1:5 char:17
+   function funca  <<<< {

Why? Because allscope copies the function to all child scopes that are created. The default behaviour, when finding a function (or variable) is to look in the current scope, then the parent, and so on up to global. By automatically copying the constant function to all scopes, you can prevent the function from ever being redefined.

So to make a function as constant as possible, define it with constant,allscope in your $profile.

PS. It appears that you can use scope modifiers with test-path. These two commands do seem to work, although I’ve not seen any documentation regarding it:

test-path function:/funca
test-path function:/script:funca

However, get-childitem function: only shows one.

2 Comments so far:

  1. William says:

    hmm. I can’t reproduce that behavior on my rtm powershell. The outer function is always run and the inner function falls out of scope so I get:

    outer funca
    inner funca

    how are you getting those results? tia

  2. adrian says:

    Assuming you’re talking about the first example, you are seeing what I’m seeing, namely that the the outer constant funca is overridden in its own body, then executed, thus demonstrating that the outer funca is not constant.

    My output is:
    this is the outer funca
    this is the inner funca

    Maybe I wasn’t clear.