Является ли стандартной практикой использование псевдонимов типов для указания семантики параметров? - PullRequest
13 голосов
/ 21 декабря 2011

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

Например, в этом различаемом союзе:

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

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

type SenderName = string
type RecipientName = string

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

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

Ответы [ 4 ]

11 голосов
/ 21 декабря 2011

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

Используйте псевдонимы типов: Таким образом, вы добавляете некоторую документацию, которая видна в 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} -> ...
6 голосов
/ 21 декабря 2011

Я читал мою долю оплаты 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
1 голос
/ 21 декабря 2011

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

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

Тогда Intellisense может предложить некоторую помощь. (РЕДАКТИРОВАТЬ: проголосовать по этому предложению здесь .)

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

1 голос
/ 21 декабря 2011

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

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

...