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

Allow results of match! to be bound #695

Closed
5 tasks done
et1975 opened this issue Sep 18, 2018 · 3 comments
Closed
5 tasks done

Allow results of match! to be bound #695

et1975 opened this issue Sep 18, 2018 · 3 comments

Comments

@et1975
Copy link

et1975 commented Sep 18, 2018

Allow results of match! to be bound

Currently binding the results of match! reintroduces quite a bit of noise.
Quoting from the conversation in the implementation:

So
let (Some x) = syncX
becomes
let x = match syncX with Some x -> x | None -> fail()
and
let! (Some x) = asyncX
becomes
let! x = async { match! asyncX with Some x -> return x | None -> fail() }
I suppose we could in theory allow ! computations on the r.h.s. of a let and I don't think it would be a breaking change - just a little odd because the let is really a bind.

Ideally, I think it would look like this:
let x = match! asyncX with Some x -> x | None -> fail()

Pros and Cons

The advantages of making this adjustment to F# are

  • Symmetry with match
  • Code that encounters this type of annoyance would be a little cleaner
  • Non-invasive and simple

The disadvantages of making this adjustment to F# are ...

Extra information

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

Related suggestions: #572

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

jwosty commented Oct 9, 2018

I see the issue, but I don't see any nice solutions to this. The possibility of just allowing one to write:

let x =
    match! x with | Some -> ... | None -> ...

Just feels super dirty and just not right to me: it breaks the consistency of let and let!. The only other way to address this that I can think of would be to introduce a construct that acts like a match! but lets you bind the result to a name as well... Going on a bit of a tangent here, but maybe let could be extended to handle multiple cases at once? I'm really not sure how to handle this.

@kspeakman
Copy link

kspeakman commented Sep 23, 2019

I run into this lately. Especially when I need to tag the result of a side effect. Here are the two ways to accomplish it that I could think of.

Note: This is used in the effectful part of my code.

Nested Async as mentioned above

        match effect with
        | EnsurePositionTableExists ->
            async {
                let! result =
                    async {
                        match! Sql.write sqlConfig PositionTrackerSql.ensureExists with
                        | Ok () ->
                            return Ok ()
                        | Error ex ->
                            log.LogCritical("{@Effect} failed {@Ex}", effect, ex)
                            return Error ()
                    }
                return PositionTableExists result
            }
        ...

Repeat tag in each branch

        match effect with
        | EnsurePositionTableExists ->
            async {
                match! Sql.write sqlConfig PositionTrackerSql.ensureExists with
                | Ok () ->
                    return PositionTableExists (Ok ())
                | Error ex ->
                    log.LogCritical("{@Effect} failed {@Ex}", effect, ex)
                    return PositionTableExists (Error ())
            }
        ...

Each of these alternatives feels wasteful in some way. Although the nested async feels worse (and should perform worse unless optimized away).

Here is how I wish I could express it:

        match effect with
        | EnsurePositionTableExists ->
            async {
                match! Sql.write sqlConfig PositionTrackerSql.ensureExists with
                | Ok () ->
                    Ok ()
                | Error ex ->
                    log.LogCritical("{@Effect} failed {@Ex}", effect, ex)
                    Error ()
                |> PositionTableExists
            }
        ...

But this includes non-related issues like eliminating gratuitous return statements and continuation with a pipe operator.

@dsyme
Copy link
Collaborator

dsyme commented Apr 12, 2023

Closing this old issue

@dsyme dsyme closed this as completed Apr 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants