Name Tuples/Anonymous Types in F#?

后端 未结 6 1629
执笔经年
执笔经年 2021-02-03 19:46

in C# you can do stuff like :

var a = new {name = \"cow\", sound = \"moooo\", omg = \"wtfbbq\"};

and in

相关标签:
6条回答
  • 2021-02-03 20:26

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

    0 讨论(0)
  • 2021-02-03 20:28

    Here's my take on the default web project route config:

    module RouteConfig =
    
        open System.Web.Mvc
        open System.Web.Routing
    
        let registerRoutes (routes: RouteCollection) =
    
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
    
            /// create a pair, boxing the second item
            let inline (=>) a b = a, box b
    
            /// set the Defaults property from a given dictionary
            let setDefaults defaultDict (route : Route) =  
                route.Defaults <- RouteValueDictionary(defaultDict)
    
            routes.MapRoute(name="Default", url="{controller}/{action}/{id}")
            |> setDefaults (dict ["controller" => "Home" 
                                  "action" => "Index" 
                                  "id" => UrlParameter.Optional])
    
    0 讨论(0)
  • 2021-02-03 20:29

    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)
    
    0 讨论(0)
  • 2021-02-03 20:30

    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.

    0 讨论(0)
  • 2021-02-03 20:36

    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.

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        {| controller = "Home"; action = "Index"; id = UrlParameter.Optional |} // Parameter defaults
      )
    

    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.

    let a = {| name = "cow"; sound = "moooo"; omg = "wtfbbq" |}
    
    0 讨论(0)
  • 2021-02-03 20:46

    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

    0 讨论(0)
提交回复
热议问题