Записи F #: Опасные, только для ограниченного использования или хорошо используемые функции?
Итак, я получил запись в своем путешествии по F #, и сначала они кажутся довольно опасными. Сначала это казалось умным:
type Card = { Name : string;
Phone : string;
Ok : bool }
let cardA = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
Идея, что cardA сопоставляется с Картой. Не говоря уже об упрощенном сопоставлении шаблонов:
let withTrueOk =
list
|> Seq.filter
(function
| { Ok = true} -> true
| _ -> false
)
Проблема:
type Card = { Name : string;
Phone : string;
Ok : bool }
type CardTwo = { Name : string;
Phone : string;
Ok : bool }
let cardA = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
cardA теперь имеет тип CardTwo, который, как я предполагаю, связан с тем, что F # работает в порядке.
Теперь это может быть невозможной ситуацией, так как никогда не может быть случая, когда одна и та же подпись берет два типа, но это возможность.
Является ли запись того, что имеет ограниченное использование, или я просто думаю об этом?
Ответы
Ответ 1
Они не опасны, и они предназначены не только для ограниченного использования.
Я считаю очень редким, что у вас будет два типа с теми же членами. Но если вы столкнулись с такой ситуацией, вы можете определить тип записи, которую хотите использовать:
let cardA = { Card.Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
Записи очень полезны для создания (в основном) неизменяемых структур данных. И тот факт, что вы можете легко создать копию только с некоторыми измененными полями, тоже замечательный:
let cardB = { cardA with Ok = true }
Ответ 2
Согласен, записывать поля, поскольку члены охватывающего модуля/пространства имен кажутся странными, сначала исходящими из более традиционных языков OO. Но F # обеспечивает достаточную гибкость здесь. Я думаю, вы найдете только надуманные обстоятельства, вызывающие проблемы, например две записи, которые
- идентичны
- имеют отношение подмножества/надмножества
Первый случай никогда не должен происходить. Последний может быть разрешен записью B с полем записи A.
Вам нужно только одно поле для разницы для того, чтобы эти два были различимы. Помимо этого, определения могут быть одинаковыми.
type Card =
{ Name : string
Phone: string
Ok : bool }
type CardTwo =
{ Name : string
Phone: string
Age : int }
let card = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
let cardTwo = { Name = "Alf" ; Phone = "(206) 555-0157" ; Age = 21 }
Совпадение шаблонов также довольно гибко, так как вам нужно всего лишь сопоставить достаточно полей, чтобы отличить его от других типов.
let readCard card =
match card with
| { Ok = false } -> () //OK
| { Age = 21 } -> () //ERROR: 'card' already inferred as Card, but pattern implies CardTwo
Кстати, ваш сценарий легко фиксируется аннотацией типа:
let cardA : Card = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
Ответ 3
Чтобы вы оценили то, что предоставляет F #, я просто хочу упомянуть, что в OCaml нет полноценного аксессора для записей. Поэтому, чтобы различать типы записей с одинаковыми полями, вы должны поместить их в подмодули и ссылаться на них с помощью префиксов модулей.
Итак, ваша ситуация в F # намного лучше. Любая двусмысленность между подобными типами записей может быть быстро решена с использованием средства доступа к записи:
type Card = { Name: string;
Phone: string;
Ok: bool }
type CardSmall = { Address: string;
Ok: bool }
let withTrueOk list =
list
|> Seq.filter (function
| { Card.Ok = true} -> true (* ambiguity could happen here *)
| _ -> false)
Кроме того, запись F # не ограничена. Он предоставляет множество приятных функций из коробки, включая сопоставление шаблонов, неизменность по умолчанию, структурное равенство и т.д.