F #: практическое правило использования определенных типов / псевдонимов? - PullRequest
1 голос
/ 31 мая 2019

Я создаю своего рода «конверт» для отслеживания того, что (команды и события) я отправляю через RabbitMQ в приложении следующим образом:

type Envelope<'T> = {
    Id: Guid
    CausationId: Guid
    CorrelationId: Guid
    Payload: 'T
    Timestamp: DateTimeOffset
}

'T обычно является командой или событием вида:

type Commands =
    | DoA of Stuff
    | DoB of Stuff
    | DoC of Stuff

type Events =
    | AHappened of Stuff
    | BHappened of Stuff
    | CHappened of Stuff

Примечание: я целенаправленно использую относительно общие имена

Однако я мог бы также использовать определенные типы:

type CausationId = CausationId of Guid
type CorrelationId = CorrelationId of Guid
type CommandId = CommandId of Guid
type EventId = EventId of Guid
type Timestamp = Timestamp of DateTimeOffset

type DataToDoA = DataToDoA of Stuff
type DataToDoB = DataToDoB of Stuff
type DataToDoC = DataToDoC of Stuff

type Commands =
    | DoA of DataToDoA
    | DoB of DataToDoB
    | DoC of DataToDoC

type DataLeftByA = DataLeftByA of Stuff
type DataLeftByB = DataLeftByB of Stuff
type DataLeftByC = DataLeftByC of Stuff

type Events =
    | AHappened of DataLeftByA
    | BHappened of DataLeftByB
    | CHappened of DataLeftByC

Что привело бы к:

type CommandEnvelope<`T> = {
    Id: CommandId
    CausationId: CausationId
    CorrelationId: CorrelationId
    Payload: `T
    Timestamp: Timestamp
}

type EventEnvelope<`T> = {
    Id: EventId
    CausationId: CausationId
    CorrelationId: CorrelationId
    Payload: `T
    Timestamp: Timestamp
}

Есть ли эмпирическое правило, определяющее, когда использовать эти конкретные типы / псевдонимы-псевдонимы?

Ответы [ 2 ]

3 голосов
/ 31 мая 2019

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

Допустим, у вас есть только Stuff и:

type DataLeftByA = DataLeftByA of Stuff
type DataLeftByB = DataLeftByB of Stuff

type Events =
    | AHappened of DataLeftByA
    | BHappened of DataLeftByB

Одна вещь, которую вы можете сделать, это написать функцию:

let processDataA (DataLeftByA stuff) = (...)

Эта функция принимает DataLeftByA, содержащую некоторое количество Stuff.Однако тип проясняет, что функцию следует использовать только для событий, вызванных A.Следующее будет ошибкой типа:

let handleEvent = function
    | AHappened adata -> processDataA adata
    | BHappened bdata -> processDataA bdata // Type error here!

Если вы определили свой Events как просто события, совпадающие с Stuff:

type Events =
    | AHappened of Stuff
    | BHappened of Stuff

Затем данные, которые переносят ваши событиято же самое, но вы теряете возможность определять такую ​​функцию, как processDataA, потому что нет отдельного типа для данных, переносимых событием A.Вы можете просто определить processStuff, но это может быть вызвано как в случае A, так и B.

Я думаю, что это единственное, что делает реальное практическое различие между двумя версиями.Итак, эмпирическое правило будет таким: нужно ли вам когда-либо определять функцию, например processDataA или нет?

2 голосов
/ 31 мая 2019

Скотт Влашин из F # для Fun и Profit немного говорит об этом в своей серии «Проектирование с типами». В посте об объединяющих типах для единственного случая он объясняет, что одна из основных причин использования единственных типов для объединений - это когда вы хотите добавить некоторую логику проверки. Это позволяет уточнить ваш тип немного больше.

Пример, который он приводит, - электронная почта. Вы можете иметь type Person = { ... Email: string ... }, но это не дает никакой гарантии, что строка является адресом электронной почты. Сделав новый тип для него type EmailAddress = Email of string, вы сможете создавать адреса электронной почты только из тех функций, которые сначала его проверяют. Это может быть хорошей проверкой работоспособности и помогает убедиться, что вы не назначаете адреса электронной почты вещам, которые не являются адресами / ссылками.

TL; DR Типы объединения с одним регистром полезны, когда вы хотите добавить дополнительную семантику / проверку в ваш код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...