Powershell: Feature Idea: Revisiting and extending the ParameterSet Experience

Created on 27 May 2020  路  16Comments  路  Source: PowerShell/PowerShell

Revisiting ParameterSets

Summary

The current limitation to parametersets easily leads to an explosion of parametersets.
Let's say a command has 4 different ways to connect to something, with 3 different identity resolution options to achieve its ways in one of 4 different ways:

4 * 3 * 4 = 48

That currently ends up as 48 different parametersets. Any additional parameter that is legal for most (but not all) 48 sets would require an equal number of separate parameter attributes! As Kirk mentioned in a previous discussion on this, Invoke-Command is a default cmdlet with a great example of the curent complexity (though not quite as bad as the scenario drawn up above).

In total, I have three issues with the current system, that I'd like to address (and a proposed form for each of them):

  • Limited utility of DefaultParameterSetName in complex parameterset situations.
  • Parameter attribute duplication with exactly the same properties except ParameterSetName
  • Combination of multiple sets of mutually exclusive parameters exploding total set number

On top of all that, we also need to work on the consuming user's experience in help and syntax display, as simplifying the experience for developers is likely to lead to an increased use, turning the current display form into utter futility if the total available complexity increases any further.

So here are my ideas on how the final form could be made to look. Feedback, counters and counter proposals more than welcome :)

1) Improving the DefaultParameterSetName

The current system is limited by the fact that we can only have a single DefaultParameterSetName to help with parameterset resolution. An example parameter situation:

The command Get-Something has two primary parameters that are mutually exclusive. It also has two supplementary parameters which _may_ be used with either, but not together. Theoretically this results in 6 parameter sets:

Get-Something -Main1
Get-Something -Main1 -SubA
Get-Something -Main1 -SubB
Get-Something -Main2
Get-Something -Main2 -SubA
Get-Something -Main2 -SubB

With the current system, this cannot be expressed at all: Assuming the first path becomes the DefaultParameterSetName, the fourth option will automatically lead to an ambiguous parameterset (as all the last three options could still be possible).

This could be solved by making the attribute accept a string-array in descending order of relevance:

function Get-Something {
    [CmdletBinding(DefaultParameterSetName = 'Set1', 'Set2')]
    param (
        # Parameters go here
    )
}

With that, Set1 would be the default parameterset. If that set is no longer possible, switch to Set2 being the default set (and so on, if more are specified).

In conjunction with proposal 3), this could be extended to also support a hashtable instead, for different parameterset groups:

function Get-Something {
    [CmdletBinding(DefaultParameterSetName = @{
        Group1 = 'Set1'
        Group2 = 'SetA'
    })]
    param (
        # Parameters go here
    )
}

2) Reduce Parameter Attribute duplication

A parameter that can be part of multiple (but not all) parametersets requires an individual parameter attribute for each possible set. This increases parameter block size, causes more effort and reduces overall readability. Example:

function Get-Something {
    [CmdletBinding()]
    param (
        # Lots of other parameters

        [Parameter(ParameterSetName = 'Set1')]
        [Parameter(ParameterSetName = 'Set2')]
        [Parameter(ParameterSetName = 'Set3')]
        [Parameter(ParameterSetName = 'Set4')]
        [Parameter(ParameterSetName = 'Set5')]
        [Parameter(ParameterSetName = 'Set6')]
        [MyModule.Options]
        $Option
    )
}

I thus propose adding three new legal notations:

# Proposal 1: Array
function Get-Something {
    [CmdletBinding()]
    param (
        # Lots of other parameters

        [Parameter(ParameterSetName = 'Set1', 'Set2', 'Set3', 'Set4', 'Set5', 'Set6')]
        [MyModule.Options]
        $Option
    )
}

# Proposal 2: Wildcard
function Get-Something {
    [CmdletBinding()]
    param (
        # Lots of other parameters

        [Parameter(ParameterSetWildcard = 'Set*')]
        [MyModule.Options]
        $Option
    )
}

