Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions 1-Draft/RFCNNNN-Native-Support-for-Interface-Declarations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
RFC: RFCNNNN
Author: Mathias R. Jessen
Status: Draft
SupercededBy: N/A
Version: 0.1
Area: Language
Comments Due: 2019-01-04
Plan to implement: Yes
---

# Native Support for .NET Interface Declarations

The PowerShell Classes feature introduced in PowerShell 5.0 allows for type definitions at runtime. Since open sourcing PowerShell Core, one of the most oft-repeated feature request I've heard from community members is the ability to also declare and define interfaces at runtime, usually for ease of implementing common software design pattern such as the Builder pattern.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you include a small illustrative example of what you want to achieve?


## Motivation

As a module author,
I can leverage common OO design patterns that require abstract type definitions,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also how about "I can interoperate with .NET frameworks that require implementing interfaces'

so that my implementations are easily communicated and explained to other developers and module authors.

## Specification

Implementing native support for interface declarations require both syntactical and semantic changes to the language engine.

### Syntax

Based on the [original issue](https://github.com/PowerShell/PowerShell/issues/2223) raised by @vors, I imagine a simplified subset of the class syntax, as follows:

```powershell
# interface declaration with no members
interface IMyInterface { }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is a breaking change with a reasonably high likelihood of colliding with a command named "interface". Not saying we shouldn't do it, just that we should point this out in the proposal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JamesWTruher and I agree that breaking is undesirable, and we did find this one instance of a real-world module that would break, but we may still decide to do this via an experimental flag and message heavily around the breakage if we decide the value is high enough


# interface declaration chaining declared members from another interface
interface IMyDisposable : IDisposable { }

# interface declaration with a method
interface IMyInterfaceWithMethod {
[void] DoSomething([int]$IntParam)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume the [void] is unnecessary as it is likewise unnecessary in classes. You might want to include such an example.

}

# interface declaration with a property
interface IMyInterfaceWithProperty {
[int]$MyProperty
}

# interface declaration extending an existing interface with properties and methods
interface IMyExtendedInterfaceWithMembers {
[int]$MyProperty

[void] DoSomething([int]$IntParam)
}
```

### Semantics

The examples above would create new interface types exactly as if one had defined those using C#

The following piece of code in PowerShell:

```powershell
interface IMyInterface {
[int]$MyProperty
[void]MyMethod([string]$StringParam)
}
```

would produce IL completely identical to what the C# compiler would produce for the equivalent:

```csharp
public interface IMyInterface {
int MyProperty { get; set; }
void MyMethod(string StringParam);
}
```

*Note: [C# 8 is slated to support default implementations](https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/) for interface methods, something we might want to consider as well*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had exactly the same thought!


### Notes on potential changes to the AST surface

Currently, when the parser recognizes a `class` or `enum` declaration, it produces a `TypeDefinitionAst` made up of `PropertyMemberAst` and `FunctionMemberAst` children. The type definition AST is then dispatched to either TypeDefiner.DefineTypeHelper or TypeDefiner.DefineEnumHelper for creating and emitting the corresponding runtime type.

We can either add new `AbstractMemberAst` types specifically for the purpose of abstract type definitions, or modify the existing `MemberAst` types to account for abstract member constraints.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on this? And perhaps add examples to the previous section? There is a strong preference at the Committee level for lots of illustrative (practical) examples.


Going with separate AST types for abstract type members would reduce the number of branches and special cases required in the rest of the code base, but the obvious benefit of reusing existing AST types for member declarations is that we can do not have to extend the existing AST visitor surface (ie. no `IAstVisitor3 : IAstVisitor2`, `AstVisitor3 : AstVisitor2, IAstVisitor3` etc.). This may present hidden parsing issues for tooling developers though.

Regardless of design changes to the AST surface, we can either define a new `TypeDefiner.DefineInterfaceHelper` for emitting interface types, or we can modify the existing `DefineTypeHelper` definition to take the `Interface` attribute into account and modify all the other emitting functions being called by it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think that there would be enough common code that it makes sense to generalize the DefineTypeHelper. Another thing to consider is if it makes sense to handle abstract classes in the process.


In my [prototype implementation](https://github.com/IISResetMe/PowerShell/tree/feature/interfaces), I've opted to:

1. extend the AST visitor surface with a set of `AbstractMemberAst` types,
2. update the relevant AstVisitor interfaces and implementations, and
3. dispatch type definition to a new `DefineInterfaceHelper` class

## Alternate Proposals and Considerations

There are a couple of open questions around the design and implementation of interface declaration support:

- Should we allow control over getter/setter declarations?
- If so, should we replicate the syntax from C#?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say yes. As far as C# syntax goes, there's the traditional stuff but there are also expression-valued getters (which are cool.) to consider. However I suspect that conservative might be the best approach.

- Existing class definitions need to [mark interface member implementations virtual](https://github.com/PowerShell/PowerShell/issues/8302)
- Depending on community feedback and envisioned use cases, are interfaces sufficient or might we consider `abstract` classes as well?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😁