Skip to content

OutVariable Transparency #120

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

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions 1-Draft/RFCNNNN-OutVariableTransparency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
RFC:
Author: Robert Holt
Status: Draft
SupercededBy: N/A
Version:
Area: Language/Common Variables
Comments Due: 2018-05-01
Plan to implement: Yes
---

# OutVariable Transparency

PowerShell supports OutVariables, which allow users to send the output of a command to the variable rather than to the pipeline.

Since this feature was included in PowerShell v1, the implementation detail of the ArrayList used to collect the output into the OutVariable has been exposed to the user, so that rather than getting the `object` or `object[]` that they would get with pipeline output, an `ArrayList` is always received.

This RFC floats the idea that OutVariable usage should be transparent to the user, so output is agnostic between OutVariable and the pipeline stream. As a corner case in PowerShell's implementation, OutVariables currently represent extra caveats for new users.

## Motivation

As a PowerShell user, I can use OutVariable just like pipeline output, so that my language experience is simpler and more consistent.
Copy link

@markekraus markekraus Mar 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with this motivation.
I specifically use this feature when I do NOT want the default pipeline behavior.

A multitude of cmdlets and functions return objects in an inconsistent way. Because -OutVariable always returns an ArrayList, I can ensure the result of the cmdlet/function has a consistent format

Take the following example function as a stand in for Get-ADUser and quite a few of the Exchange and Exchange Online cmdlets:

function Get-Result {
    [CmdletBinding()]
    param ()
    end {
        :outer
        switch( 1..9 | Get-Random) {
            1 { 'String'; break }
            2 { 1; break }
            3 { $null; break }
            4 { 1,2,3,4; break }
            5 { ,@(1,2,3,4) }
            6 { throw; break }
            7 { Write-Error 'SomeError'; break }
            8 { break :outer }
            9 { break }
        }
    }
}

Now, run the following and see all the various issues you encounter:

1..20 | %{
    $_
    $Result = 'Stale'
    try {
        $Result = Get-Result
    }
    Catch {
        'Caught'
    }
    finally {
        'Stale? {0}' -f ('Stale' -eq $Result)
        $Result.gettype().name
        $Result.Count
        $Result[0]
    }
}

A string will result in the array index being S, throw results in the $result being stale.

Compare that with

1..20 | %{
    $_
    $Result = 'Stale'
    try {
        $null = Get-Result -OutVariable Result
    }
    Catch {
        'Caught'
    }
    finally {
        'Stale? {0}' -f ('Stale' -eq $Result)
        $Result.gettype().name
        $Result.Count
        $Result[0]
    }
}

The ArrayList is always created fresh, so it is never stale even when the cmdlet errors or throws. it is always an ArrayList so I don't get weird indexing issues. I can always rely on the methods and properties of an ArrayList to be there.

If this is taken away and changed, this will break what I feel is the most stable and reliable method for retrieving output from commands. I will be forced to write ridiculous logic around Microsoft's cmdlets in many cases to try and deal with the insanity of the results they provide.


## Specification

This table summarises the changes proposed in this RFC:

| Input | Non-OutVar Result Type | Old Result Type | New Result Type |
| :----------------------------------------------: | :--------------------------: | :--------------------------: | :--------------------------: |
| `'Hello'` | System.String | System.Collections.ArrayList | System.String |
| `@(1, 2)` | object[] | System.Collections.ArrayList | object[] |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we use Object[]? we should be moving away from Object[] in favor of List<Object>. I don't see why taking a step back from ArrayList to Object[] is beneficial to anyone.

| `[System.Collections.ArrayList]::new(@(1,2))` | object[] | System.Collections.ArrayList | object[] |
| `@(,[System.Collections.ArrayList]::new(@(1,2))` | System.Collections.ArrayList | System.Collections.ArrayList | System.Collections.ArrayList |
| `$null` | ⊥ | System.Collections.ArrayList | ⊥ |

## Alternate Proposals and Considerations

This RFC would consitute a breaking change, and would break any
scripts depending on the output of OutVariable being a collection.

The alternative here is to leave OutVariables as they are, as an element of the language that users are used to, depend on and like.