# Proposal 3: Regex
function Get-Something {
    [CmdletBinding()]
    param (
        # Lots of other parameters

        [Parameter(ParameterSetPattern = '^Set\d$')]
        [MyModule.Options]
        $Option
    )
}

3) Multiple Sets of Parametersets

Simply put, too often we have two different sets of parameters, that each within their group are mutually exclusive, but could be freely combined with any one of the options of the other group.

An example of how that could look:

function Get-Something {
    [CmdletBinding()]
    param (
        [Parameter(ParameterSetGroup = 'Group1', ParameterSetName = 'Set1')]
        $Param1,

        [Parameter(ParameterSetGroup = 'Group1', ParameterSetName = 'Set2')]
        $Param2,

        [Parameter(ParameterSetGroup = 'Group1', ParameterSetName = 'Set3')]
        $Param3,

        [Parameter(ParameterSetGroup = 'Group2', ParameterSetName = 'SetA')]
        $Param4,

        [Parameter(ParameterSetGroup = 'Group2', ParameterSetName = 'SetB')]
        $Param5,

        [Parameter(ParameterSetGroup = 'Group2', ParameterSetName = 'SetC')]
        $Param6,

        $Param7
    )
}

Adding a bit on top: What if another parameter is not actually supported for _all_ parameters of a given group?

function Get-Something {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ParameterSetGroup = 'Group1', ParameterSetName = 'Set1')]
        $Param1,

        [Parameter(Mandatory = $true, ParameterSetGroup = 'Group1', ParameterSetName = 'Set2')]
        $Param2,

        [Parameter(Mandatory = $true, ParameterSetGroup = 'Group1', ParameterSetName = 'Set3')]
        $Param3,

        [Parameter(Mandatory = $true, ParameterSetGroup = 'Group2', ParameterSetName = 'SetA')]
        $Param4,

        [Parameter(Mandatory = $true, ParameterSetGroup = 'Group2', ParameterSetName = 'SetB')]
        $Param5,

        [Parameter(Mandatory = $true, ParameterSetGroup = 'Group2', ParameterSetName = 'SetC')]
        $Param6,

        [Parameter(ParameterSetName = @{
            Group1 = 'Set1','Set2'
            Group2 = 'SetA'
        })]
        [switch]
        $Param7,

        [string]
        $Param8
    )
}

I am not sure I would be happy with commands that introduce this level of complexity to their parameterization, but it could at least still be handled that way.

4) Impact on the syntax notation

With all the proposals above, we would be enabling more complex parameterization situations while - I believe - improving the overall coder experience of defining them. However, we cannot forget the _user_ experience, trying to figure out how to actually use the bloody command!

Here too I'd like to borrow from Kirk's excellent post. With actual ParameterSetGroups, I believe it to be possible to implement his proposed new form of syntax notation:

SYNTAX
    Get-Something {<Group1>} {<Group2>} [-Param8 <String>] [<CommonParameters>]

    {<Group1>}
    Set1: -Param1 <Object> [-Param7 / {<SetA>}]
    Set2: -Param2 <Object> [-Param7 / {<SetA>}]
    Set3: -Param3 <Object>

    {<Group2>}
    SetA: -Param4 <Object> [-Param7 / {<Set1> | <Set2>}]
    SetB: -Param5 <Object>
    SetC: -Param6 <Object>

Concluding

I really hope we can improve the parameterset situation and I believe this to be a step in that direction. Any opinions / counter proposals / further enhancements?

Cheers,
Fred

Issue-Enhancement WG-Engine

Most helpful comment

I dunno. That kinda feels a bit much like dynamicparam-lite. Part of why I _don't_ like dynamicparam is it's a much less declarative syntax, and it's much harder to tell at a glance what's going on as a result. 馃槙

All 16 comments

