Although we have several ways to create cleaner PS code when creating objects, there still isn't support for actually specifying constructor parameter names, or any .NET methods' parameter names. Hashtable object construction only works if the object has an empty public constructor as well as public settable properties for the properties you want to set.
So, we still can't do this:
[System.Security.AccessControl.FileSystemAccessRule]@{
IdentityReference = 'User'
FileSystemRights = 'FullControl'
Type = 'Allow'
}
Sure, three constructor properties? Meh, okay, it's clear enough. But with the five that that class sometimes needs (and it's far from alone, in the sea of complex .NET constructors) it gets very messy, no matter how you slice it.
In C# you can do something like this instead:
accessRule = new FileSystemAccessRule(
IdentityReference: 'User',
FileSystemRights: 'FullControl',
Type: 'Allow'
);
Which is unfortunately not made available by the PS parsing logic. This could be changed. After all, there is already some _similar_ support available for attributes:
[Parameter(ValueFromPipeline = $true, Position = 1)]
This doesn't seem to be quite the same thing (looks like it borrows the hashtable conversion logic or something?), but there's no reason this couldn't be extended into some slightly different syntax for either New-Object
or the exposed ::new()
constructors available for classes:
$AccessRule = [System.Security.AccessControl.FileSystemAccessRule]::new(
IdentityReference = 'User',
FileSystemRights = 'FullControl',
InheritanceFlags = 'ObjectInherit,ContainerInherit',
PropagationFlags = 'None',
Type = 'Allow'
)
Or, even better would be permitting the hashtable 'construction' option to actually dig into the class constructors in order to apply its key/value pairs to the appropriate constructor parameters! This would allow using the hashtable construction for basically any .NET class that has a public constructor.
I don't (yet) know enough C# to look at how/where to do this. /u/SeeminglyScience (sorry, don't know your github username off the top of my head!) pointed me here:
So I have some starting points. If y'all have any pointers, I'd appreciate them. This'll be the first C# project I'll be looking at messing with in a long time.
Oh, and if you think I shouldn't do this for some reason... yeah, let me know. I can't think of any reason we wouldn't want to do this. I think there's also no reason not to allow all .NET method calls to provide the explicit parameter names as well; provided the syntax can be constructed in a relatively useful way (e.g., the current ways that attributes are built with named properties) then it should make working with .NET methods from PowerShell considerably cleaner and clearer.
This is a good candidate for inclusion with changes to classes mentioned in #6418
I think we should get an approvement from PowerShell Committee before start implementing the enhancement.
I'm afraid that there may be a lot of changes in the binder to take into account named parameters.
shrugs
Just because it is difficult is no reason to avoid doing it. I barely even know the codebase, but I'm sure I could figure it out with a bit of help. The linked sections are a solid start, and I can work from there.
If anyone is already familiar with some of the code areas, I'm sure it could get done much quicker and cleaner than a first attempt from me, but there's little to really stop this working, in my opinion.
I'm not trying to dissuade you :-) - I welcome your desire to develop PowerShell Core. My comment is aimed to help you get a better understanding.
Believe me, I've been looking at the code trying to figure out a good way to do it. 馃槃
I haven't got there myself yet, but I'm certain it's doable without breaking much. Just gotta figure out how. 馃榿
@PowerShell/powershell-committee reviewed this. Agree that since the parameter binder already has this capability, it makes sense to generalize it as PowerShell users typically are comfortable with hashtables. Committee approves of this proposal.
I've been looking into this with @SeeminglyScience in terms of the possible methods of implementation, and we've found a couple possibilities -- but each seems to have their drawbacks.
NamedAttributeArgumentAst
This is somewhat problematic, as it would introduce a new AST class and thus break compatibility with PS Standard.
NamedAttributeArgumentAst
for more generic use casesAgain, kind of problematic for the above reason. I do think that this might end up being the most tidy method, however.
This would (probably) mean the tokenizer would need to not parse the name of the parameter as a generic/command token, and something like a ReadOnlyCollection<Tuple<int, string>>
would need to be passed around with the List<ExpressionAst>
for the regular method arguments.
This might have the least impact, but requires a fair bit of special casing and would be completely inconsistent with the nearest-comparable syntax PS has currently, i.e., named attribute arguments -- although these aren't really method arguments per se.
This is also problematic. C# permits method signatures that differ simply by the order of the parameters. As a result, the hashtable construction would need to mimic [PSCustomObject]
's handling of retaining the order of parameters, and there are a good many edge cases that would require complex logic to implement.
Additionally, it would almost certainly be impossible to set object properties as well, due to the fact that constructor argument names and actual object property names may collide or differ unexpectedly. If it were permitted to take constructor arguments it would have to be a solid either that _or_ setting properties as it does currently.
So, the latter is probably not the best idea to work with.
I would appreciate any thoughts you guys have on this matter; I think these suggestions are all doable, to varying degrees of complexity, but I would appreciate your input on whether there is another possibility that I haven't come across as yet, and/or what the best approach may be here. 馃槃
See my proposed syntax which relies on generalized splatting
With my proposed syntax, you would introduce a new kind of expression, say SplattedExpressionAst
that has a child expression which can be any expression.
The tokenizer would return the @
token to the parser when the next character is not {
or a character that can start a variable name. The parser would then treat that token as a unary operator, parsing it's child expression. This helpfully avoids touching the argument parsing code.
I think what I've described above is straightforward.
Once you can parse - the fun begins. Updating the binders to support named arguments will require some pretty deep understanding of how dynamic binding works. I'm happy to mentor whoever takes this on - ping me on Gitter when you're ready.
So essentially you PeekChar()
and if you get anything other than {
, (
, or a variable name character you scan for ExpressionAst
and compile it into a SplattedExpressionAst
I... Think I can probably manage that. I'll found out soon. :)
@SteveL-MSFT Please confirm that we can begin implementing the generalized splatting RFC
@iSazonov in my opinion, you (or @vexx32, or maybe even @lzybkr) need to move the RFC forward through _the process_ by addressing the comments...
I'll take a thorough read-through of the full RFC and comments tomorrow. That one's been sitting there long enough; time to either make it work or move it aside.
To give an update on this, @PowerShell/powershell-committee discussed the generalized splatting RFC and @joeyaiello submitted updates to the RFC https://github.com/PowerShell/PowerShell-RFC/pull/154/files
Removing Review - Committee
. Further feedback should be in the RFC.
The powershell committee has decided to withdraw the RFC Generalized Splatting, so I think it's better to close this one in favor of #13307, to make it less confusing.
Most helpful comment
@iSazonov in my opinion, you (or @vexx32, or maybe even @lzybkr) need to move the RFC forward through _the process_ by addressing the comments...