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

More struct tuple inference #988

Open
5 tasks done
Happypig375 opened this issue Mar 13, 2021 · 7 comments
Open
5 tasks done

More struct tuple inference #988

Happypig375 opened this issue Mar 13, 2021 · 7 comments

Comments

@Happypig375
Copy link
Contributor

More struct tuple inference

type C() =
    member _.M(x:struct(int*int)[]) =
        for a, b in x do () // Allowed
        x |> Array.iter (fun (a, b) -> ()) // Error, should allow
        for v in x do
            let a, b = v  // Error, should allow
            v |> fun (a, b) -> ()  // Error, should allow

The existing way of approaching this problem in F# is:

type C() =
    member _.M(x:struct(int*int)[]) =
        for a, b in x do () // I want this everywhere!
        x |> Array.iter (fun struct(a, b) -> ())
        for v in x do
            let struct(a, b) = v
            v |> fun struct(a, b) -> ()

Pros and Cons

The advantages of making this adjustment to F# are

  1. Conciseness
  2. Convenience
  3. Making an existing feature better

The disadvantages of making this adjustment to F# are that is will be more difficult to determine whether a tuple is a struct just by looking at the code.

Extra information

Estimated cost (XS, S, M, L, XL, XXL): S to 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

For Readers

If you would like to see this issue implemented, please click the 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.

@dsyme
Copy link
Collaborator

dsyme commented Mar 15, 2021

Yes, I agree. Marking as approved in principle

@kerams
Copy link

kerams commented Mar 26, 2021

For type inference would the typechecker now potentially have to go through the entire method body to settle the (non)structness of a tuple?

type C() =
    member _.M(x) =
        for a, b in x do ()
        x |> Array.iter (fun (a, b) -> ())
        for v in x do
            let a, b = v
            // up to this point struct has not been used, so we can't be sure either way yet
            v |> fun (a, b) -> ()
            // no struct in the last pattern either, so x is an array of "heap" tuples

@smoothdeveloper
Copy link
Contributor

@kerams great point, and it sounds plausible.

Although I'm wondering if it really matters to have that deferred since it is already identified as a tuple, which is most of the work in simple cases.

It would be interesting to see if there are method overloads in the wild that does so on tuple types arguments, in this case I'd say there is more risk of slippery slope in the type checker performance; if your question was geared to that.

@Happypig375
Copy link
Contributor Author

@kerams To stay in line with F#'s top-to-bottom left-to-right type inference, we would probably still need a type annotation at the top.

@dsyme
Copy link
Collaborator

dsyme commented Mar 29, 2021

Some cases of this can I think be dealt with by changing this use of UnifyRefTupleType to UnifyTupleTypeAndInferCharacteristics in CheckExpressions.fs:

| SynSimplePats.SimplePats (ps, m) -> 
    let ptys = UnifyRefTupleType env.eContextInfo cenv env.DisplayEnv m ty ps

However I think the more general case would require putting an inference variable into TupInfo:

[<RequireQualifiedAccess>] 
type TupInfo = 
    /// Some constant, e.g. true or false for tupInfo
    | Const of bool

This is specifically for the cases like let a, b = v - the problem here is that the pattern is processed before the r.h.s. The "decision" about whether the pattern is a ref tuple or struct tuple thus needs to be delayed until after the r.h.s. of the let binding has been processed. This is best done via an inference variable that delays that decision.

Putting an inference variable into TupInfo is do-able but non-trivial. If somone wants to look at this I can help eith this part.

@Lanayx
Copy link

Lanayx commented Oct 8, 2023

It has just came to my mind that tuples type-directed inference will look nice along with collections inference, i.e.

let x: ResizeArray = [ 1; 2; 3 ]
let y: struct(int*int*int) = (1, 2, 3)

CC #1086

@auduchinok
Copy link

I think this one is rather important one to implement, at least for the tooling. Consider this example:

let f (a: struct (int * int)) =
    a |> (fun ((x, y)) -> x + y)

The parens aren't required by syntax, and tooling may suggest to remove them and/or not add during source modifications:

Screenshot 2023-10-09 at 13 48 54

However, if the type is inferred to be a struct tuple, an error is produced if the extra parens are absent:
Screenshot 2023-10-09 at 13 50 11

This is quite unfortunate, as tools depending on the syntax analysis shouldn't know anything about expected types. These tools include formatters, redundant parens analyzers, helpers for source changes and so on. Without this change we'll need to always add extra parens around tuple patterns in some of these tools. With this change we can safely remove them when the language version used supports it.

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

8 participants