I like the ideas here, for the most part; I'm not sold on the groups / partial groups thing, I think that may be overcomplicating things a fair bit.

I also think the syntax should be much simpler, if possible. Reducing lines & repetition is great, but if we end up with lines > 1 screen wide a lot of folks will just end up splitting it up into more lines again.

Also... if we're redefining this and adding new names/syntaxes/etc I'd _much_ prefer the Parameter prefix on every property name to go away. We already have the clear context that this is in a [parameter] attribute -- I don't think we also need to specify that it's a ParameterSetName, it's (IMO) plenty clear to call it SetName or even just Set in that context -- CmdletBinding should still use DefaultParameterSet (drop the Name, _please_, it's so long!) for clarity.

function Some-Function {
    [CmdletBinding(DefaultParameterSet = 'DefaultSet')]
    param(  
    [Parameter(Mandatory, Group = 'Group1', SetName = 'Set1')]
    $Param1,

    [Parameter(Mandatory, Group = 'Group1', SetName = 'Set2')]
    $Param2,

    [Parameter(Mandatory, Group = 'Group1', SetName = 'Set3')]
    $Param3,

    [Parameter(Mandatory, Group = 'Group2', SetName = 'SetA')]
    $Param4,

    [Parameter(Mandatory, Group = 'Group2', SetName = 'SetB')]
    $Param5,

    [Parameter(Mandatory, Group = 'Group2', SetName = 'SetC')]
    $Param6,
    )
    ...
}

Hmm. Still not sold on the groups. Set and Group seem fairly interchangeable and prone to confusion tbh.

Also, allowing mutliple default parameter sets would need different syntax I think? [CmdletBinding(DefaultParameterSet = 'SetA', 'SetB')] is ambiguous; we'd need at least [CmdletBinding(DefaultParameterSet = @( 'SetA', 'SetB') )] to be able to be clear about which values belong to which property. There may be better alternatives 馃檪

Multiple default parameter sets are NOT default at all logically. Default result is logically single one. Multiple end-result implies condition. Dead end idea.

I agree with the premise there @scriptingstudio but I don't agree with your conclusion. Yes, having "multiple defaults" is a bit of a contradiction, but the idea comes from the currently-unworkable but still desirable configuration @FriedrichWeinmann mentioned in his initial description.

One way or another we need a way to handle that appropriately, whether that's just making the binder much smarter in those circumstances, or having an explicit way to specify fallback "defaults" for those slightly more ambiguous cases.

Anything we can do to make the management of parameter sets easier I'm all for. The "main" function on BurntToast is sitting at eight sets, and that's after trimming them back. I dread, and am very picky, about adding new functionality because of potentially breaking things or the number of sets multiplying.

There was a discussion on native support for mutually exclusive parameters a few years back started by @markekraus. This idea would suite my needs, as one of my key drivers is keeping "sound" options away from the "silent" options.

As for this idea, I like everything under heading # 2. Just being able to supple multiple sets under one attribute would simplify things dramatically.

Indifferent on heading # 1. I can see and support the problem it solves, but haven't personally run into it to feel personally invested in the fix 馃槢

Not sure if I'm sold on the Groups/Sets concept... just not getting my head around it (might just need a more "real world" example.)

We do need a better way of displaying the sets to end users. I wonder if we can use colour to help with this? Say all the mandatory parameters are a certain colour, the parameters that are in all sets are another? I haven't fully thought this through yet...

@Windos for that last point, we have #8692 tracking that, but afaik little or no work has been done in that area either. Definitely related here, though; we need a way to display whatever we come up with clearly to users.

What about a slightly more mixed approach.

Implement a parametersets code block construct similar to what's done for dynamic parameters, in that it comes after the param block. Define one or more groups within the block as string arrays. To make it even easier, set it up so that any parameters not included in any groups are automatically included available in all groups.

