-
Notifications
You must be signed in to change notification settings - Fork 20
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
Support anonymous record types [ RFC FS-1030 ] #207
Comments
I really do enjoy this feature in C# and the ability to return them from functions in TypeScript. The fact that F# doesn't have this concept surprised me because of the extensive use of type inference in other places I guess I just expected it was there until I went reaching for it. |
Archived comments:
|
I find records are a reasonable fallback for not having anonymous type, maybe a good solution would be to declare types in scope of a let binding (I'd assume they'd have a mangled name in IL and eventually could be coallesced at assembly level). I must say I haven't felt the lack of them in F# to be hindering me much albeit I tend to use them in C# (lack of short tuple notation?). For me this is at most a nice to have feature, I'm looking for more C# converts to 👍 it. |
I do need them when working with intermediate data structure. The alternative is either to use tuples or to declare out of the body of the functions all the intermediate records that are being used. I know that struct tuples in C# will have named fields (erased at runtime), that's exactly what we need. This is unfortunate because it's F# rather than C# who needs this feature. C# has already anonymous Specifically I just had a situation with a customer where I proposed F# as language for migrating a huge set of web scraping scripts to .Net, but the fact that it doesn't support anonymous types didn't convince the customer to use F# instead of C# and unfortunately I don't have any argument against that because he's right, it will be more verbose and will require more maintenance since each time there is a change in the fields there must be edited in two different places. |
@gmpl does having ability to define local types come close to this? AFAIR anonymous types in C# don't have structural equality so I wouldn't qualify those as anonymous records. Coming on par / compatible with named fields in tuples from C# would be a great thing. It seems for this feature (or the first shot) to generate more appeal, it should iron out the details more:
Can a type provider solution help in your specific context? I know it sometimes helps with maintenance and schema changes in similar contexts. |
@smoothdeveloper You're right, I used the wrong word, I wanted to say "anonymous types" and I know it's not exactly the same but at least they have a mechanism available. Now they have two, we still have zero. Type providers are vey limited in this scenario, because normally the data is not in the shape you will store it, you have to collect it, un-pivot it so you need some intermediate structures, in each step you do data transformation, that way is easy to debug/test it. Type providers help in the case where the data already has more or less the shape you expect or in the initial step, but then you need to transform it and also perform some validations. If you are interested I can show you some examples, you'll see how important is to have named tuples available. |
@gmpl, thanks I understand and agree that anonymous types are a nice feature for this use case. This seems like a not so small feature, apparently lots of vote for it in uservoice :) |
@gmpl @smoothdeveloper I do feel the lack of this feature when doing data-centric programming in F# Putting aside syntax, the fundamental issues with anonymous types are
For now, assume a syntax like
For point 2, the question is whether
If "yes" to all, then access to the types is necessarily going to have to be via a dynamic mechanism (at least when it is not possible to determine which assembly the type is declared in, along with a canonical stable mangled name for the type). I do feel a mechanism to ensure "fast access" across assembly boundaries would be sensible. For point 5 above, the question is whether |
@dsyme I don't think we need them to be compatible across assembly boundaries, the idea behind anonymous types is that they are short-lived types used internally in a method. Anyway, don't you think that right now for F# is better to concentrate in having C# named StructTuples instead? I would love to see this feature, if it's too late for F# 4.1 at least for the updates. |
From a language design perspective, I would be pretty upset to have assembly-boundary identity for these types. I think that just sucks in all sorts of ways, though I understand it may cover many use cases.
That's the C# design idea. But it's fundamentally flawed and only justifiable through the self-justifying circle of "that's how C# does it so it must be right" or even "that's all we could do given the design of the CLR even though the guys down the corridor control the design of the CLR". It certainly encourages larger methods and makes refactoring large code into smaller reusable methods difficult.
Perhaps. However I find the named-field-of-tuples-via-attribute-metadata feature very "smelly" in C#. There are many cases where the lack of reflection on property names will make these worse than useless (e.g. LINQ queries mapping to actual table names; or serializing using Json.NET, or pushing the objects across to a UI, or the lack of debugging introspection except in cases where additional analysis is possible to recover the field names etc. etc.). The use of partial erasure makes them smell like Java generics. It's also not clear how widely they will be adopted in API design. And even if widely, it's not clear if they will be respected and loved as part of good API design. Aside: TBH it's very odd from a language design perspective for C# to introduce non-reflection-friendly types in such a fundamental construct so late in the day, at version 7.0, when so many C# tools depend on using reflection well. It's honestly like one of the fundamental design principles of C# has been forgotten along the way: things like anonymous types were designed very much with reflection in mind. For F#, we also use reflection a lot, e.g. in pretty-printing output using %A or FSI, or for all the same scenarios as C#.
Definitely far, far too late for F# 4.1! More like 4.2 or 4.3 or 5.0 |
C# anonymous types are immutable, opposite of recently added struct tuples. If there is no overhead, we should consider making those feel like records or struct records as they are well known to F# users and have some advantages (structural comparison and |
@dsyme, there is a feature of CLR for making type with same representation accross different assemblies work like the same type, but I don't recall the name, maybe that would be useful to remove the "all sorts of ways" the C# anonymous types aren't so great? It is used in VS extension development and I think it came with .NET 4.5. |
My interest in anonymous records is to have them treated very much like I can in TypeScript where I can return adhoc typed objects from functions and have those functions signatures including the return type inferred at compile time. What I'd like to be able to do is stuff like the following. let createAHappyOne name = new { name; mood = "Happy" }
let createASadOne name = new { name; mood = "Sad" }
let printOne one = printf "%s is a in a %s mood" one.name one.mood Without the ability to deal with the values outside of the scope of a function I think usage would be related to the same cases it is currently within C# which helps but doesn't seem to cover several other interesting cases. Ideally having these be able to cross assembly boundaries would be nice to but only from a type inference perspective as no name would be available to use as a hint. Perhaps the underlying name could be derived from the first function that returns the type. For example it would be nice to be able to have I would expect these values to be immutable, having the ability to have either struct or ref type, and yes having structural equality like anonymous types in C# makes sense. Sorry if my post is light on the technical side but I just wanted to express my reason for supporting the feature and what I was hoping to expect out of it. |
in C# they are not structurally equal, at least in C# interactive: var a = new[] { new { a = 1 }, new { a = 1 } };
a[0] == a[1]
// false
a[0].GetType() == a[1].GetType()
// true |
I do think this some rule like this may be plausible and practical, and at least allow all types of all data to be named,inferred and used across assembly boundaries. When compiling to Javascript then these limitations would presumably be liftable. Other options are mangling |
@smoothdeveloper, perhaps structural equality is not the correct term but what I mean is that for C# the following works because anonymous types automatically override var a = new[] { new { a = 1 }, new { a = 1 } };
object.Equals(a[0], a[1]);
// true Which makes me curious about how F# record types behave in C# on whether they override == operator or not or how that even makes sense to override an operator when interoping between CLR languages. Update: Based on the details here it looks like overriding the |
a[0] == a[1] this compares references. Structural equality check is: a[0].a == a[1].a and is, of course, true. |
@chojrak11 what I mean is this (tried in C# interactive): var a = new { a = 1 };
var b = new { a = 1 };
a.GetType() == b.GetType()
// true
a == b
// false |
So this is referential integrity, not structural. C# by default compares references, and |
@chojrak11 yes, I was stating anonymous types in C# aren't structurally comparable, and in the context of this feature request some peolpe compared them to F# records (which are), I was asking the question "do we want F# anonymous types as discussed in this feature suggestion to be structurally comparable?". I hope this clarifies why I brought structural equality and the lack of support in C#. I'd want F# anonymous types discussed here to be structurally comparable by default, what do others think? |
@smoothdeveloper I would go so far as to amend the feature request to "anonymous record types" (implying structural comparability). At least I think that's what I would want from this feature. |
Here are a handful of StackOverflow questions I could find as evidence of real world friction people are running into especially around Linq. http://stackoverflow.com/q/8144184/83658 |
@jpierson Thanks for that list I must admit I struggle with this issue in F#. I want the language to have anonymous record types, but can't settle on the right tradeoff to make. It's one of the things that continually niggles away at me as I think about the language design. |
@sgoguen Could you move those comments to the RFC discussion thread please? Thanks! |
I can write nested functions, so why can't I write nested types? let f x =
let g y = y + 5
g x
let f x =
type MyThing = ...
let g y = y + 5
g x
For .NET library usage reasons the classes are sometimes large e.g. type MyClass() as this =
inherit SomeFrameworkLibraryType<SomeContext>()
member __.AThing() =
async {
// 30 lines...
}
// 300 lines...
member __.NThing() =
async {
// Instead of tuple, let me just write the return type near the usage
// I don't care if it's named tuple, anonymous, or whatever
// I just want to write it here, and not 400 lines up.
// If not here, can I write it before `NThing()`, not before the whole class?
return (x1, y1, z1, x2, y2, z2, ...)
} |
Note that C# recently added another language feature that can serve a similar purpose (among others) - the named tuple:
|
Is there an option to bypass structural equality? |
@xperiadri, I would assume that Object.ReferenceEquals would work for testing reference equality. |
@kflu Yes and no. The C# named-tuple feature uses non-standard metadata representations that mean the metadata is not available via normal .NET reflection. This unfortunately means we're going to see a mess where people extract the metadata from attributes using non-standard and no doubt buggy techniques. It's an unfortunate situation that I fear will gradually lead to the over-complexifying of code that supports reflective inspection.
This is covered in the RFC. |
The PR has been merged: dotnet/fsharp#4499 |
What About
which is mentioned in the RFC? Should we open up a new Suggestion item for this? The Extension seems unobstrusive to me, but would improve my workflow (mainly FSI + working with data). |
@ronaldschlenker Can you file a new issue with a use case stated? |
@cartermp @dsyme I realize this issue is closed but I am just hitting an exception when consuming a anonymous record across assembly boundary, and it's not clear to me after reading the RFC (https://github.com/fsharp/fslang-design/blob/master/FSharp-4.6/FS-1030-anonymous-records.md#cross-assembly-working) and reading through the above thread whether it was decided that this should be a fully supported scenario in F# 4.6. Could you confirm? Here is the exception that I get, the type is defined in assembly
I am happy to provide more details in a separate issue if this is confirmed to be a bug. |
@blumu This should indeed be fully supported. Can you submit a bug report to https://github.com/dotnet/fsharp ? |
Do we have a tracking bug for this yet? I definitely want to understand it |
@dsyme @cartermp Thanks for the response. Good news: shortly after I posted my message, I was able to work around the bug by tweaking some of the type definitions in my code. |
Submitted by Howard Mansell on 3/24/2014 12:00:00 AM
215 votes on UserVoice prior to migration
Commonly I want to return some named values, or sequence of named values, in some expression. Currently I am forced to either go through the additional effort of defining a type, or use a tuple (with its associated error-proneness if there are multiple values of the same type). Anonymous record types would be very useful and would eliminate one of the areas of additional verbosity compared to C#.
Original UserVoice Submission
Archived Uservoice Comments
RFC is here: https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1030-anonymous-records.md
The text was updated successfully, but these errors were encountered: