I suggest we ...

Support C#-like Anonymous Types in F#

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#.

215 votes
Vote
Sign in
(thinking…)
Sign in with: facebook google
Signed in as (Sign out)
You have left! (?) (thinking…)
Howard Mansell shared this idea  ·   ·  Flag idea as inappropriate…  ·  Admin →

18 comments

Sign in
(thinking…)
Sign in with: facebook google
Signed in as (Sign out)
Submitting...
  • Jeffrey Pierson commented  ·   ·  Flag as inappropriate

    If voting wasn't close I would +3 for this. I really do enjoy this feature in C# and the ability to return them from functions in TypeScript.

  • Bruno Bozza commented  ·   ·  Flag as inappropriate

    This is badly needed for data wrangling over typed datasets. I tried to use F#, which I prefer for everything else, but when every 5 lines of query code induce a new record type, updating the records becomes tedious very quickly, so I am back to C# for this.

    C#'s support is not perfect (or new), but it has really convenient record punning. And for the drastic cuts they had to make in the design (i.e.: anonymous types being non-denotable and limited to method scope), my favorite feature of this design is that it allowed the team to ship it, and now I can use it. Yes, I expect more from F#, but I would be happy to give up on structural subtyping, first class labels and even denotability, in order to get C#-style functionality.

  • Yemi Bedu commented  ·   ·  Flag as inappropriate

    What is the expectation for these types to shadow each other?
    Would you want the compiler to warn about a named record that matches your anonymous one in certain use cases?
    Examples would be anonymous record of { Length:int ; Width:int } that could be in a lot of places.
    Another example is if you describe a Point { X:int ; Y:int } and later the anonymous version gets used in a function.
    Should it infer Point or just warn "a named record of type Point matches"?

    Would we want the allow declaration signatures to be anonymous

    let Girl = {Name: string ; Age: int }
    let Boy = {Name: string; Age: int }
    let greet (child: {Name;Age}) = printfn "%A" child.Name

    greet {Name="you";Age=21}

  • Ben Lappin commented  ·   ·  Flag as inappropriate

    One benefit is that if you're only using a type in one place, somewhere deep inside a let binding, you currently need to define the type prior to the outermost let, which can be inconvenient. Take the following example:

    type Thingy = { Inty: int; Stringy: string }
    let ....
    let ....
    let aThingy = { Inty = 5; Stringy = "F# is fun" }

    It would be preferable to be able to write something like:

    let....
    let....
    let aLocalThingy = { Inty: int = 6; Stringy: string = "Future F# is funner" }

    ...and in either case, you could then do what you needed with the record, as long as the flow of code allowed its (anonymous) type to be inferred.

    Essentially I would see this as "syntactic sugar" over defining a type that will only be used in one place.

  • Jari Pennanen commented  ·   ·  Flag as inappropriate

    Anonymous Records (which is a same thing as propsed here) are really neat, they are basically like TypeScript interfaces, allowing structural typing where it makes sens.

    Scala (library) implementation: http://downloads.typesafe.com/website/presentations/ScalaDaysSF2015/T4_Vogt_Compossible.pdf

    Above Scala implementation has some really great benefits, potentially shortening typesafe SQL syntax a lot.

    Haskell (library) implementation https://gist.github.com/nikita-volkov/6977841 not very familiar with this one, but that is where I took the name.

  • Michael commented  ·   ·  Flag as inappropriate

    What's the benefit here? Creating anonymous types, just to turn around and use reflection on them seems sort of hacky. C# APIs like ASP.NET MVC use this because they don't have any slick way of doing [ "prop1", "val1"; "prop2", "val2" ].

  • Christopher Stevenson commented  ·   ·  Flag as inappropriate

    If the issue with this idea is syntax, here's a thought: "new with { property1 = value; property2 = value }". The "new with" indicates that this is an anonymous record.

  • Anonymous commented  ·   ·  Flag as inappropriate

    For a bit of historical perspective, structural record types were part of Standard ML decades before C#.

  • Eamon Nerbonne commented  ·   ·  Flag as inappropriate

    Swift's solution isn't very good, however, so let's not copy that. in particular, swift's names don't really matter, it's still just a positional tuple, so when you do a destructuring let binding, you bind by position, not name. If you happen to swap the names (or mistype one, or refactor and change a member's name), then swift will still let you destructure by position, ignoring the names.

  • Maciej J. Bańkowski commented  ·   ·  Flag as inappropriate

    @Joel Mueller, I have created a pull request for Neo4jClient that includes support for tuples: https://github.com/Readify/Neo4jClient/pull/56 It is not perfect but works as a workaround for the lack of anonymous types.

    BTW I totally agree F# should support something like anonymous record types. I do not know why this comes up so late in the F# dev cycle but the requirement to create one-time-use types to work with data is hard for me to understand. As @Lev Gorodinski noted, dynamic has major drawback in that it relaxes strong typing which is a very important F# feature we should keep.

    Does anyone know if there is any technical issue preventing implementation of anonymous types in F#?

  • Joel Mueller commented  ·   ·  Flag as inappropriate

    You can't use the dynamic operator in a quotation, however. There are some C# libraries (such as Neo4jClient) that expect LINQ expression trees that create anonymous types, and extract property names and data types from the expression tree. An F# equivalent is only possible if the dynamic operator is usable in a quotation, or if F# supports anonymous types.

F# Language

Feedback and Knowledge Base