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

Make Destructing Assignments in Match Statements First Class #611

Closed
robkuz opened this Issue Sep 26, 2017 · 4 comments

Comments

Projects
None yet
3 participants
@robkuz

robkuz commented Sep 26, 2017

Make Destructing Assignments in Match Statements First Class

When working with DUs, especially ones that describe a state machines
I over and over encounter the following code pattern that I'd like to abstract away

type StateMachine =
    | StateA of SomeType
    | StateB of OtherType
    | StateC of int

let processStateA (s:StateMachine) =
    match s with
    | StateA v -> doSomethingWithAState v |> Success
    | _ -> Error "This State is not allowed"

let processStateB (s:StateMachine) =
    match s with
    | StateB v -> BStateProcessing v |> Success
    | _ -> Error "This State is not allowed"

let processStateC (s:StateMachine) =
    match s with
    | StateC v -> workingOnC v |> Success
    | _ -> Error "This State is not allowed"

Now most of this would be possible to extract and put into a generic method except for the destructing assignments.
So what I'd like to do is to be able to get a handle on that assignment

let processStateGeneric doSomethingFn destructing (s:StateMachine) =
    match s with
    | destructing v -> doSomethingFn v |> Success
    | _ -> Error "This State is not allowed"

This would then allow then to define the functions to be redifined as this

let processStateA = processStateGeneric doSomethingWithAState StateA
let processStateB = processStateGeneric BStateProcessing StateB
let processStateC = processStateGeneric workingOnC StateC

The existing way of approaching this problem in F# is not available (Maybe some code quotations could do the trick)

Pros and Cons

The advantages of making this adjustment to F# are that handling of DUs (specifically of state machines) could be improved quite a bit.

The disadvantages of making this adjustment to F# are implementation woes, more complexity in the language (even thou I would assert that the contrary is true)

Extra information

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

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

This comment has been minimized.

Show comment
Hide comment
@mexx

mexx Sep 27, 2017

Just wanted to point out, that processStateGeneric can already be written generically.

let processStateGeneric doSomethingFn (|Destructing|_|) (s:StateMachine) =
    match s with
    | Destructing v -> doSomethingFn v |> Success
    | _ -> Error "This State is not allowed"

The only thing which is not easily accessible right now is the partial active pattern for the union case name.
One would need to call processStateGeneric with some lambdas.

let processStateA = processStateGeneric doSomethingWithAState (function | StateA x -> Some x | _ -> None)
let processStateB = processStateGeneric BStateProcessing (function | StateB x -> Some x | _ -> None)
let processStateC = processStateGeneric workingOnC (function | StateC x -> Some x | _ -> None)

It would be cool to have a compiler support for those boiler plate function definitions :)

mexx commented Sep 27, 2017

Just wanted to point out, that processStateGeneric can already be written generically.

let processStateGeneric doSomethingFn (|Destructing|_|) (s:StateMachine) =
    match s with
    | Destructing v -> doSomethingFn v |> Success
    | _ -> Error "This State is not allowed"

The only thing which is not easily accessible right now is the partial active pattern for the union case name.
One would need to call processStateGeneric with some lambdas.

let processStateA = processStateGeneric doSomethingWithAState (function | StateA x -> Some x | _ -> None)
let processStateB = processStateGeneric BStateProcessing (function | StateB x -> Some x | _ -> None)
let processStateC = processStateGeneric workingOnC (function | StateC x -> Some x | _ -> None)

It would be cool to have a compiler support for those boiler plate function definitions :)

@mrakgr

This comment has been minimized.

Show comment
Hide comment
@mrakgr

mrakgr Sep 28, 2017

I had no idea that partial active patterns could be passed as arguments like that and I've programming in F# for a while now. This should probably go into the documentation somewhere.

mrakgr commented Sep 28, 2017

I had no idea that partial active patterns could be passed as arguments like that and I've programming in F# for a while now. This should probably go into the documentation somewhere.

@robkuz

This comment has been minimized.

Show comment
Hide comment
@robkuz

robkuz Sep 28, 2017

@mexx wow, I did not know that this is possible. knowing that I can put just a bit of code quotation magic and can probably write something like

let processStateA = processStateGeneric doSomethingWithAState (extract StateA)
//extract: (SomeType -> StateMachine) -> (StateMachine -> SomeType)

If that works out I will close this issue

robkuz commented Sep 28, 2017

@mexx wow, I did not know that this is possible. knowing that I can put just a bit of code quotation magic and can probably write something like

let processStateA = processStateGeneric doSomethingWithAState (extract StateA)
//extract: (SomeType -> StateMachine) -> (StateMachine -> SomeType)

If that works out I will close this issue

@robkuz robkuz closed this Oct 2, 2017

@robkuz

This comment has been minimized.

Show comment
Hide comment
@robkuz

robkuz Oct 2, 2017

I have been able to condense this down to

type FooBar<'a, 'b> =
    | Foo of 'a
    | Bar of 'b

let foo: FooBar<int, bool> = Foo 88

processGen Foo (fun x -> x + 1) foo 
// yields 89

The call to processGen would usually happen somewhere where I already established which Case is being handled which was the main reason to propose this

robkuz commented Oct 2, 2017

I have been able to condense this down to

type FooBar<'a, 'b> =
    | Foo of 'a
    | Bar of 'b

let foo: FooBar<int, bool> = Foo 88

processGen Foo (fun x -> x + 1) foo 
// yields 89

The call to processGen would usually happen somewhere where I already established which Case is being handled which was the main reason to propose this

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