5

Let's take the classic first-order functions example:

function Get-MyName { "George" }

function Say-Hi([scriptblock]$to) {
  Write-Host ("Hi "+(& $to))
}

This works just fine:

Say-Hi { "Fred Flintstone" }

this does not:

Say-Hi Get-MyName

because Get-MyName is evaluated, not passed as a value itself. How do I pass Get-MyName as a value?

4 Answers 4

10

You have to pass Get-Myname as a scriptblock, because that's how you've defined the variable type.

Say-Hi ${function:Get-MyName}
Sign up to request clarification or add additional context in comments.

8 Comments

How is this different from Say-Hi {Get-MyName}? Is there a type other than [scriptblock] that would allow passing of named functions?
In this case, there is no difference, because the return value is the same as the scriptblock definition of the function. Which one are you wanting to pass, the scriptblock of the function or the return value from the invocation of that scriptblock?
I would like to pass the function itself, same as you can do in javascript, or any of the .net languages using delegates
OK. We just did that. The 'value' of a function is the content of it's script block. In this case the script block is just "George".
That's not quite true, suppose Get-MyName took parameters, in your version you would have to know all the parameters that it is invoked with and forward them manually, if you're passing the function directly (as you would in js or c#) its parameters are whatever it is invoked with from inside Say-Hi
|
3

If you are ready to sacrifice the [scriptblock] parameter type declaration then there is one more way, arguably the simplest to use and effective. Just remove [scriptblock] from the parameter (or replace it with [object]):

function Get-MyName { "George" }

function Say-Hi($to) {
  Write-Host ("Hi "+(& $to))
}

Say-Hi Get-MyName
Say-Hi { "George" }

So now $to can be a script block or a command name (not just a function but also alias, cmdlet, and script).

The only disadvantage is that the declaration of Say-Hi is not so self describing. And, of course, if you do not own the code and cannot change it then this is not applicable at all.

I wish PowerShell has a special type for this, see this suggestion. In that case function Say-Hi([command]$to) would be ideal.

Comments

3

This might be a better example to illustrate the question, and details of execution scope. @mjolinor's answer appears to work nicely for this use case:

function Get-MyName($name) { $name; throw "Meh" }

function Say-Hi([scriptblock]$to) {
  try {
      Write-Host ("Hi "+(& $to $args)) # pass all other args to scriptblock
  } catch {
      Write-Host "Well hello, $_ exception!"
  }
}

The command and its output:

PS C:\> Say-Hi ${function:Get-MyName} 'George'
Well hello, Meh exception

In particular, I'm using this pattern for wrapping functions that work with a flaky remote SQL Server database connection, which sleep, then retry several times before finally succeeding or throwing a higher exception.

Comments

1

Strictly based on your code, the correct answer is this:

Say-Hi {(Get-MyName)}

This will produce "Hi George"

3 Comments

Thanks! Do you have any insight as to the difference between this syntax and that by @mjolinor above?
As @SamDevx said "strictly based on your code", there is not much difference. But if the function has parameters then calling it with parameters in this way will not be straightforward.
@GeorgeMauer The difference is that here is passed a new block in which Get-MyName is called. In mjolinor's example there is a direct reference to the Get-MyName-function passed. (With parameters this variation could be written like Say-Hi { Get-MyName @args } whereas mjolinor's would work without modification.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.