Search code examples
powershellpure-function

Explicit Return in Powershell


I can write the following code in javascript:

function sum(num1, num2) {
  return num1 + num2;
}

and then get a value

var someNum = sum(2,5);

I would like to do the same thing in Powershell, but I read the following guide:

PowerShell also knows the return keyword; however, it follows a different logic. In general, the purpose of return is to end the execution of a code section and to give the control back to the parent block.

If you add a parameter to the return statement, the value will indeed be returned to the calling subroutine. However, this also applies for all other statements with an output. This means that any output produced in the function will be stored in the variable together with the return parameter.

I want to do this for the sake of having pure functions. However, it seems doing

var someNum = sum(2,5);

is entirely redundant, when I can just call the function above, define someNum inside of it, and it will be available in the global scope.

Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?


A bit tangential, but here is my actual code:

function GetPreviousKeyMD5Hashes() {
    $query = "SELECT Name, MD5, executed FROM [AMagicDb].[dbo].cr_Scripts";

    $command = New-Object System.Data.SQLClient.SQLCommand;
    $command.Connection = $connection;
    $command.CommandText = $query;

    try {
        $reader = $command.ExecuteReader();
        while ($reader.Read()) {
            $key = $reader.GetString(1)
            $previousScripts.Add($key) | Out-Null
        }

        $reader.Close();
        Write-Output "$(Get-Date) Finished querying previous scripts"
    }
    catch {
        $exceptionMessage = $_.Exception.Message;
        Write-Output "$(Get-Date) Error running SQL at with exception $exceptionMessage"
    }
}

and then:

$previousScripts = New-Object Collections.Generic.HashSet[string];
GetPreviousKeyMD5Hashes;

This code isn't clear to me at all - running GetPreviousKeyMD5Hashes does set $previousScripts, but this is entirely unclear to whoever modifies this after me. My only other alternative (afaik) is to have all this in line, which also isn't readable.


Solution

  • is entirely redundant, when I can just call the function above, define someNum inside of it, and it will be available in the global scope.

    No: functions execute in a child scope (unless you dot-source them with .), so variables created or assigned to inside a function are local to it.

    Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?

    Yes: The implicit output behavior only applies to statements whose output is neither captured - $var = ... - nor redirected - ... > foo.txt

    If there are statements that happen to produce output that you'd like to discard, use $null = ... or ... > $null

    Note: ... | Out-Null works in principle too, but will generally perform worse, especially in earlier PowerShell versions - thanks, TheIncorrigible1.

    If there are status messages that you'd like to write without their becoming part of the output, use Write-Host or, preferably Write-Verbose or, in PSv5+, Write-Information, though note that the latter two require opt-in for their output to be visible in the console.
    Do NOT use Write-Output to write status messages, as it writes to the success output stream, whose purpose is to output data ("return values").
    See this answer of mine for more information about PowerShell's output streams.

    The equivalent of your JavaScript code is therefore:

    function sum($num1, $num2) {
      Write-Host "Adding $num1 and $num2..."  # print status message to host (console)
      $num1 + $num2 # perform the addition and implicitly output result
    }
    
    PS> $someNum = sum 1 2 # NOTE: arguments are whitespace-separated, without (...)
    Adding 1 and 2...  # Write-Host output was passed through to console
    
    PS> $someNum  # $someNum captured the success output stream of sum()
    3