To accommodate slightly more complex scenarios, you could add a keyword option for the group definitions to account for specialized functions like 'set', 'exclusivity', and 'allsets'. 'Set' is a normal parameter set, while the second can be used to specify one or more collections of mutually exclusive parameters. This would allow for multiple parameters to all be included in one or mote sets, but use of any single one in an exclusion group blocks use of others within the same exclusion set. The 'allsets' type would be a special optimism type that could only be used once, and would serve to intentionally include specific parameters in all sets. If used, then PowerShell would generate an error in the event not all parameters were accounted for in at least one set group.

This approach would make it easy for devs and also for Parsing the help for users using the proposed augments to format as well. The only change I would propose here would be a simple indicator to mark a parameter as being part of an exclusivity set for a given parameter group.

Hm, I must admit I have my doubts about that improving the overall readability for me.
Another complicating factor is that all the parameter attribute values can be specific to a given parameterset (for example accepting from pipeline or mandatory). It would admittedly be more flexible, but by the time we add in those other attributes, getting a coherent picture of the parameter when we need to look at both the parameter block as well as the ParameterSet section ... I don't really see that working out.
Mind doing a mock-up of what you had in mind?

Another thing to keep in mind is the cost to parameter binding performance, which I definitely would be concerned with, though I'm sure that would be a solvable issue.

I dunno. That kinda feels a bit much like dynamicparam-lite. Part of why I _don't_ like dynamicparam is it's a much less declarative syntax, and it's much harder to tell at a glance what's going on as a result. 馃槙

I'll see about getting it mocked up once in at a real compute.
As far as the first issue you're聽describing, there's nothing saying you couldn't use an optional聽value pair instead of just strings to indicate if a parameter is mandatory for a particular set.
As for the resolution speed issue, it would seem to me that this is already built into the shell to a degree...match in sequence of definition,with the first viable聽match taking precedence. You could likely even leverage the mandatory indicator to narrow down the possible sets in the absence of sufficient other identifiers to indicate the target set.聽For really complex mixed scenarios, perhaps add a second keyword that could also be used only once to indicate a set as the default...or perhaps a set order keyword (similar to the positional聽one for parameters) to give control over the order, though i think it better to just use the definition order...the other seems like it would only add confusion.
Admittedly I'm a bit lacking in 'true' programming expertise for the last part...I've been scripting since the batch file days, and have been using PowerShell since Monad,聽with lots of聽experience writing my own modules as well, but no real .NET or other 'real' programming knowledge outside of using a dll or consuming an API.

