Name Tuples/Anonymous Types in F#?
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:
var results = context.Students
.Where(x => x.CourseID = 12)
.Select(x => new {
StudentID = x.ID,
Name = x.Forename + " " + x.Surname
});
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 in x
and hit dot and have intellisense tell me what fields are available.
I find it easier to do
let route = routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}" // URL with parameters
)
route.Defaults.Add("controller", "Home")
route.Defaults.Add("action", "Index")
or
[ "controller", "Home"
"action", "Index" ]
|> List.iter route.Defaults.Add
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
let inline (=>) a b = a, box b
let defaults = dict [
"controller" => "Home"
"action" => "Index"
]
route.Defaults <- RouteValueDictionary(defaults)
Now in F# 4.6 (preview) we have Anonymous Records
So we can have this code syntax:
let AwesomeAnonymous = {| ID = Guid.NewGuid()
Name = "F#"
|}
AwesomeAnonymous.Name |> Debug.WriteLine
It is also supported on the Visual Studio Intellisense:
So that code could be like this:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
{| controller = "Home"; action = "Index"; id = UrlParameter.Optional |} // Parameter defaults
)
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:
// Creating an anonymous tuple
let route = ("Home", "Index", UrlParameter.Optional)
// Declaration and creating of a record with named fields
type Route = { controller : string; action : string; id : UrlParameter }
let route = { controller = "Home"; action = "Index"; id = UrlParameter.Optional }
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).