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

Local type names for anonymous records #776

Open
Savelenko opened this issue Jul 31, 2019 · 3 comments

Comments

@Savelenko
Copy link

commented Jul 31, 2019

It feels that convenience and hence utility of anonymous records suffers from the need to (repeatedly) provide type annotations, especially in relatively local contexts, where an anonymous record value, however, does not "flow" in the same expression. My motivating example is the following function which defines an FsCheck generator:

let generator someData = gen {
  let transformedData =
      someData |> Seq.map (fun d -> {| A = f d; B = g d; C = h d |})
  // Now I want to define multiple small functions all working with transformed data.
  let modify1 (td : {| A : TA ; B : TB ; C : TC |}) = //...
  let modify2 (td : {| A : TA ; B : TB ; C : TC |}) = //...
  let modify3 (td : {| A : TA ; B : TB ; C : TC |}) = //...
  // And maybe more, as per generator combinator programming style.
  // The rest
}

While throwing together values of the anonymous record type in the binding of transformedData is convenient, all subsequent definitions of modifyN are very tedious.

I propose we allow local naming of anonymous record types, such that the name can be used in the neighboring signatures, removing the need for bulky record-style type annotations. Possible syntax could be unified with named patterns syntax:

let generator someData = gen {
  let transformedData =
      someData |> Seq.map (fun d -> {| A = f d; B = g d; C = h d |} as type R) // New syntax
  // Now I want to define multiple small functions all working with transformed data.
  let modify1 (td : R) = expr td.A // Compiler knows of field A due to name R (??)
  let modify2 (td : R) = //...
  let modify3 (td : R) = //...
  // And maybe more, as per generator combinator programming style.
  // The rest
}

Type name R is local in the sense that, similarly to named patterns, it does not escape the scope of function generator in this case. This way, it is not possible to introduce a myriad of globally visible types from within functions/expressions.

Pros and Cons

Pros: improved ergonomics of anonymous records.

Cons: new ad-hoc (but small) syntax.

Extra information

Estimated cost (XS, S, M, L, XL, XXL): Frankly, no idea about the internals. Somehow "lift" the anonymous record type under the hood? M-XL?

Related suggestions:
#759 mentions similar symptoms,
#713 local name might allow pattern matching in modifyN due to the same principle of "enough information about the type".

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

This comment has been minimized.

Copy link

commented Jul 31, 2019

I think this may be a related issue. Its hard to make code with anonymous records re-usable because of lack of type constraint for records or anonymous records.
For example,

let f x = {|x with A=1|}

does not compile due to type error: "FS3245: The input to a copy-and-update expression that creates an anonymous record must be either an anonymous record or a record"

Hopefully there is an easy fix

@charlesroddie

This comment has been minimized.

Copy link

commented Jul 31, 2019

#150 is a better solution.

@Savelenko

This comment has been minimized.

Copy link
Author

commented Aug 1, 2019

#150 is a better solution.

It is similar, but does not profit from anonymous records. With this proposal, one can, in effect, define the type and the value "in-line". With a separate local type declaration, one would have to type essentially the same twice. Furthermore, adding or removing a field from an anon. record is all there is to a potential refactoring, while a separate type declaration would, again, require fixing also the type declaration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.