Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pattern matching with Generics #828

Open
Happypig375 opened this issue Jan 22, 2020 · 7 comments
Open

Pattern matching with Generics #828

Happypig375 opened this issue Jan 22, 2020 · 7 comments

Comments

@Happypig375
Copy link
Contributor

@Happypig375 Happypig375 commented Jan 22, 2020

Pattern matching with Generics

I propose we permit pattern matching on generic types.

type Packet() = class end
type KeepalivePacket() = inherit Packet()
let send<'T when 'T :> Packet>(packet:'T) =
    match packet with
    | :? KeepalivePacket as keepalive -> ()
        // Do stuff with keepalive
error FS0008: This runtime coercion or type test from type
    'T    
 to 
    KeepalivePacket    
involves an indeterminate type based on information prior to this program point. Runtime type tests are not allowed on some types. Further type annotations are needed.

The existing way of approaching this problem in F# is to change it to an if-statement on type objects.

This was already implemented in C# 7.1. We should do so too.

Pros and Cons

The advantages of making this adjustment to F# are

  1. I couldn't find any mention of unsupported pattern matching with generic parameters/variables in the design notes/blog posts.
  2. If intended I do hope that this is a limitation that could be removed in the future as I see your use case as one that wouldn't be all that uncommon.
  3. Looks like a bug.
  4. It seems strange to me that the language specification is defined such that no (explicit) conversion exists here. We'll look at what we can do about that.
  5. This is a very surprising limitation. It severely limits the usefulness of pattern matching in generic code, making many of the changes I was hoping to make to my code base impossible.
  6. Cases where pattern-matching should "obviously" be permitted currently fail to compile.

The disadvantages of making this adjustment to F# are none that I can think of.

Extra information

Estimated cost (XS, S, M, L, XL, XXL): S

Related suggestions:
dotnet/roslyn#16195
dotnet/roslyn#16993
dotnet/csharplang#154
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/generics-pattern-match.md

Affidavit (please submit!)

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I or my company would be willing to help implement and/or test this
@charlesroddie

This comment has been minimized.

Copy link

@charlesroddie charlesroddie commented Jan 26, 2020

The existing way of approaching this problem in F# is to change it to an if-statement on type objects.

I would consider (at least outside the compiler) matching on types and downcasting to be a code smell, and the extra difficulty of using if-statements on type objects here may provide a good deterrent. Do others consider this type of code to be proper?

@Happypig375

This comment has been minimized.

Copy link
Contributor Author

@Happypig375 Happypig375 commented Jan 26, 2020

Matching on DU-like hierarchies from C# is a valid scenario and this proposal can help create concise and correct code.

@charlesroddie

This comment has been minimized.

Copy link

@charlesroddie charlesroddie commented Jan 26, 2020

OK that's a good example!

@jwosty

This comment has been minimized.

Copy link
Contributor

@jwosty jwosty commented Jan 26, 2020

There is a workaround:

match (packet :> obj) with
| :? KeepalivePacket as keepalive -> ()
@abelbraaksma

This comment has been minimized.

Copy link

@abelbraaksma abelbraaksma commented Jan 28, 2020

Note that the compiler can do this statically, but it requires a hidden switch (I forgot which it was), that would allow you to match over types in an SRTP way. An example is the current string implementation, but there are many more.

The simpler workaround is to just use boxing.

@jwosty

This comment has been minimized.

Copy link
Contributor

@jwosty jwosty commented Jan 28, 2020

Note that the compiler can do this statically, but it requires a hidden switch (I forgot which it was), that would allow you to match over types in an SRTP way. An example is the current string implementation, but there are many more.

The simpler workaround is to just use boxing.

Isn't it the switch that's used to compile the core library? i.e. the one that's used to make the (+) operator work with all number types

@abelbraaksma

This comment has been minimized.

Copy link

@abelbraaksma abelbraaksma commented Jan 29, 2020

@jwosty, yes, I think so, though the way I understand it, it's essentially an extension to SRTP. One option would be to allow that syntax outside the compiler, but I've experimented in the past with that hidden compiler switch. At least it used to work, but had some limitations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.