Подтвердить что ты не робот

Является ли стандартной практикой использование псевдонимов типов для обозначения семантики параметров?

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

Например, в этом дискриминационном объединении:

type NetworkEvent =
| Message of string * string * string
| ...

Я хочу дать понять, что первый и второй элементы являются именами отправителя и получателя соответственно. Хорошая практика - сделать что-то вроде этого:

type SenderName = string
type RecipientName = string

type NetworkEvent =
| Message of SenderName * RecipientName * string
| ...

Многие библиотеки C/С++ имеют множество типов (например, win32.h), но на этих языках, хотя имена параметров необязательны во многих случаях, это все равно можно сделать. Это не относится к F #.

4b9b3361

Ответ 1

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

Использовать псевдонимы типов:. Таким образом, вы добавляете некоторую документацию, которая видна в IntelliSense, но она не распространяется через систему типов - когда вы использовали значение типа псевдонима, компилятор будет рассматривать его как string, поэтому вы не увидите всю документацию повсюду.

Использовать однофазные соединения. Это шаблон, который использовался в некоторых местах компилятора F #. Это делает информацию более заметной, чем использование псевдонимов типов, поскольку тип SenderName на самом деле отличается от типа string (с другой стороны, это может иметь небольшое ограничение производительности):

type SenderName = SenderName of string
type RecipientName = RecipientName of string
type NetworkElement =
  | Message of SenderName * RecipietName * string

match netelem with
| Message(SenderName sender, RecipientName recipiet, msg) -> ...

Использовать записи: Таким образом, вы явно определяете запись для переноса информации о случае объединения. Это более синтаксически многословно, но, вероятно, добавляет дополнительную информацию самым доступным способом. Вы по-прежнему можете использовать сопоставление образцов по записям, или вы можете использовать точечную нотацию для доступа к элементам. Также легче добавлять новые поля во время разработки:

type MessageData = 
  { SenderName : string; RecipientName : string; Message : string }
type NetworkEvent = 
  | Message of MessageData

match netelem with
| Message{ SenderName = sender; RecipientName = recipiet; Message = msg} -> ...

Ответ 2

Я прочитал свою долю в F #, как в Интернете, так и в книгах, но никогда не видел, чтобы кто-либо использовал псевдонимы в качестве формы документации. Поэтому я собираюсь сказать, что это не стандартная практика. Его также можно рассматривать как форму дублирования кода.

В общем случае конкретное представление кортежа должно использоваться только как временная структура данных внутри функции. Если вы долго сохраняете кортеж или передаете его между разными классами, тогда пришло время сделать запись.

Если вы собираетесь использовать дискриминационный союз для нескольких классов, используйте записи, как вы предложили, или сохраните все методы, охваченные дискриминационным союзом, как показано ниже.

type NetworkEvent =
    | Message of string * string * string

    static member Create(sender, recipient, message) =
        Message(sender, recipient, message)

    member this.Send() =
        math this with
        | Message(sender, recipient, message) -> 
            printf "Sent: %A" message

let message = NetworkEvent.Create("me", "you", "hi")

Вы можете использовать записи в сопоставлении с образцом, поэтому кортежи действительно являются предметом удобства и должны быть заменены записями по мере роста кода.

Если дискредитированный союз имеет кучу кортежей с одной и той же сигнатурой, тогда пора разбить его на два дискриминационных объединения. Это также помешает вам иметь несколько записей с одной и той же сигнатурой.

type NetworkEvent2 =
    | UDPMessage of string * string * string
    | Broadcast of string * string * string
    | Loopback of string * string * string
    | ConnectionRequest of string
    | FlushEventQueue

в

type MessageType =
    | UDPMessage
    | Broadcast
    | Loopback

type NetworkEvent =
    | Message of MessageType * string * string * string
    | ConnectionRequest of string
    | FlushEventQueue

Ответ 3

Я думаю, что в этом случае вам лучше использовать тип записи для первых двух элементов кортежа, чтобы укрепить тот факт, что порядок имеет значение.

Для чисел вы можете сделать что-то более элегантное, используя единицы измерения, но это не работает для строк

Ответ 4

Я не вижу в этом ничего плохого, но может раздражать 10 псевдонимов для string, например. Если это вас не беспокоит, я говорю, продолжайте. Лично мне хотелось бы, чтобы поддерживалось следующее:

type NetworkEvent =
| Message of SenderName:string * RecipientName:string * string
| ...

Тогда Intellisense может оказать некоторую помощь. (EDIT: проголосовать за это предложение здесь.)

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