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

Closed
5 tasks done
Happypig375 opened this issue Jan 22, 2020 · 8 comments
Closed
5 tasks done

Pattern matching with Generics #828

Happypig375 opened this issue Jan 22, 2020 · 8 comments

Comments

@Happypig375
Copy link
Contributor

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
Copy link

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
Copy link
Contributor Author

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
Copy link

OK that's a good example!

@jwosty
Copy link
Contributor

jwosty commented Jan 26, 2020

There is a workaround:

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

@abelbraaksma
Copy link
Member

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
Copy link
Contributor

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
Copy link
Member

@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.

@dsyme
Copy link
Collaborator

dsyme commented Jun 16, 2022

This is by design, as 'T is not known to be related to the target type, except via a super-type.

Since any type is related to any other type via the super-type obj it doesn't really make sense to lift this restriction - or doing so would expose a way of querying types that is probably in many cases mistaken or not what is intended.

@dsyme dsyme closed this as not planned Won't fix, can't repro, duplicate, stale Jun 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants