Skip to content

Support C#-style Deconstruct method based pattern matching #751

Open
@yatli

Description

@yatli

The C#-way of doing quick pattern matching and value extraction is by declaring member functions of name Deconstruct, or static extension methods accordingly. A Deconstruct method has the signature of:

public void Deconstruct(out T1 name1, out T2 name2, ...)

... which actively extracts values from the class instance.
Multiple overloads can be supplied to accommodate different ways of deconstruction.

In F#, we automatically receive pattern matching benefits for DUs and records, but currently the only way to peek into the content of a class instance in a pattern, is to create an active pattern for it. Since active patterns cannot be overloaded, one has to come up with different names for different ways of extraction, which adds extra complexity to the matter.

So I propose that we support this in F#.
A new kind of pattern is then added to classes, which allows a class to be matched against a tuple. When the compiler sees such a pattern, it looks up the class definition and extensions for Deconstruct methods, and align the tuple signature with the [<Out>] T byref parameters -- the [<Out>] and byref part should be removed. Then further matching of the elements in the tuple may proceed. Type inference rules unify the items.

Note, it's not possible practical to use records (anonymous or not) in this case, because there can be multiple Deconstruct overloads.

A quick glance of what it may look like:

type MyEventArgs() =
    inherits EventArgs()
    member val foo: int = 123 with get, set
    member x.Deconstruct([<Out>] foo: _ byref) =
        foo <- x.foo

// later:

myControl.MyEvent.Subscribe(fun (foo: int)  ->
    printfn "extracted foo = %d" foo
) |> ignore

Applications Brainstorming

  • It would be then very cool to also add support in FSharp.Data, so that the provided types can have better pattern matching.
  • A ResizeArray<T> can be then matched as a list!
  • Allow custom deconstruction on DU/records?
  • (Going too far maybe?) if some parameters are not marked as [<Out>] byref, it can be used as input parameter, giving it full active pattern matching capabilities

Pros and Cons

The advantages of making this adjustment to F# are:

  • More natural pattern matching on classes
  • Better interop with C# while not disrupting the F# paradigms

The disadvantages of making this adjustment to F# are:

  • Implicit dependency on the name Deconstruct
  • [<Out>] name: T byref seems taboo in F#
  • (Taken from the C# docs) Multiple Deconstruct methods that have the same number of out parameters or the same number and type of out parameters in a different order can cause confusion
  • ...but C# already did this

Extra information

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

Related suggestions: (put links to related suggestions here)

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions