2011-12-21 2 views
13

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

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

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 #.

+3

В F # 3.1 они могут иметь имена. –

+0

@JamesMoore Да, это меня радует: D –

ответ

10

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

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

Исходя из вышеизложенного, это, вероятно, мнение меньшинства, но многословие записей или однократных союзов является нарушителем сделки в моем разум. Это полностью отрицает удобство и синтаксическое удобство союзов. Это похоже на проблему, которую лучше всего решить Intellisense - при условии, что существует способ аннотации полей - не изменяя ваши типы и стиль кодирования. – Daniel

+1

Я думаю, что это (в противном случае отличный ответ) может потребоваться обновление. Теперь вы можете сделать 'type X = Msg of Sender: string * Recip: string'. И вы также можете использовать эти поля: 'let x = X (Sender =" Foo ", Recip =" Bar ")'. Точно так же, как записи, но с различным синтаксисом. – Abel

1

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

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

+0

Не будет ли распространение записей столь же плохим - если не хуже, если есть много «NetworkEvents»? Вывод типа очень запутан, когда есть множество записей с одинаковыми именами параметров, и вам нужно полностью квалифицировать каждое отдельное поле. –

+0

@ReiMiyasaka - на самом деле ситуация не так уж плоха. Если вы полностью квалифицируете первое поле, которое вы указываете при создании экземпляра, остальные будут правильно исправляться. – Kit

1

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

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

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

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

+0

Да, есть тонна аспектов синтаксиса F #, которые просто делают невозможным иметь такой же богатый IntelliSense, как C#. –

+0

Недавнее улучшение, которое позволяет это - «Начиная с F # 3.1 вы можете указать отдельное поле имя, но это имя является необязательным, даже если другие поля в одном и том же случае называются». http://msdn.microsoft.com/en-us/library/dd233226.aspx –

6

Я прочитал свою долю в 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") 

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

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

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 
Смежные вопросы