Skip to content

Enhancement: Ability to invoke generic methods with explicit type parameters when PowerShell cannot figure out <T> from the context #5146

Closed

Description

Please provide ability to invoke generic methods with explicit type parameters.

Steps to demonstrate

[Array]::Empty[string]()

or using a class:

class A {
    static [string] GetTypeName[T]() { return [T].Name }
}

[A]::GetTypeName[string]()

(Array.Empty was chosen as a simplest possible example. In scripts Iʼd use @())

Current behavior

At line:1 char:16
+ [Array]::Empty[string]()
+                ~
Array index expression is missing or not valid.
At line:1 char:16
+ [Array]::Empty[string]()
+                ~~~~~~~
Unexpected token 'string]' in expression or statement.
At line:1 char:24
+ [Array]::Empty[string]()
+                        ~
An expression was expected after '('.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingArrayIndexExpression

Ruminations

I understand that itʼs not important to have full support of generics in a scripting language or to have 100% feature parity with C# (I might be wrong, — now we have classes in PowerShell after all 🤔)

Here are some not-very-elegant workarounds from the Interwebs:

Here are my questions:

  • Is it possible to implement this at all? How hard it could be?
  • Anyone tried to implement this before?
  • Is it worthwhile?

Just posting this here for discussion. Didnʼt find any mentions of this problem in other issues.

