Skip to content
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

AutomationNull Behaviour #9997

Closed
vexx32 opened this issue Jun 24, 2019 · 44 comments
Closed

AutomationNull Behaviour #9997

vexx32 opened this issue Jun 24, 2019 · 44 comments
Labels
Committee-Reviewed PS-Committee has reviewed this and made a decision Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-Answered The question is answered.

Comments

@vexx32
Copy link
Collaborator

vexx32 commented Jun 24, 2019

See the comments in #9794 for the full extent of the existing discussion, starting with #9794 (comment)

AutomationNull.Value is sometimes detectable and distinguishable from $null in certain cases, for example:

using namespace System.Management.Automation.Internal

$null -is [psobject] # false
[AutomationNull]::Value -is [psobject] # true

@($null).Count # 1
@([AutomationNull]::Value).Count # 0

@SeeminglyScience mentioned a possible way we could have it be handled more closely like [dbnull] and [nullstring] are being handled as of #9794, without losing its current function in the pipeline internals, in #9794 (comment):

Ideally if this were to be fixed it would just be removed from assignment, e.g. $obj = [AutomationNull]::Value would populate $obj with true null. The -is operator is one of the few things that never "lie" on occasion and I think it would be a detriment to change that.

You can also make [AutomationNull]::Value -is [AutomationNull] work by:

  1. Removing the static keyword from the class decl
  2. Make it inherit PSObject
  3. Change the singleton instance to new AutomationNull()

That would allow $autoNull -is [psobject] to still work while enabling $autoNull -is [AutomationNull]. Though I think there are a few places in the compiler where it uses a pattern like if (expr.ExpressionType == typeof(PSObject) && expr.Value == AutomationNull.Value) so that might need to change.

/cc @daxian-dbw @mklement0

I'm personally in favor of Patrick's solution, as it enables the very clear and concise $item -is [AutomationNull] with few downsides (the biggest downside being potential implementation complications, but in my opinion these are probably worth tackling). Interested to hear any further discussion on this! 💖

@vexx32 vexx32 added the Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a label Jun 24, 2019
@vexx32 vexx32 changed the title AutomationNull.Value Behaviour AutomationNull Behaviour Jun 24, 2019
@mklement0
Copy link
Contributor

mklement0 commented Jun 25, 2019

using namespace System.Management.Automation.Internal
[AutomationNull]::Value -is [AutomationNull]

returning $true makes perfect sense both intrinsically and for symmetry with [DbNull]::Value -is [DbNull] and [NullString]::Value -is [NullString] returning $true.

The current workaround for detecting [System.Management.Automation.Internal.AutomationNull]::Value actually requires two tests (to also distinguish [AutomationNull]::Value from an empty collection object).

$null -eq $someValue -and @($someValue).Count -eq 0

As for why there is an occasional need to distinguish between a true $null and [System.Management.Automation.Internal.AutomationNull]::Value:

A command that produces no output technically outputs [System.Management.Automation.Internal.AutomationNull]::Value, which is distinct from a command explicitly returning $null.

While $null and [System.Management.Automation.Internal.AutomationNull]::Value are treated the same in an expression context, their behavior differs fundamentally in the pipeline:

# A true $null is sent through the pipeline. 
PS> & { $null }  | % { 'here' }
here

# A command with no output technically outputs [System.Management.Automation.Internal.AutomationNull]::Value, which is NOT sent through the pipeline - it is an empty enumeration.
PS> & { }  | % { 'here' }
# NO output

Also, because the switch statement treats its operand as an enumeration, a [System.Management.Automation.Internal.AutomationNull]::Value value causes the statement to be skipped altogether; that is, it is effectively ignored:

$val = & {}
# Because $val contains [System.Management.Automation.Internal.AutomationNull]::Value,
# the switch statement is effectively ignored;
switch ($val) {
  default { 'hi' }
}

Related issues:


Just like [AutomationNull]::Value -is [AutomationNull] currently being $false is a head-scratcher, so is [AutomationNull]::Value -is [psobject] being $true and - given the unfortunate identity of [psobject] and [pscustomobject] - [AutomationNull]::Value -is [pscustomobject] being equally $true.

