Имя кортежей/анонимных типов в F #?
в С# вы можете делать такие вещи, как:
var a = new {name = "cow", sound = "moooo", omg = "wtfbbq"};
и в Python вы можете делать такие вещи, как
a = t(name = "cow", sound = "moooo", omg = "wtfbbq")
Не по умолчанию, конечно, но тривиально реализовать класс t
, который позволяет вам это сделать. Infact Я сделал именно это, когда я работал с Python и нашел его невероятно удобным для небольших контейнеров с отбросами, где вы хотите иметь доступ к компонентам по имени, а не по индексу (который легко смешивать).
Помимо этой детали, они в основном идентичны кортежам в нише, которую они обслуживают.
В частности, я сейчас смотрю на этот код С#:
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 #? Я не против прыгать через некоторые обручи (даже пылающие обручи!) Сейчас, если это означает, что это даст мне что-то полезное для DRY-кода, подобного этому.
Ответы
Ответ 1
Мне легче сделать
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
из С#. Это специфические для языка функции. Обычно существует языковая агностическая перегрузка/обходной путь.
ИЗМЕНИТЬ
Просто посмотрел на документы - вот еще один способ сделать это
let inline (=>) a b = a, box b
let defaults = dict [
"controller" => "Home"
"action" => "Index"
]
route.Defaults <- RouteValueDictionary(defaults)
Ответ 2
Вы не можете создавать "анонимные записи" в 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 - это неправильное использование определенной функции С# для выполнения чего-то, для которого оно не было разработано. Возможно, это не так плохо, как , но это все еще странно. Библиотека принимает анонимный тип С#, но использует ее как словарь (т.е. Использует ее как хороший способ создания пар ключ-значение, потому что свойства, которые вам нужно указать, являются динамическими).
Итак, если вы используете ASP.NET из F #, вероятно, проще использовать альтернативный подход, в котором вам не нужно создавать записи - если ASP.NET API предоставляет некоторую альтернативу (как показывает Дэниэл, там это лучший способ написать это).
Ответ 3
OP не описывает наилучшее использование анонимного типа. Они лучше всего используются при использовании 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 сообщит мне, какие поля доступны.
Ответ 4
Теперь в F # 4.6 (превью) у нас есть анонимные записи
Таким образом, мы можем иметь этот синтаксис кода:
let AwesomeAnonymous = {| ID = Guid.NewGuid()
Name = "F#"
|}
AwesomeAnonymous.Name |> Debug.WriteLine
Он также поддерживается в Visual Studio 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 Предварительный просмотр
Ответ 5
Здесь я беру конфигурацию маршрута веб-проекта по умолчанию:
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])
Ответ 6
Как указал Тони в своем ответе, это не намного лучше, чем в F # 4.6. Тестируя аналогичный пример с использованием .NET Core SDK 3.0.100-preview4-011158, я смог продемонстрировать использование новой функции анонимной записи. Что касается метода 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" |}