Activity

  1. added
    WG-Languageparser, language semantics
    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif
    on Oct 20, 2017
  2. iSazonov commented on Oct 20, 2017

    @iSazonov
    Collaborator

    At first glance, this is not worthwhile because the main asset of PowerShell is to hide complex details - Powershell automatically calls the correct methods.

    As for implementation it seems is a lot of work but not so difficult.

  3. 0xfeeddeadbeef commented on Oct 20, 2017

    @0xfeeddeadbeef
    ContributorAuthor

    Ilya (@iSazonov) Problem is that PowerShell can not automatically call correct methods when there is no way to deduce generic type parameter automatically (as seen in "Steps to demonstrate"). It must be specified explicitly. This is true for other CLR languages too.
    We are forced to dig through all the complex details using reflection (see list of workarounds).

  4. iSazonov commented on Oct 20, 2017

    @iSazonov
    Collaborator

    I mean that the implementation of the general case is likely to be redundant. On the other hand, if there are specific popular scenarios, maybe we could implement them natively in PowerShell.
    We also have to take into account that PowerShell performs many implicit type conversions - we may encounter insurmountable hurdles.

  5. lzybkr commented on Oct 20, 2017

    @lzybkr
    Contributor

    This is totally a reasonable thing to add, and a perfect example of why I'm excited PowerShell is open source.

    People do want this feature, but as you point out, it's not critical for a shell. With limited bandwidth, this feature was never quite important enough for a small team to implement, but that's not an issue anymore - there are plenty of motivated and capable people who might add this feature now.

    For anyone willing to tackle this:

    • I think the proposed (and obvious) syntax should work, but you must be careful to not break array references, e.g.
    $t = [System.Collections.Generic.Dictionary[string,int]]
    $t.GenericTypeArguments[0]

    When you first see the [, you can't know if you are expecting an expression or a type argument list.

    • We will need a new property in MemberAst. It might not be obvious why here and not InvokeMemberAst, but sometimes you refer to a method without arguments so you can call it later, e.g.
    $op = "hello".PadLeft
    $op.Invoke(15)

    It's probably not important to support the equivalent for generic methods in the initial implementation, but it is important that the representation support it should we want that in the future.

    • This type probably needs a new property to hold the specified type arguments, which would get added from the Ast here.

    Hopefully this is enough to get someone started, if more help is needed, just ask.

  6. added
    Issue-Enhancementthe issue is more of a feature request than a bug
    Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributors
    on Oct 20, 2017
  7. dlwyatt commented on Oct 20, 2017

    @dlwyatt
    Contributor

    I'll take a look when I have some time. It'll certainly be easier than writing PSGenericMethods was, since I can just use the type conversion / binding code / overload identification that's already there instead of having to reinvent it.

  8. dlwyatt commented on Oct 20, 2017

    @dlwyatt
    Contributor

    Jason Shirk (@lzybkr) , how would you feel about this syntax for the PSMethod reference option:

    $op = "Hello".SomeGenericMethod
    $op.InvokeGeneric([someType], $arg1, $arg2)

    This makes it easier to see what's happening at parse-time, and we only need to modify InvokeMemberExpressionAst to support it. If we modify MemberExpressionAst instead (allowing for $op = "Hello".SomeGenericMethod[someType]), it's tricker to distinguish between a property followed by an array index versus a method with a generic type. By looking for MethodName[type](args), it's not ambiguous (parser currently will complain about the open parenthesis).

  9. lzybkr commented on Oct 20, 2017

    @lzybkr
    Contributor

    Because a pipeline isn't allowed as the index expression, I don't think there is any ambiguity - one token of look ahead is sufficient - if could be a type argument list, it is, otherwise it must be an expression.

    If a pipeline was allowed, a bare word is ambiguous - is it a type or an command invocation.

    Note that one token look ahead doesn't mean there is only one token to look for, there is at least a bare word and an '[', e.g.

    [Array]::Empty[string]()
    [Array]::Empty[[string]]()

    The extra square brace is there for a corner case where you want to specify the assembly name along with multiple type arguments, e.g.

    [System.Collections.Generic.Dictionary[[string, mscorlib], string]]
  10. dlwyatt commented on Oct 20, 2017

    @dlwyatt
    Contributor

    Hmm... playing devil's advocate for a second, do we want to support variables in the type expression?

    $something.MethodName[$typeInAVariable]()

    If so, then this could be ambiguous:

    $something.MethodName[$typeInAVariable] # Array index or method with generic type params?
  11. lzybkr commented on Oct 21, 2017

    @lzybkr
    Contributor

    That seemed useful in V2, but it's been that long since I've seriously considered it.

    And honestly, if we wanted to support that, I'd look for a different syntax for explicit type parameters.

  12. dlwyatt commented on Oct 21, 2017

    @dlwyatt
    Contributor

    ok, cool. I'll be on a plane for 4-5 hours tomorrow morning; if I'm not feeling too groggy (waking up at 4am, bleh), I'll plug away at it a bit.

  13. dlwyatt commented on Oct 23, 2017

    @dlwyatt
    Contributor

    I might be biting off more than I can chew on this one. I've tweaked the parser and AST to support the syntax, but I'm having trouble wrapping my head around the compiler and binders to see where to make use of the new information from the AST. Will sleep on it.

  14. 10 remaining items

  15. vexx32 commented on Apr 17, 2020

    @vexx32
    Collaborator

    Would it make sense to create a new PSGenericMethodInvocationConstraints to store the additional type arguments needed for a generic method as a separate value to the types of the method parameters?

  16. lzybkr commented on Apr 17, 2020

    @lzybkr
    Contributor

    Rain Sallow (/u/ta11ow) (@vexx32) - probably not - just add a new field to the existing type.

  17. mklement0 commented on Apr 17, 2020

    @mklement0
    Contributor

    Jason Shirk (@lzybkr), Dave Wyatt (@dlwyatt), I'm unclear on what the consensus ended up being regarding the syntax:

    [Array]::Empty[string](...)

    vs.

    [Array]::Empty.InvokeGeneric([string], ...)

    or both?

    If it's either / or, the first seems much preferable; if we only support type literals, then confusion with numeric indexing shouldn't be a concern ([Array]::Empty[0]); also, even multiple overloads are reported as a single PSMethod`1 object, so indexing isn't useful anyway.

  18. vexx32 commented on Apr 17, 2020

    @vexx32
    Collaborator

    I think that the latter is simpler to implement as it doesn't require parser changes. It could be implemented as a halfway measure, handling the actual logic of invoking the generic method first, and then we can tackle the parser challenge afterwards.

    Jason Shirk (@lzybkr) summarised the possible challenges better than I can in his comment: #5146 (comment)

  19. lzybkr commented on Apr 17, 2020

    @lzybkr
    Contributor

    Rain Sallow (/u/ta11ow) (@vexx32) - the sharing of logic between invoking methods on a PSMethod and invoking methods the normal way is less than ideal. That's not to say you shouldn't bother implementing the former, just don't get your hopes up that the problem is half solved if you go that route.

  20. dlwyatt commented on Apr 18, 2020

    @dlwyatt
    Contributor
  21. vexx32 commented on Apr 19, 2020

    @vexx32
    Collaborator

    Dave Wyatt (@dlwyatt) thanks for that! After resolving a few merge conflicts I was able to get that code rebased on top of the current master branch, and with a few nudges from Patrick Meinecke (@SeeminglyScience) here and there on some of the more confusing bits, I've got a working implementation.

    Tidying it up and figuring out some tests to write, and then I'll throw up a PR.

  22. removed
    Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributors
    on Jul 30, 2020
  23. oising commented on May 3, 2021

    @oising
    Contributor
  24. vexx32 commented on May 3, 2021

    @vexx32
    Collaborator

    This is already sorted in #12412, it's pending a followup on the review. As far as I know, I've sorted all the outstanding comments out, unless I missed something.

  25. oising commented on May 3, 2021

    @oising
    Contributor

    Nice work man!

  26. mikeTWC1984 commented on Jul 21, 2021

    @mikeTWC1984
    Contributor

    Wondering, what is the status of #12412 ? Is it dead?

  27. vexx32 commented on Jul 29, 2021

    @vexx32
    Collaborator

    I've rebased it & asked the other members of the Engine working group to give it a look over. 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifIssue-Enhancementthe issue is more of a feature request than a bugResolution-FixedThe issue is fixed.WG-Languageparser, language semantics

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions