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 for a shorter deconstruction syntax #781

Closed
5 tasks done
vbfox opened this issue Sep 11, 2019 · 10 comments
Closed
5 tasks done

Allow for a shorter deconstruction syntax #781

vbfox opened this issue Sep 11, 2019 · 10 comments

Comments

@vbfox
Copy link

vbfox commented Sep 11, 2019

I propose we allow for a deconstruction syntax that would be much shorter to declare especially for anonymous records. The original idea was how to declare React component props in a Fable context with anonymous records but it would also be very nice for standard records in a lot of cases.

I present the deconstruction happening here in parameter position but the same should work in other deconstruction usages.

Existing syntax

type Props = { foo: string; bar: string; baz: string }

// Existing, working syntax
let myFunction { foo = foo; bar = bar; baz = baz} =  // Props -> unit
    printfn "%s %s %s" foo bar baz

Proposal

// Simplified version, like #653 but in parameter position
let myFunction { foo; bar; baz } =  // Props -> unit
    printfn "%s %s %s" foo bar baz

// Optionally allow to specify one or more types
let myFunction { foo; bar; baz: string } =  // Props -> unit
    printfn "%s %s %O" foo bar baz

Proposal for anonymous records

// Work with anonymous records
let myFunction {| foo; bar; baz |} = // {| foo: string; bar: string; baz: string |} -> unit
    printfn "%s %s %s" foo bar baz

// Work with anonymous records
let myFunction {| foo; bar; baz: string |} = // {| foo: string; bar: string; baz: string |} -> unit
    printfn "%s %s %O" foo bar baz

Pros and Cons

The advantages of making this adjustment to F# are

  • Promote more usage of types when there is a lot of parameters as combined with imply record labels from expressions #653 it would make them as easy to use as parameters (beside the type creation itself)
  • Unify construction and deconstruction if imply record labels from expressions #653 is done
  • Allowing the syntax for anonymous records would make implementation of javascript APIs where record like arguments are common (Like React) easier.

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

  • Additional complexity in the language for developers, especially for the type specifications as I think the no-type version would be relatively obvious to any F# developer

Extra information

Estimated cost (XS, S, M, L, XL, XXL): M or L I think but not sure

Related suggestions:

Existing implementations of the concept

Javascript/TypeScript already works the same with a similar syntax (Except with less type inference)

interface Props { foo: string; bar: string; baz: boolean }

function test1 ({foo, bar, baz}: Props) {
    console.log(`${foo}, ${bar}, ${baz}`)
}

function test2 (p: Props) {
    let {foo, bar, baz} = p
    console.log(`${foo}, ${bar}, ${baz}`)
}

function test3 ({ foo: xfoo, bar : xbar, baz}: Props) {
    console.log(`${xfoo}, ${xbar}, ${baz}`)
}

function test4 (p: Props) {
    let {foo: xfoo, bar: xbar, baz} = p
    console.log(`${xfoo}, ${xbar}, ${baz}`)
}

Affidavit

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 (Not 100% sure I can't see a breaking change in the parameter position at least)
  • I would be willing to help test this
@robkuz
Copy link

robkuz commented Sep 12, 2019

If you implement this then the reverse should also work

let make foo bar baz = {foo; bar; baz} // string -> string -> string -> Props

@vbfox
Copy link
Author

vbfox commented Sep 13, 2019

@robkuz that's already what #653 woudl allow, even if the sample are all with an anonymous record instead of a named one and show the feature with property getters instead of simply local variables.

But I 100% agree that theses 2 features should work together to provide a simpler unified syntax both in construction and deconstruction position

@cartermp
Copy link
Member

I think this would have a wonderful symmetry with #653

@dsyme
Copy link
Collaborator

dsyme commented Sep 18, 2019

I'm ok with both this and #653.

That said, I'm no particular fan of record pattern matching - I think it makes code obscure. But I can see this improves the situation :)

@markpattison
Copy link

markpattison commented Oct 6, 2019

Could you clarify whether this this relies on having the names of the record fields identical to the names of the deconstructed values?

Assuming this is so, and with typical Pascal casing of record field labels, it feels slightly unaesthetic to end up with a Pascal-cased local value.

@vbfox
Copy link
Author

vbfox commented Oct 6, 2019

Ideally the default would be same-name deconstruction with an option to specify the name (The already existing { Foo = foo } one for example) it's actually a lot easier to imagine a syntax for that in this direction than for #653 where the target is specifically to avoid specifying another identifier.

@dsyme
Copy link
Collaborator

dsyme commented Oct 10, 2019

Assuming this is so, and with typical Pascal casing of record field labels, it feels slightly unaesthetic to end up with a Pascal-cased local value.

Yes, it's very unfortunate. Ugh. Discuss.

@smoothdeveloper
Copy link
Contributor

Assuming this is so, and with typical Pascal casing of record field labels, it feels slightly unaesthetic to end up with a Pascal-cased local value.

Maybe the pascal casing of record field labels convention is not something to be strictly enforced (by whichever mean that would be, like tools like fxcop or gendarme, or naming guidelines), especially if it clashes with some aesthetic aspects of how the record is actually used in the code.

From my perspective, for some records it makes sense to have fields pascal case, and others not.

For me, (longtime C# user) Pascal case mean public member and exposing fields is discouraged for OO reasons.

I don't mind fields (public or not) not being Pascal cased in areas of my code, and in general, I don't mind other aesthetical choices as they inevitably show (unless it is enforced by compiler / language spec) in variety of F# code authored so far.

To come back to the shorter deconstruction syntax (I think the consideration on field naming is not the matter of wether the suggestion is useful / desirable), construction and deconstruction of records is pervasive and powerful construct in F# language, and I like how it works with C# anonymous record constructor.

#781 + #653 get my upvotes.

@ebresafegaga
Copy link

This would be great for pattern matching and I think it's called field punning in OCaml and Haskell (with a GHC extension)

@dsyme
Copy link
Collaborator

dsyme commented Jun 16, 2022

We can track this by #653

@dsyme dsyme closed this as not planned Won't fix, can't repro, duplicate, stale Jun 16, 2022
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

7 participants