Repository navigation
Description
Problem
Parameter sets are great for grouping parameters into sets that work together. This can, to some degree, also be used to make parameters exclusive. The problem arises when you have mutually exclusive parameters that need to exist in all parameter sets. The current implementation would require creating additional parameter sets to accommodate the actual parameter sets and the mutually exclusive parameters.
For very simplistic demonstration, lets say you have a New-Widget:
function New-Widget {
[CmdletBinding()]
param (
[Parameter(ParameterSetName="Size")]
[int]$Height,
[Parameter(ParameterSetName="Size")]
[int]$Width,
[Parameter(ParameterSetName="Size")]
[int]$Length,
[Parameter(ParameterSetName="Color")]
[int]$Red,
[Parameter(ParameterSetName="Color")]
[int]$Green,
[Parameter(ParameterSetName="Color")]
[int]$Blue
)
}Widgets can be defined either by color or size. Now, let say that all widgets, regardless of begin defined by size or color, can either have a flavor or a sound but not both. Currently, to accommodate this you would need to do the following:
function New-Widget {
[CmdletBinding()]
param (
[Parameter(ParameterSetName="SizeFlavor")]
[Parameter(ParameterSetName="SizeSound")]
[int]$Height,
[Parameter(ParameterSetName="SizeFlavor")]
[Parameter(ParameterSetName="SizeSound")]
[int]$Width,
[Parameter(ParameterSetName="SizeSound")]
[Parameter(ParameterSetName="SizeFlavor")]
[int]$Length,
[Parameter(ParameterSetName="ColorFlavor")]
[Parameter(ParameterSetName="ColorSound")]
[int]$Red,
[Parameter(ParameterSetName="ColorSound")]
[Parameter(ParameterSetName="ColorFlavor")]
[int]$Green,
[Parameter(ParameterSetName="ColorSound")]
[Parameter(ParameterSetName="ColorFlavor")]
[int]$Blue,
[Parameter(ParameterSetName="SizeFlavor")]
[Parameter(ParameterSetName="ColorFlavor")]
[string]$Flavor,
[Parameter(ParameterSetName="SizeSound")]
[Parameter(ParameterSetName="ColorSound")]
[string]$Sound
)
}This can get wildly out of hand if you have a third or fourth set of mutually exclusive parameters. This becomes difficult to maintain and to add additional parameters. Another option is to have logic in the body of the function to check for mutually exclusive Parameters at the cost of the mutual exclusion not being as discoverable.
In some instances you may be able to refactor into additional cmdlets or make use of custom types. But that doesn't always work for command line utilities meant to ease user burden at the cost of code complexity.
Proposal
I propose that parameters should support mutual exclusion natively. I don't have a solid implementation plan, but one idea would be to add a Parameter Attribute property like ParameterMutexName. Using the example above it would work like this:
function New-Widget {
[CmdletBinding()]
param (
[Parameter(ParameterSetName="Size")]
[int]$Height,
[Parameter(ParameterSetName="Size")]
[int]$Width,
[Parameter(ParameterSetName="Size")]
[int]$Length,
[Parameter(ParameterSetName="Color")]
[int]$Red,
[Parameter(ParameterSetName="Color")]
[int]$Green,
[Parameter(ParameterSetName="Color")]
[int]$Blue,
[Parameter(ParameterSetName="Size", ParameterMutexName="FlavorOrSound")]
[Parameter(ParameterSetName="Color", ParameterMutexName="FlavorOrSound")]
[string]$Flavor,
[Parameter(ParameterSetName="Size", ParameterMutexName="FlavorOrSound")]
[Parameter(ParameterSetName="Color", ParameterMutexName="FlavorOrSound")]
[string]$Sound
)
}Parameter binding logic would then check if more than one parameter is supplied in a ParameterMutexName and throw. This is flexible enough to accommodate parameters being exclusive in one set while not exclusive in other sets. This could also accommodate multiple Mutex sets within a parameter set.
Activity
SteveL-MSFT commentedon Oct 19, 2017
Mark Kraus (@markekraus) you should take a look at an old draft of an RFC as a proposal to solve this https://github.com/PowerShell/PowerShell-RFC/blob/master/X-Withdrawn/RFC0001-Mutual-Exclusion-Parameters-and-Properties.md
markekraus commentedon Oct 20, 2017
Steve Lee (@SteveL-MSFT) Interesting. When I was thinking about this my first thought was to add an attribute like what is in that RFC but I immediately realized that it makes things more painful. That's when I thought to add the parameter attribute property.
Would a solution to this need to work for both classes and parameters? I'm not sure doing something like that is really the correct approach for class properties. It just feels wrong. Was something like what I suggest here ever discussed? It doesn't look like it from the RFC's history, but maybe an offline discussion took place.
SteveL-MSFT commentedon Oct 20, 2017
I don't think your specific proposal came up when we original thought about this. I think the big challenge was having something that was easy to author and also easy to read (once authored) particularly when cmdlets get extended and more parameters get added (like what you experienced with the web cmdlets).
iSazonov commentedon Oct 20, 2017
I thought about this too. It is not difficult to implement mutual exclusion parameters on top level but not for subparameters - definitions with attributers becomes a mystery.
In fact, we must define a graph. We can do it in a variety of ways, (even with Json and Yaml).
markekraus commentedon Oct 20, 2017
Steve Lee (@SteveL-MSFT) Any thoughts on the readability and ease of coding for what I proposed?
Ilya (@iSazonov) Can you expand on that? I'm not sure I understand.
Could you psuedo code an example of what that would look like?
iSazonov commentedon Oct 20, 2017
Note the
Param221.SteveL-MSFT commentedon Oct 20, 2017
Mark Kraus (@markekraus) it seems it would be just as complicated with existing parameter sets once you get to several [ParameterMutexName] attributes.
Personally, when you start having a large number of attributes that scroll off the screen, it makes it hard to mentally map all the sets and exclusions. So I think I prefer something at the [CmdletBinding] level to declare the exclusions and maybe that new thing is mutually exclusive to parametersets.
If we use
Invoke-RestMethodas the canonical example, today if you get the help syntax:It's already hard to figure out what to do (and this doesn't include the new capabilities Mark Kraus (@markekraus) is adding). Thinking out loud here. The current syntax should look more like:
And if we simplify it a bit and not including some of the params to keep it smaller:
and we turn this into some sort of declarative syntax:
So how to put this into code? (has to work for both C# and script)
markekraus commentedon Oct 20, 2017
It's almost like the entire parameter system would need to be redone or at least a new one added in parallel with the existing one. Part of the problem is that the existing system is too cumbersome. but completely breaking it seems almost a like non-starter. hmmm
iSazonov commentedon Oct 21, 2017
We could borrow method overloads idea from C#. In this case, we only need to find the appropriate entry point into the cmdlet from several.
markekraus commentedon Oct 21, 2017
Ilya (@iSazonov) I'm not sure how you apply the same logic to exclusive parameters of the same type with different names. I have hard time visualizing this as being less work for the user. You'd need to basically define each unique binding signature. If you have 3 parameter sets and 3 exclusive sets, that a lot unique signatures to define. But maybe I don't have a good idea of what you are proposing. What is some mock code how how that would look from a user perspective?
lzybkr commentedon Oct 21, 2017
Parameter sets already are the equivalent of C# overloads.
Some random thoughts:
A not fully formed idea - instead of specifying mutually exclusive parameter, how about expressing compatible parameter sets?
So this says:
If we assume this is transitive, then C is compatible with both A and B, and the same for D.
This gives us a syntax like:
cmd [-A | -B] [-C | -D]In terms of the implementation - after binding is finished, it's possible we won't have a single parameter set, but that's OK because we now know which parameter sets are compatible.
I wonder if this idea helps reduce the need for a large number of
[Parameter]attributes.markekraus commentedon Oct 21, 2017
Jason Shirk (@lzybkr) I'm trying to wrap my brain around how to use that in the instance where you have, say, 2 governing parameter sets, but then you have 2 sets of mutex parameters
There is set A and set B. Set A includes normal parameters A1 and A2. Set B includes normal parameters B1 and B2. There is a C and D which are mutex but belong to both A and B sets. There is also E and F which are mutex and can belobn to both A and B sets.
the syntax is something like
With my equally not-fully-formed proposal, it would look like this:
markekraus commentedon Oct 21, 2017
If only this were possible:
7 remaining items
markekraus commentedon Oct 22, 2017
Forgive me... I have no clue what that is (reminder that I'm now only 8 months into C# 😉 ).
Sure they can. Just try using your syntax for the web cmdlets.. You will likely have well over 100 definitions. You are definitely going to miss one. I have a pretty deep understanding of these cmdlets and I'm not confident I wouldn't miss one.
iSazonov commentedon Oct 22, 2017
This problem exists for any new syntax. Any optimization found for one will apply to the other. :-)
Thanks for Yaml sample.
markekraus commentedon Oct 22, 2017
Ilya (@iSazonov) It doesn't have to. The JSON and YAML options pull of that level of complexity with way less code.
lzybkr commentedon Oct 23, 2017
I'm really liking the "PowerShell figures it out from your syntax" approach - and my primary reasoning - it should be an inverse function - meaning we might have a verbose way to specify the same with attributes, but nobody would use that going forward.
iSazonov commentedon Oct 24, 2017
Original C# overloads look cumbersome.
After we remove types it looks simpler.:
For example above signatures will be ('+' - optional parameter):
For Add-Member cmdlet ($PassThru we can exclude at all):
markekraus commentedon Oct 24, 2017
To add to the problems... currently ValueFromPipeline, ValueFromPipelineByPropertyName, DontShow, etc can be configured per parameter set how do we jive that with any of the new syntax proposals? more sigils? do we not?
iSazonov commentedon Oct 24, 2017
For general solution we should allow set attributes in parametersets. This allows us to use the exact type to InputObject.
SteveL-MSFT commentedon Nov 19, 2017
Related to #5435, we should provide the user with more specific error message about why they couldn't use two or more parameters together because they are mutually exclusive (and additionally a detailed error if insufficient parameters are provided to resolve a parameter set)
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 23, 2023
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.