Ok, finally made it to an actual computer. What's in my head is something like the below example. Processing would, of course, run from top to bottom, stopping at the first full match. Obviously if you end up with 50 different Sets you might run into some speed challenges, but to my non-programmer eyes at least, the below is easier to read down than the current approach, and I would personally find it easier than trying to shove everything into the param block. There's still some repetition (I don't know that you could really avoid that), but it's more logically organized.

function Get-Something {
    [CmdletBinding()]
    param (
        [Parameter()]
        $Param1,
        [Parameter()]
        $Param2,
        [Parameter()]
        $Param3,
        [Parameter()]
        $Param4,
        [Parameter()]
        $Param5,
        [Parameter()]
        $Param6
    )

    ParamSetGroup {
        [ParamSet(Default=$true)]
        $Set1 = {Param1, Param4, Param6},

        [ParamSet()]
        $Set2 = {Param4, Param5},

        [ParamSet()]
        $Set3 = {
            (Param1, Mandatory=$true),
            (Param4),
            (Param6)
        },

        [ParamExclusivity()]
        $Set4 = {Param4, Param6},

        [ParamAllSets()]
        $Set5 = {Param2, Param3}
    }
        ....
}

The net result of the above would be as follows:

  • Specify param2 and/or param3 = $Set1 is selected
  • Specify jusst param1 = $Set3 is selected (because of the mandatory)
  • Specify param1 and param4 = $Set1 is selected
  • Specify param1, param6, and param4 (in that order) = $Set3 is selected, but error is thrown for param4

The one scenario the above doesn't necessarily account for would be the need for mutual exclusivity specific to a single 'set' definition, but my though here is perhaps to use something like a 'Group' keyword in the ParamExclusivity definition to allow targeting - [ParamExclusivity(Group=$Set1)]

Hate to disagree, but ... I don't consider that more intuitive to grasp than my approach. Also doesn't really cover the multiple groups of sets part of my proposal - adding that would not be too hard, but make the whole thing yet more complicated.

Here's the same param block using my proposal with @vexx32 's updated notation:

function Get-Something {
    [CmdletBinding()]
    param (
        [Parameter(Set = 'Set1')]
        [Parameter(Set = 'Set3', Mandatory)]
        $Param1,
        $Param2,
        $Param3,
        $Param4,
        [Parameter(Set = 'Set2')]
        $Param5,
        [Parameter(Set = @('Set1', 'Set3'))]
        $Param6
    )
}

Perhaps this one comes down more to mindset I guess, and I'm certainly not trying to imply that your approach聽is bad, or that聽mine is superior.聽For me personally, defining everything at the parameter level,聽even using the current approach, has聽proven challenging to master because it's too muddled together.I聽always end up missing something,聽or causing PowerShell to throw errors because it couldn't properly determine the intended set definitively. I always聽end up having to either model it outside of PowerShell and translate it back in, or scrapping the use of聽sets completely.
By separating it out to is own construct, I can build my parameters first, then organize them into logical groups in what (again, to me personally) is a visually and logically consistent manner. I build each set all at once for a particular usage pattern, can specify in line if聽the parameter is mandatory for that specific set, and have a simple mechanism for setting exclusivity globally or at the set level...though with a simpler nomenclature than the dynamic parameters that gave聽me the idea. With your approach, while simplified language wise, it聽actually decreases usability for me personally by putting more elements into the same space, making it harder for me to ensure I account for everything.

As for the multiple groups of sets, I guess I'm unclear on that one....I think it wouldn't be overly difficult to accommodate however if you use nested hash tables, or add another type to hold a collection聽in,聽similar in manner to dynamic parameters....like a dictionary or something, but聽more naively accessible.

Better still perhaps might be to incorporate something like my approach as an underlying framework that powers your approach, then supporting both to appeal to more variability in mind set. For those that can process everything all together, it provides for shortee overall code structures, while for people like me, it provides a different logical grouping approach with the same net result.

To be clear here, I fully聽agree that the current approach is both聽confusing and far too limited, and I can absolutely聽see the merits of your approach. That said, our discussion here seems to clearly indicate a need to have something that doesn't necessarily聽try to be one size fits all...which makes sense really...that's what makes PowerShell so powerful...it's ability to appeal to multiple mind sets by offering multiple approaches to problems.

. An example parameter situation:

The command Get-Something has two primary parameters that are mutually exclusive. It also has two supplementary parameters which _may_ be used with either, but not together. Theoretically this results in 6 parameter sets:

Get-Something -Main1
Get-Something -Main1 -SubA
Get-Something -Main1 -SubB
Get-Something -Main2
Get-Something -Main2 -SubA
Get-Something -Main2 -SubB

With the current system, this cannot be expressed at all: Assuming the first path becomes the DefaultParameterSetName, the fourth option will automatically lead to an ambiguous parameterset (as all the last three options could still be possible).

Not quite,

  • main1 is mandatory in sets 1,2 & 3
  • main2 is mandatory in sets 4,5 & 6.
  • subA is mandatory in 2 & 5,
  • subB is mandatory in 3 and 6.

This causes a complication when things are piped in but it's solvable.

That aside there is a lot of sense here. Especially with the problem of multiplication, if every parameter ends up being prefixed with multiple lines saying which set it is in and where it is mandatory the parameters themselves shrink into a sea of other information. Help becomes horrible because each set is listed.

A way of saying "Allow one of these" without having to double or treble or quadruple the number of sets would solve many , maybe most, problems in one go

I dunno. That kinda feels a bit much like dynamicparam-lite. Part of why I _don't_ like dynamicparam is it's a much less declarative syntax, and it's much harder to tell at a glance what's going on as a result. 馃槙

Dyamic parameters cause at least as many problems as they solve. Having everything relating the parameter with the parameter means even if you can't see at once what is going on, it's in one place.
But when you say
Parameter A is in Set 1,3, 5 and 7
Parameter B is in Set 2,3, 6 and 7
Parameter C is in Set 4,5, 6 and 7

What's in any given set isn't obvious (which is what we have now)
If you say
Set 1 is A, 2 is B, 3 is A and B, 4 is C, 5 is A and C, 6 is B and C and 7 is A, B and C
What sets B is in needs a long hard look.

Hmm. Still not sold on the groups. Set and Group seem fairly interchangeable and prone to confusion tbh.

I think we have this X-Y product thing going on.; but I'm having a problem visualizing multiple axes.
In importExcel we have some issues Import-Excel has 6 sets and Set-ExcelRange which applies formatting does use any because of the trouble in trying to support all that would be needed
It has borderAround, which excludes Borderleft, borderRight etc. It originally just had switches for [add] bold, italic, underline, strikethru, subscript, superscript
So you have size, and type face which go in all sets, including the unnamed default
borderaround, and each of the other borders which divide things into two sets; subscript , superscript (and neither), So that's six sets. When someone said they wanted remove-border remove-bold, etc we went for the unconventional -bold:$false because -bold -unbold and neither would allowing -noborder -bold, -borderbottom -Unbold, -borderAround -italic -unStrikethru ... with the appropriate options blocked means over 100 sets.

I think @merddyin has got closer to solving this problem, because currently sets serve the need of saying "These combinations cannot be used together" by specifying what can. That's great if the choice is _either_ (A and/or,B and/or C) _or_ (D, and/or E and/or F) not for (A or B) and/or (C or D) and / or (E or F)

Expressing the latter needs a new syntax which may need nesting
something like

param (
$FontSize. # in all sets
[exclusive]( param([switch]$bold) , 
                  param([switch]$notbold) ,
)
exclusive( param([switch]$BorderAround) , 
                param([switch]$borderTop, switch]$borderleft, <<etc>> ) 
                param([switch]$No Border ) 
)

with nesting it can do things like border weight but I don't think the syntax can be called nice.

exclusive(   
    param ( 
        exclusive( 
            param([switch]$BorderAround) , 
            param([switch]$borderTop, switch]$borderleft, <<etc>> ) 
         )
         param ( $BorderWeight )
    )
   param([switch]$No Border ) 
)

