Имя кортежей / анонимных типов в F #? - PullRequest
34 голосов
/ 16 ноября 2011

в C # вы можете делать такие вещи, как:

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

, а в Python вы можете делать такие вещи, как

a = t(name = "cow", sound = "moooo", omg = "wtfbbq")

Не по умолчанию, конечно, но тривиально реализоватькласс t, который позволяет вам это сделать.Фактически, я сделал именно это, когда работал с Python и обнаружил, что это невероятно удобно для небольших одноразовых контейнеров, где вы хотите иметь возможность доступа к компонентам по имени, а не по индексу (что легко смешать).

Кроме этих деталей, они в основном идентичны кортежам в нише, которую они обслуживают.

В частности, я сейчас смотрю на этот код C #:

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

и это F # эквивалент

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
  )

Что является как многословным, так и повторяющимся, не говоря уже о довольно раздражающем.Насколько близко вы можете получить такой синтаксис в F #?Я не возражаю прыгать через некоторые обручи (даже горячие обручи!) Сейчас, если это означает, что это даст мне что-то полезное для СУХОГО кода, подобного этому.

Ответы [ 6 ]

24 голосов
/ 16 ноября 2011

Мне проще сделать

let route = routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}" // URL with parameters
    )
route.Defaults.Add("controller", "Home")
route.Defaults.Add("action", "Index")

или

[ "controller", "Home"
  "action", "Index" ]
|> List.iter route.Defaults.Add

В F # я бы избегал вызова перегрузок, принимающих анонимные типы, так же, как я бы избегал вызова метода F #FSharpList из C #.Это языковые особенности.Обычно существует не зависящая от языка перегрузка / обходной путь.

РЕДАКТИРОВАТЬ

Только что посмотрел на документы - вот еще один способ сделать это

let inline (=>) a b = a, box b

let defaults = dict [
  "controller" => "Home"
  "action"     => "Index" 
]
route.Defaults <- RouteValueDictionary(defaults)
18 голосов
/ 16 ноября 2011

Вы не можете создавать «анонимные записи» в F # - при использовании типов вы можете использовать кортежи, которые являются анонимными, но не имеют меток, или вы можете использовать записи, которые должны быть объявлены заранее и имеют метки:

// 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 } 

Технически, проблема с анонимными записями состоит в том, что они должны быть определены как фактические классы где-то (для среды выполнения .NET требуется тип), но если компилятор помещает их в каждую сборку, то два Анонимные записи с одинаковыми элементами могут быть разных типов, если они были определены в разных сборках.

Честно говоря, я думаю, что приведенный вами пример - просто плохое дизайнерское решение в ASP.NET - он неправильно использует особую функцию C #, чтобы сделать что-то, для чего она не была разработана. Это может быть не так плохо, как this , но все же странно. Библиотека принимает анонимный тип C #, но использует его в качестве словаря (то есть использует ее просто как хороший способ создания пар ключ-значение, потому что свойства, которые вам нужно указать, являются динамическими).

Итак, если вы используете ASP.NET из F #, вероятно, проще использовать альтернативный подход, при котором вам не нужно создавать записи - если API ASP.NET предоставляет какую-то альтернативу (как показывает Даниэль, это хороший способ написать это).

11 голосов
/ 31 июля 2014

ОП не описывает наилучшее использование анонимного типа.Их лучше всего использовать при использовании LINQ для сопоставления с произвольным классом.Например:

var results = context.Students
              .Where(x => x.CourseID = 12)
              .Select(x => new { 
                 StudentID = x.ID, 
                 Name = x.Forename + " " + x.Surname
              });

Я знаю, что это можно сделать путем определения нового типа записи, но тогда у вас есть два места для сохранения кода: (1) определение типа записи (2), где вы использовалиЭто.

Вместо этого это можно сделать с помощью кортежа, но для доступа к отдельным полям необходимо постоянно использовать синтаксис деконструкции (studentId, name).Это становится громоздким, если в кортеже 5 предметов.Я бы лучше набрал x и нажал точку, чтобы intellisense сообщал мне, какие поля доступны.

3 голосов
/ 24 января 2019

Теперь в F # 4.6 (превью) у нас есть Анонимные записи

Таким образом, у нас может быть такой синтаксис кода:

let AwesomeAnonymous = {|  ID = Guid.NewGuid()
                           Name = "F#"
                        |}

AwesomeAnonymous.Name |> Debug.WriteLine

Он также поддерживается в VisualStudio Intellisense: Screenshot

Чтобы код мог выглядеть следующим образом:

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

См. Также: Объявление F # 4.6 Предварительный просмотр

2 голосов
/ 24 июня 2014

Вот мой пример конфигурации маршрута веб-проекта по умолчанию:

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])
1 голос
/ 10 апреля 2019

Как указал Тони в своем ответе, это не намного лучше, чем в F # 4.6. Тестирование аналогичного примера с использованием .NET Core SDK 3.0.100-preview4-011158 Мне удалось продемонстрировать использование новой функции Anonymous Record . Что касается метода RouteMap, я не знаю, какие типы значений принимает этот API, но я подозреваю, что приведенный ниже пример будет работать.

Ex.

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

Обратите внимание на использование символа | внутри фигурных скобок. Это то, что сейчас отличает обычные записи от анонимных записей в F #.

Что касается вашего другого примера, возможно, пример F # теперь будет выглядеть так, как показано ниже.

let a = {| name = "cow"; sound = "moooo"; omg = "wtfbbq" |}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...