Allow upcasting in pattern match expressions #536

Open
chkn opened this Issue Feb 11, 2017 · 1 comment

Projects

None yet

2 participants

@chkn
chkn commented Feb 11, 2017 edited

Allow upcasting in pattern match expressions

I propose we enable some syntax to upcast a bound identifier in a pattern matching expression.

Consider this simple example:

type Base() = member __.SomeBaseMember = "hello"
type Foo() = inherit Base()
type Bar() = inherit Base()
type DU =
   | FOO of Foo
   | BAR of Bar
let foo = FOO(Foo())
match foo with
| FOO(b)
| BAR(b) -> b.SomeBaseMember

As expected, this code yields a message like:

This expression was expected to have type
     'Foo'    
 but here has type
     'Base'     at 5,6

I propose we add an upcast syntax so you can write something like:

match foo with
| FOO(b :> Base)
| BAR(b :> Base) -> b.SomeBaseMember

In the pattern match body, the identifier b would then be typed Base.

Alternatives

  1. Make no changes to the language and instead structure your code like this:

    match foo with
    | FOO(b) -> b.SomeBaseMember
    | BAR(b) -> b.SomeBaseMember

    This works, but when there are many cases, or when b.SomeBaseMember is actually a complex expression, this becomes less than ideal.

    A better workaround is to declare an active pattern:

    let inline (|Base|) b = b :> Base
    match foo with
     | FOO(Base b) 
     | BAR(Base b) -> b

    However, this is still not as elegant as an upcast operator built into the pattern matching language.

  2. Instead of adding new syntax, use the current type check pattern:

    match foo with
    | FOO(:? Base as b)
    | BAR(:? Base as b) -> b.SomeBaseMember

    This currently produces a compiler error:

Type constraint mismatch. The type 
     'Base'    
is not compatible with type
     'Foo'    
at 5,6

Pros and Cons

Pro: A more elegant syntax to solve the problem above.

Con: Requires implementation in the compiler

Extra informtion

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

Affadavit (must be submitted)

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 would be willing to help implement and/or test this
  • I or my company would be willing to help crowdfund F# Software Foundation members to work on this
@smoothdeveloper

It is a nicely written suggestions and I like the idea of extending abilities in pattern matching.

That being said, I'm trying to see if F# couldn't be made super clever in such case by doing that unified binding automatically, so it wouldn't even require the cast in this case (but the proposed syntax could be supported as well for defining the type explicitly).

It would work by identifying the most specialized common subtype that can unify the bindings.

I think having the unification work without extra syntax would be very nice and keep that feature approachable to the newcomers.

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