[parameter()] inside these blocks would say if one member is present another is mandatory and also say that NoBold and noBorder come at the end of tab completion so users don't need to tab through on/off for options they don't want to set.

I don't think there is a single new syntax which will please everyone, and if there is to be change the first thing to do might be to identify the cases which give the most frequent problems in practice (the above seems is a real problem for _me_, but how common is it?) there are likely to be cases which can't be done but no-one wants.

It's also worth saying that accessing other parameters in validation is not easy and two validation attributes could solve the problem

 [NotWith(Bold)}
[Switch]$NoBold

[NotWith(NoBorder,BorderLeft,BorderRight<etc>)}
$BorderAround

[With(BorderAround,BorderLeft,BorderRight<etc>)]
$BorderWeight

Note that tab completion would need to look at the with / notWith attributes of parameters, so borderweight only becomes an option if border around or left or right is specified. I can imagine how one says say _some_ border is mandatory - but there are 17 allowable combinations - but I would hope never to see it coded !

Interesting idea/concern.
I suppose some of this concern could be mitigated in the original concept by making an entire parameterset-group optional if no parameter in it is defined as mandatory.

Alternatively, I suppose we could also add Exclude and Require options in the parameter attribute:

[Parameter(Group = 'Group1', Set = 'Set1'; Exclude = 'Param2', Require = 'Param4')]
$Param1

