У меня есть этот общий контейнер значений:
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
, хотя для этого уже есть встроенный шаблон. - Несмотря на то, что у меня есть дискриминационный союз, компилятор не может сказать мне, забыл ли я случай, потому что каждый из шаблонов - это частичные активные шаблоны.
Есть ли лучший способ?