[AutomationNull]::Value -is [psobject] being $true makes no sense from an end-user perspective, and looks like another case of [psobject] peeking from behind the curtain - see #5579.

@iSazonov
Copy link
Collaborator

The AutomationNull is defined in System.Management.Automation.Internal namespace that implies only internal non-public use. We free to change the internal AutomationNull but it make sense only if it is really needed for addressing important scenarios.

@vexx32
Copy link
Collaborator Author

vexx32 commented Jun 25, 2019

If it is purely for internal use it should never be leaking in the ways described above.

However, due to the way it's intended to behave, I'm not sure it can be purely internal and still provide the same utility.

@bpayette
Copy link
Contributor

@iSazonov is correct. AutomationNull is for internal use by the runtime. It exists to distinguish an empty stream from a stream containing null.

@vexx32

AutomationNull.Value is sometimes detectable and distinguishable ... for example...

All of your examples explicitly reference the type. I would hardly call that a "leak".

However, due to the way it's intended to behave, I'm not sure it can be purely internal and still provide the same utility

What do you think it should do that it currently doesn't do and why do you think that behaviour is useful? For example, this

using System.Management.Automation.Internal
[AutomationNull]::Value -is [AutomationNull]

does not qualify as useful because it's an internal class.

@vexx32
Copy link
Collaborator Author

vexx32 commented Jun 25, 2019

Sure, for simplicity of definition I opted to use the explicit class name. @mklement0 offered several other ways to obtain the value. Also, it's not internal. I can reference the type name from PS directly. It might be in the internal namespace, but it's public.

Here's another way to get an auto-null:

$a = if ($false) { Do-Thing }

Contrived? Yes. Easily found in a real application? Absolutely. Even more so in a pipeline which may not return any value.

@bpayette
Copy link
Contributor

@vexx32

It's in the internal namespace:

System.Management.Automation.Internal

This namespace contains classes and interfaces that must be public for some reason but are not considered part of the public PowerShell API.

{master}PSCore (1:1) >  $a = if ($false) { Do-Thing }
{master}PSCore (1:2) >  $a -eq $null
True
{master}PSCore (1:3) >  $a.GetType().FullName
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $a.GetType().FullName
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

{master}PSCore (1:4) >

So where does AutomationNull show up in this?

@vexx32
Copy link
Collaborator Author

vexx32 commented Jun 25, 2019

As Dongbo mentioned in the linked PR, AutomationNull doesn't respect GetType(); it's special-cased in that instance to appear as $null. However, compare $a in that example with true $null as follows:

$a -is [psobject] # true
$null -is [psobject] # false

@($a).Count # 0
@($null).Count # 1

If it's not meant to be part of the public API, then y'all need to figure out how to hide it better. 😉

@SeeminglyScience
Copy link
Collaborator

SeeminglyScience commented Jun 25, 2019

@BrucePay I've had to account for it before with methods that have a parameter typed as object[]. For example:

$a = if ($false) { Do-Thing }
[type]::GetTypeArray(@(0, $a, 1))

And to be fair, the documentation does not do a great job at explaining that it shouldn't be used (and by that I mean it says that it's imperative that you do use it). You also have to remember the audience, most PowerShell folks won't think twice about the namespace because they aren't familiar with that pattern. Granted, most folks who end up needing AutomationNull will probably be pretty technical, but still.

Whether or not it was intended to be used, I'd still advise that care be taken when it comes to breaking changes.

@mklement0
Copy link
Contributor

Good points, @vexx32 and @SeeminglyScience.

I don't think [AutomationNull] can be hidden, because users need to understand how it differs from $null and need to be able to inspect a given value to infer its pipeline behavior - and also more obscure differences such as @SeeminglyScience's method-argument example.