Not sure I'd be a fan of the added complexity though, especially when it comes to documenting the syntax to user.

we could also add Exclude and Require options in the parameter attribute:

[Parameter(Group = 'Group1', Set = 'Set1'; Exclude = 'Param2', Require = 'Param4')]
$Param1

Not sure I'd be a fan of the added complexity though, especially when it comes to documenting the syntax to user.

I've thought more about this since making the post and something ("with" / "notwith", "exclude/require") in the parameter attribute does feel to me more and more like the right way.

Try doing get-help where to see how the existing syntax goes wrong. Because -gt -match -notlike and all the others can't be used together each has it's own set - 32 of them! More than a screenful of sets before the help. add -Full and look at the detail under, say, NotMatch and NotLike. It says BOTH are required ... they are required _in their own mutually exclusive parameter sets_ but the help doesn't give you that info _with the parameter._
Now imagine that help showing one parameter set and under the parameter in the help it said "Can't be used with: -eq -ge -gt ...."
Explaining to end users gets _way_ simpler (although where-object is an unusual case) and to programmers who currently have to grasp sets its also simpler. In both cases it does what @vexx32 said above (I think it was) keep everything about the parameter in one place. The information which says -NotLike and -NotMatch are not required _at the same time_ is 500 lines back up the help from the suggestion that they are.

It needs more thought, and some more input would certainly be good, as I say, I'm starting to think this is a good solution to a lot of the cases.

It is useful to look a conclusion for old @SteveL-MSFT's RFC https://github.com/PowerShell/PowerShell-RFC/issues/2#issuecomment-248972027

Thanks for all the feedback. At this time, I'm going to withdraw this RFC. The feedback makes it clear that the current proposal doesn't completely solve the problem without introducing new problems in readability. Any RFC for this issue should also (as @KirkMunro pointed out) include addressing making help more readable as generating many parameter sets makes it much less readable for end users.

A main point here - User Experience

_Obviously enhancements of existing attributes or introducing new attributes add new complicity and impairs readability and maintainability._

We should consider these attributes as implementation details and hide the complicity in PowerShell Engine/compiler.
Why?
Let's look how we create new cmdlet/adv. function: define the cmdlet name, output type(s), define parameters and their input value validations, then define parameter dependencies.
Only last is in a scope of the discussion. How do we define parameter dependencies? First, we create a syntax diagram on paper or in the head, then _manually_ transform/"compile" the diagram to ValidationSet attributes with some names, and at the end, we run Get-Command -Syntax to generate a syntax diagram to check results.

_So starting and ending points is the same __syntax diagram___. Can anybody propose anything more user-friendly than syntax diagram? We can't think of anything else. (If we look the related issue #13746 there @iRon7 is trying to resolve the problem and he has started with syntax diagram too.)

This immediately raises questions:

  • why don't we use the diagram right away?
  • why do we do this transformation manually if, following simple formal rules, we can entrust it to a computer?

__Thus the solution to this problem is to prompt the user to create a syntax diagram.__
This gives us:

  • best UX
  • great flexibility
  • hide complicity in Engine
  • full backward compatibility

Roadmap is:

  • Design a notation for the parameter dependencies syntax diagram in a RFC
    (we could start with @KirkMunro's proposal mentioned above in the post)
  • Create parser/compiler for the notation
    (we get very flexible Ast tree )
  • Create new attribute to assign the syntax diagram to a cmdlet
    (we get great UX)
  • Enhance existing attributes (like ParameterSet) and add new ones so that implement parameter dependencies any complicity (which is hided from uses in Engine).
Was this page helpful?
0 / 5 - 0 ratings