У меня есть этот общий контейнер значений:
open System
type Envelope<'a> = {
Id : Guid
ConversationId : Guid
Created : DateTimeOffset
Item : 'a }
Я хотел бы иметь возможность использовать Match Matching в Item, сохраняя при этом значения огибающей.
В идеале я хотел бы сделать что-то вроде этого:
let format x =
match x with
| Envelope (CaseA x) -> // x would be Envelope<RecA>
| Envelope (CaseB x) -> // x would be Envelope<RecB>
Однако это не работает, поэтому я задаюсь вопросом, есть ли способ сделать что-то вроде этого?
Дополнительная информация
Предположим, что у меня есть следующие типы:
type RecA = { Text : string; Number : int }
type RecB = { Text : string; Version : Version }
type MyDU = | CaseA of RecA | CaseB of RecB
Я хотел бы иметь возможность объявлять значения типа Envelope<MyDU> и по-прежнему иметь возможность совпадения с содержащимся Item.
Возможно, это происходит по неверному касанию, но я сначала попытался использовать функцию отображения для конвертов:
let mapEnvelope f x =
let y = f x.Item
{ Id = x.Id; ConversationId = x.ConversationId; Created = x.Created; Item = y }
Эта функция имеет подпись ('a -> 'b) -> Envelope<'a> -> Envelope<'b>, поэтому она выглядит как-то, что мы видели раньше.
Это позволяет мне определить этот Частичный активный шаблон:
let (|Envelope|_|) (|ItemPattern|_|) x =
match x.Item with
| ItemPattern y -> x |> mapEnvelope (fun _ -> y) |> Some
| _ -> None
и эти вспомогательные частичные активные шаблоны:
let (|CaseA|_|) = function | CaseA x -> x |> Some | _ -> None
let (|CaseB|_|) = function | CaseB x -> x |> Some | _ -> None
С этими строительными блоками я могу написать такую функцию, как этот:
let formatA (x : Envelope<RecA>) = sprintf "%O: %s: %O" x.Id x.Item.Text x.Item.Number
let formatB (x : Envelope<RecB>) = sprintf "%O: %s: %O" x.Id x.Item.Text x.Item.Version
let format x =
match x with
| Envelope (|CaseA|_|) y -> y |> formatA
| Envelope (|CaseB|_|) y -> y |> formatB
| _ -> ""
Обратите внимание, что в первом случае x является Envelope<RecA>, который вы можете видеть, потому что можно прочитать значение off x.Item.Number. Аналогично, во втором случае x есть Envelope<RecB>.
Также обратите внимание, что для каждого случая требуется доступ к x.Id из конверта, поэтому я не могу просто совместить с x.Item для начала.
Это работает, но имеет следующие недостатки:
- Мне нужно определить Partial Active Pattern, например
(|CaseA|_|), чтобы разложитьMyDUнаCaseA, хотя для этого уже есть встроенный шаблон. - Несмотря на то, что у меня есть дискриминационный союз, компилятор не может сказать мне, забыл ли я случай, потому что каждый из шаблонов - это частичные активные шаблоны.
Есть ли лучший способ?