In other words: it is insufficient for PowerShell to pretend that a type('s singleton) is $null when it actually behaves differently, situationally.

That difference must be discoverable, and having to resort to obscure and cumbersome workarounds to detect [AutomationNull]::Value - which you'll never discover unless you know about this "internal" type - is obviously unsatisfactory.

So, yes,

$var = & {}
$var -is [AutomationNull]

is useful - and that it currently returns $false is self-contradictory.

Furthermore, the type must be documented in the end-user docs.

@mklement0
Copy link
Contributor

@SeeminglyScience, stumbling upon this again I've noticed bizarre behavior around your example:

# This FAILS, as in your example.
$anull = & {}; [Type]::GetTypeArray(@(0, $anull)).Name | Should -Be 'Int32', 'PSObject'

# This SUCCEEDS - single-element array, with just AutomationNull.
$anull = & {}; [Type]::GetTypeArray(@(, $anull)).Name | Should -Be 'PSObject'

# RETRYING the ORIGINAL command NOW SUDDENLY SUCCEEDS.
$anull = & {}; [Type]::GetTypeArray(@(0, $anull)).Name | Should -Be 'Int32', 'PSObject'

@SeeminglyScience
Copy link
Collaborator

Yeah, looks like a binder bug. There must be a difference in how it approaches converting single item arrays vs more populated arrays. That would explain why the third works, the binder would still be cached.

@mklement0
Copy link
Contributor

Thanks, @SeeminglyScience; I've created #11118.

@mklement0

This comment has been minimized.

@iSazonov
Copy link
Collaborator

Are we still tracking anything in the issue or we can close?

@SeeminglyScience
Copy link
Collaborator

Not sure, but I do want to clarify that my previous idea of:

Ideally if this were to be fixed it would just be removed from assignment, e.g. $obj = [AutomationNull]::Value would populate $obj with true null.

isn't feasible because then these two examples would react differently:

$anull = $null | % {}
$anull | % { 'something' }

# vs
$null | % {} | % { 'something' }

@mklement0
Copy link
Contributor

@SeeminglyScience, do you still think the other part of the proposal - making -is [AutomationNull] work - is feasible without breaking things?

If so, we'd also have to introduce [AutomationNull] as a type accelerator to make this practical.

@SeeminglyScience
Copy link
Collaborator

If the goal is to make -is [AutomationNull] work, that's the way to do it imo. I don't know that it necessarily needs to work since well over 99% of the time you don't need to worry about auto null and the current methods work fine.

AutomationNull is also still in sort of a gray area where it isn't "technically" supported. It may be worth while to either move AutomationNull or make it internal (preferably the former) similar to what ASP.NET did with their pubternal API's.

Might even be able to get away without any breaking changes by doing this:

namespace System.Management.Automation
{
    public sealed class AutomationNull : PSObject
    {
        private AutomationNull()
        {
        }

        public static AutomationNull Value { get; } = new AutomationNull();
    }
}

namespace System.Management.Automation.Internal
{
    [Obsolete("Use System.Management.Automation.AutomationNull")]
    public static class AutomationNull
    {
        public static PSObject Value => System.Management.Automation.AutomationNull.Value;
    }
}

Auto null should probably be officially supported before any additional enhancements are made.

@mklement0
Copy link
Contributor

Thanks, @SeeminglyScience - my vote is definitely to do it, even if it's only needed 1% of the time, so users can solve the mysteries discussed above.

@iSazonov
Copy link
Collaborator

@mklement0 Do you ready to ask PowerShell committee?

@mklement0
Copy link
Contributor

I am, @iSazonov - thanks.

@iSazonov
Copy link
Collaborator

/cc @SteveL-MSFT for PowerShell Committee conclusion.

@SteveL-MSFT SteveL-MSFT added the Review - Committee The PR/Issue needs a review from the PowerShell Committee label Feb 3, 2020
@BrucePay
Copy link
Collaborator

@SeeminglyScience

If the goal is to make -is [AutomationNull] work.

Why? If you want to check for AutomationNull you should do $x == [AutomationNull]::Value. because it's a singleton.

AutomationNull is primarily designed for use in the pipeline to distinguish between the value $null and no value (the empty stream.) When a pipeline returns AutomationNull it is converted into a $null on variable assignment.

AutomationNull needed to be publicly visible for various reasons. For example, a compiled cmdlet might want to explicitly return it. (Though generally, if the cmdlet doesn't call write, the pipeline processor will automatically return AutiomationNull.)

Overall, when we introduced AutomationNull in V1, sorting out when something was $null versus AutomationNull was extremely painfull. There were tons of related bugs that took a long time to fix properly. So any significant changes to AutomationNull are likely to result in a whole bunch of new, obscure bugs so I would classify it as high risk change. Conversely, as far as I can see, the proposed changes don't actually add significant value. Why should we take a high-risk low-value change?

@mklement0
Copy link
Contributor

mklement0 commented Feb 20, 2020

Why?

For the reasons outlined above.

If you want to check for AutomationNull you should do $x == [AutomationNull]::Value.

That doesn't help with checking, the awkwardness and obscurity of which has been outlined above, due to the non-distinction between $null and $AutomationNull in expressions; to recap:

# Awkward and obscure test whether $x contains [AutomationNull]::Value
$null -eq $x -and @($x).Count -eq 0

When a pipeline returns AutomationNull it is converted into a $null on variable assignment.

That was true up to v2; v3+ preserves the value, and that mustn't change.

PS> $x = & {}; $null -eq $x -and @($x).Count -eq 0
True

So any significant changes to AutomationNull are likely to result in a whole bunch of new, obscure bugs

I agree that making [AutomationNull]::Value no longer a true singleton, as proposed above, is problematic.

However, what I think would be a low-risk change - if feasible (can't speak to that) - is to have the -is operator tell a new white (useful) lie (actually: truth), the way it already does for PSCustomObject, without the implementation of [AutomationNull]::Value needing to change:

# Technically a PSObject, but presents as PSCustomObject
PS> [pscustomobject] @{} -is [System.Management.Automation.PSCustomObject]
True

Therefore, along with implementing [AutomationNull] as a type accelerator, the following should then work:

PS> $x = & {}; $x -is [AutomationNull] 
True  # wishful thinking

I think that would be sufficient and makes for a nice complement to the pending implementation of -is $null (#10704)

@SeeminglyScience
Copy link
Collaborator

SeeminglyScience commented Feb 20, 2020

@SeeminglyScience

If the goal is to make -is [AutomationNull] work.

Why?

I agree for the most part. I don't think it comes up anywhere near often enough to really touch it aside from maybe making a actual public version that just essentially points to the pubternal one.

The wording there was carefully chosen to convey that but it probably wasn't direct enough. My change proposal is more "if it's decided that it should be done for reason x, then this is how it should be done".

When a pipeline returns AutomationNull it is converted into a $null on variable assignment.

It was at one point, but it's assigned to the variable now. Here's an example of what I assume is the reason why, copied from one of my comments above:

# If autonull wasn't saved, these two examples would act differently.
$anull = $null | % {}
$anull | % { 'something' }

# vs
$null | % {} | % { 'something' }

AutomationNull needed to be publicly visible for various reasons. For example, a compiled cmdlet might want to explicitly return it. (Though generally, if the cmdlet doesn't call write, the pipeline processor will automatically return AutiomationNull.)

Why pubternal then? Why not just a normal public API?

So any significant changes to AutomationNull are likely to result in a whole bunch of new, obscure bugs so I would classify it as high risk change. Conversely, as far as I can see, the proposed changes don't actually add significant value. Why should we take a high-risk low-value change?

I'm not sure I necessarily agree with the risk assessment, but I'm not arguing the low value side of that. @mklement0 you're welcome to make a case for it. Hah you did as a pressed submit 🙂

@vexx32
Copy link
Collaborator Author

vexx32 commented Feb 20, 2020

Point of clarity that seems to have been ever so slightly glossed over... you can't check $value -eq [automationnull]::Value in PowerShell and expect the right result; $null -eq [automationnull]::Value will always return $true

@mklement0
Copy link
Contributor

Good point, @vexx32, and, in a similar vein, to amend my previous comment:

While [pscustomobject] @{} -is [System.Management.Automation.PSCustomObject] returning $true is a - beneficial - lie, $x = & {}; $x -is [AutomationNull] returning $true would actually be telling the truth - that the current conflation of [AutomationNull]::Value with $null gets in the way of.

@SeeminglyScience
Copy link
Collaborator

SeeminglyScience commented Feb 21, 2020

@mklement0 That's actually not a lie. When you create a PSObject without a base object, PSCustomObject is used:

/// <summary>
/// Initializes a new instance of PSObject with an PSCustomObject BaseObject.
/// </summary>
public PSObject()
{
CommonInitialization(PSCustomObject.SelfInstance);
}

@mklement0
Copy link
Contributor

@SeeminglyScience, but it is still wrapped in a PSObject, isn't it?

PS> [type]::GetTypeArray(([pscustomobject] @{})).Name
PSObject

And speaking of conflation and lies: the conflation of [psobject] and [pscustomobject], both of which are PSObject, makes the following virtually useless:

PS> [pscustomobject] @{} -is [pscustomobject]
True  # OK, but ....

PS> (Get-Item /) -is [pscustomobject]
True  # !! ALSO true, because  [pscustomobject]  is the SAME AS [psobject]

-is [pscustomobject] working the same as -is [System.Management.Automation.PSCustomObject] would be a more beneficial lie, by far - the current behavior is both useless and confusing.

And let's not forget that with -as even the verbose [pscustomobject] @{} -as [System.Management.Automation.PSCustomObject] doesn't work, let alone -as [pscustomobject]: #4343

Note that there is already precedent for situationally treating [psboject] and [pscustomobject] differently:

# `[pscustomobject] @{}`  is *syntactic sugar* for constructing a PSCustomObject 
PS> ([pscustomobject] @{}).GetType().Name
PSCustomObject

# `[psobject] @{}`, by contrast, confusingly creates a [hashtable] that is
# virtually invisibly and uselessly in a PSObject instance.
# This despite the fact that both `New-Object PSObject` 
# and `[psobject]::new()` *do* create PSCustomObject instances.
PS> ([psobject] @{}).GetType().Name
HashTable

@SeeminglyScience
Copy link
Collaborator

SeeminglyScience commented Feb 21, 2020

@SeeminglyScience, but it is still wrapped in a PSObject, isn't it?

Well yeah sure but most things are, most of the time. PSObject is the magic wrapper that makes the language functional (I know you know this, bear with me), so really it's lying in the same way that [psobject]'string' -is [string] is lying.

The other stuff is probably better suited for a new issue.

My point is that it's not worth making -is lie (aside from the single "lie" of evaluating a psobject's base object) to make $anull -is [automationnull] work for the dozen of us that will use it once it year. If something was going to change, I really don't think my proposal is dangerous. I understand that when significantly more substantial changes were made in the past that there were understandably difficult to resolve bugs. My proposal is very small though, it's unlikely that any code outside of the class itself would need to be changed.

@SeeminglyScience
Copy link
Collaborator

SeeminglyScience commented Feb 21, 2020

@mklement0 I missed this initially:

I agree that making [AutomationNull]::Value no longer a true singleton, as proposed above, is problematic.

In my proposal it's still a true singleton. The same exact object will be returned from both Internal.AutomationNull.Value and AutomationNull.Value, every single call. It will also still be a PSObject since AutomationNull would subclass PSObject.

The only possible break I can think of is if there is an obscure branch of code where obj == AutomationNull.Value is gated by an explicit type check like obj.GetType() == typeof(PSObject). For example:

// Would NOT catch auto null
if (obj.GetType() == typeof(PSObject))
{
    return obj == AutomationNull.Value;
}

// This would still work
if (obj is PSObject)
{
    return obj == AutomationNull.Value
}

If there are any instances of that (ideally there wouldn't be as PSObject is not sealed), they should be pretty easy to find by following references of AutomationNull.Value.

@mklement0
Copy link
Contributor

mklement0 commented Feb 21, 2020

Thanks for the clarification, @SeeminglyScience, I had missed the nuances of your proposal; so it sounds like we needn't worry about the proposed change being risky .

This leaves us with the question: Why do it?

make $anull -is [automationnull] work for the dozen of us that will use it once it year.

To me, it's not about how often the need arises, but about the need to provide a way to discover and make sense of a real-world behavioral difference that would otherwise be inexplicable without insider information (currently, the only high-profile source of this information that I'm aware of is this Stack Overflow question, where @PetSerAl has provided an in-depth answer).

As it stands, the conflation of $null and AutomationNull in terms of conditionals and reflection amounts to a leaky abstraction:
They are not the same and in the contexts described above whether you're dealing with one or the other makes a fundamental difference - yet nothing in PowerShell currently tells you which one you're dealing with.

Making the following all work (meaningfully) would be a great enhancement in my estimation:


Parting thought re:

PSObject is the magic wrapper that makes the language functional

Yes, but it's another leaky abstraction that occasionally peeks from behind the curtain to cause seemingly inexplicable behavioral differences: #5579

@bpayette
Copy link
Contributor

@vexx32

Point of clarity that seems to have been ever so slightly glossed over... you can't check $value -eq [automationnull]::Value in PowerShell and expect the right result; $null -eq [automationnull]::Value will always return $true

Correct and by design. The intent was to make AutomationNull as invisible to script users as possible.

So again - why? What significant scenarios depend on actively working with AutomationNull in script. Why would we take a significant risk to do this?
.

@vexx32
Copy link
Collaborator Author

vexx32 commented Feb 21, 2020

What risk? There isn't any significant risk whatsoever, unless you have some specific disagreements with @SeeminglyScience's assessments.

I think @mklement0 and prior points in this thread have addressed the why of it quite well enough. 🙂

@BrucePay
Copy link
Collaborator

@vexx32

What risk?

The significant risk of breaking all kinds of things in obscure ways because we've done something to make AutomationNull more visible rather than less visible. As I mentioned before, getting a usable system with AutomationNull took months of bug hunting.

And I have yet to hear a credible rational for changing the semanitcs of AutomationNull. In a value/expression content AutomationNull should work exactly like null. In a pipeline, it's purpose is to indicate that the pipeline returned no results which is not the same as null.

@vexx32
Copy link
Collaborator Author

vexx32 commented Feb 26, 2020

@BrucePay Sure, but it doesn't work "exactly" like null in all cases. Therefore having a reliable and accessible way to distinguish the two is desirable so that you can properly handle it when you need to.

Your arguments are hypotheticals that we "might" break something, with very little information . The responses from @mklement0 and @SeeminglyScience as well as the occasional question that crops up on a fairly frequent basis in the community chat channels indicates that having a reliable way to distinguish $null from AutomationNull is worth implementing.

Unless you can demonstrate something that the proposed changes will actually break, I'm not sure your concerns can be tested in any way. An untestable hypothesis isn't especially useful as a talking point.

@mklement0
Copy link
Contributor

To add to @vexx32' s helpful comment: No one in this thread has advocated changing the semantics of AutomationNull - the proposed change in no way modifies the existing behavior, it merely enables reflection on a preexisting behavioral difference that has hitherto been undiscoverable without insider knowledge.

Anecdotal references to past struggles and insisting on not being personally convinced are no substitutes for rational debate, especially if the points that have been made aren't being addressed.

@iSazonov
Copy link
Collaborator

We were sure that # 9794 is correct but it was reverted. This experience says that each application area can have its own 'null' and it is probably not worth crossing them as far as possible (even if it raises a lot of questions).

@mklement0
Copy link
Contributor

mklement0 commented Feb 27, 2020

@iSazonov, #9794 was a different story altogether, and in a sense the opposite (complement) of what is being proposed here: it tried to extend the set of types that are conflated with $null when compared it via -eq (in keeping with -eq's loose concept of equality); by contrast, here we're asking to unambiguously identify a "specific flavor of $null". Again, we're only talking about a reflection feature here that makes real-world behavioral differences discoverable - no existing behavior relating to using these values will change.

@iSazonov
Copy link
Collaborator

we're only talking about a reflection feature here that makes real-world behavioral differences discoverable - no existing behavior relating to using these values will change.

@vexx32 @mklement0 The issue description is based on #9794 and mentioned dbnull and stringnull. This is misleading. I think you need to close this discussion and create a new one where you clearly indicate the desired changes as 1, 2, 3.

Please strongly take into account a history which Bruce revealed.
Also please take into account that AutomationNull is "public" mainly for SDK (see C# vs PowerShell - ~3000 vs ~0 results)

@SeeminglyScience
Copy link
Collaborator

Please strongly take into account a history which Bruce revealed.

The history he revealed is that there were a lot of bugs when they made the change that allowed autonull to be saved to a variable. Nothing is revealed there, that's a huge change that pretty obviously increases risk.

It's sort of like saying that there was a lot of obscure bugs when PowerShell's parser switched from purely token based to AST based so we shouldn't consider adding an overload to Parser.ParseInput.

If the argument was "this comes up so infrequently that it's not even worth discussing" I'd be with ya 100%. If instead the argument is "significantly more drastic and involved changes we made 10 years ago caused problems" then I have a hard time seeing that as anything other than off topic.

@iSazonov
Copy link
Collaborator

I mean mainly the old history and experience:

AutomationNull is primarily designed for use in the pipeline to distinguish between the value $null and no value (the empty stream.) When a pipeline returns AutomationNull it is converted into a $null on variable assignment.

AutomationNull needed to be publicly visible for various reasons. For example, a compiled cmdlet might want to explicitly return it. (Though generally, if the cmdlet doesn't call write, the pipeline processor will automatically return AutiomationNull.)

Our new history and experience is that in #9794 we got married [dbnull], [stringnull] and [null] and then stumbled upon AutiomationNull that was highlighted in the separate discussion. In the history no real scenario presents. So question is - what scenarios do we need to fix to help (1) script writers, (2) binary module developers? what can they not do? what can they not overcome? What annoys them and forces them to make bad workarounds?
Although Bruce mentioned only one scenario where AutiomationNull might be used (binary cmdlet), perhaps there are others. I would rather expect that these scenarios were discussed with the aim of hiding or getting rid of AutiomationNull because it is the visibility of AutiomationNull (knowledge that it exists) that causes questions for users, although most likely they do not need it and they can bypass the emerging problem easily.

@SteveL-MSFT
Copy link
Member

@PowerShell/powershell-committee discussed this. We feel that using AutomationNull directly comes up infrequently enough that it doesn't warrant the time spent on discussion already. We would also be concerned about potential breaking change as noted by @SeeminglyScience and @bpayette.

@SteveL-MSFT SteveL-MSFT added Committee-Reviewed PS-Committee has reviewed this and made a decision Resolution-Answered The question is answered. and removed Review - Committee The PR/Issue needs a review from the PowerShell Committee labels Mar 4, 2020
@mklement0
Copy link
Contributor

mklement0 commented Mar 4, 2020

comes up infrequently enough

How often this comes up isn't the point, as argued before.

using AutomationNull directly

That wasn't the point either; it's not about using, but about discovering something that you can't help but encounter in real life, without the language giving you the ability to understand why not all apparent $null values are created equal.

Recent case in point: https://stackoverflow.com/q/60515757/45375

concerned about potential breaking change

What the proposal turned into would not result in any breaking change (as @SeeminglyScience has conclusively argued, aside from his not being personally convinced of the need for a change).


@iSazonov:

I think you need to close this discussion and create a new one

Re-reading the OP, I agree; unfortunately I didn't get around to it in time.

it is the visibility of AutomationNull (knowledge that it exists) that causes questions for users

The exact opposite is the case: most users are unaware of AutomationNull, but they see its effects, which is why they need to be given a way to discover this value.

with the aim of hiding or getting rid of AutomationNull

In light of the above: AutomationNull must neither be hidden nor can it be gotten rid of.

Stack Overflow
I am writing a Chef library to make writing a custom resource for managing Microsoft MSMQ resources on Windows Server easier. Chef interfaces with Windows using Powershell 5.1.

I want to raise an...

@mklement0
Copy link
Contributor

@iSazonov, I finally got around to this suggestions of yours:

I think you need to close this discussion and create a new one

Please see #13465, which focuses just on -is [AutomationNull] and hopefully avoids the confusion that arose in this discussion around non-existent breaking changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Committee-Reviewed PS-Committee has reviewed this and made a decision Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-Answered The question is answered.
Projects
None yet
Development

No branches or pull requests

7 participants