in C# you can do stuff like :
var a = new {name = "cow", sound = "moooo", omg = "wtfbbq"};
and in Python you can do stuff like
a = t(name = "cow", sound = "moooo", omg = "wtfbbq")
Not by default, of course, but it's trivial to implement a class t
that lets you do it. Infact I did exactly that when I was working with Python and found it incredibly handy for small throwaway containers where you want to be able to access the components by name rather than by index (which is easy to mix up).
Other than that detail, they are basically identical to tuples in the niche they serve.
In particular, I'm looking at this C# code now:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
and it's F# equivalent
type Route = {
controller : string
action : string
id : UrlParameter }
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
{ controller = "Home"; action = "Index"; id = UrlParameter.Optional } // Parameter defaults
)
Which is both verbose and repetitive, not to mention rather annoying. How close can you get to this sort of syntax in F#? I don't mind jumping through some hoops (even flaming hoops!) now if it means it'll give me something useful to DRY up code like this.
Now in F# 4.6 (preview) we have Anonymous Records
So we can have this code syntax:
It is also supported on the Visual Studio Intellisense:
So that code could be like this:
See also: Announcing F# 4.6 Preview
You can't create "anonymous records" in F# - when using types, you can either use tuples which are anonymous, but don't carry labels or you can use records which have to be declared in advance and have labels:
Technically, the problem with anonymous records is that they would have to be defined as actual classes somewhere (the .NET runtime needs a type), but if the compiler put them in every assembly, then two anonymous records with same members might be different types if they were defined in different assemblies.
Honestly, I think that the example you posted is just a poor design decision in ASP.NET - it is misusing a particular C# feature to do something for which it wasn't designed. It may not be as bad as this, but it's still odd. The library takes a C# anonymous type, but it uses it as a dictionary (i.e. it uses it just as a nice way to create key-value pairs, because the properties that you need to specify are dynamic).
So, if you're using ASP.NET from F#, it is probably easier to use an alternative approach where you don't have to create records - if the ASP.NET API provides some alternative (As Daniel shows, there is a nicer way to write that).
Here's my take on the default web project route config:
The OP does not describe the best use of anonymous type. They are best used when using LINQ to map to an arbitrary class. For example:
I know this can be done by defining a new record type, but then you have two places to maintain code, (1) the record type definition (2) where you've used it.
It could instead be done with a tuple, but to access individual fields you have to use the deconstruction syntax
(studentId, name)
all the time. This becomes unwieldy if you have 5 items in the tuple. I would rather type inx
and hit dot and have intellisense tell me what fields are available.I find it easier to do
or
In F#, I would avoid calling overloads that accept anonymous types much as I would avoid calling an F# method accepting
FSharpList
from C#. Those are language-specific features. Usually there is a language-agnostic overload/workaround available.EDIT
Just looked at the docs--here's yet another way to do it
As Tony indicated in his answer this is not much better as of F# 4.6. Testing a similar example using .NET Core SDK 3.0.100-preview4-011158 I was able to demonstrate use of the new Anonymous Record feature. As for the
RouteMap
method, I'm unfamiliar with what types of values this API accepts but I would suspect that the example below would work.Ex.
Notice the use of the
|
character on the insides of the curly braces. This is what now distinguishes regular records from anonymous records in F#.As for your other example, perhaps the F# example would now look such as below.