gpt4 book ai didi

generics - 可区分联合的通用容器上的模式匹配

转载 作者:行者123 更新时间:2023-12-03 23:49:07 24 4
gpt4 key购买 nike

我有这个通用的值容器:

open System

type Envelope<'a> = {
Id : Guid
ConversationId : Guid
Created : DateTimeOffset
Item : 'a }

我希望能够使用 模式匹配 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
| _ -> ""

请注意,在第一种情况下, xEnvelope<RecA> ,您可以看到,因为可以从 x.Item.Number 中读取值.同样,在第二种情况下, xEnvelope<RecB> .

另请注意,每个案例都需要访问 x.Id从信封开始,这就是为什么我不能只匹配 x.Item 的原因开始。

这有效,但有以下缺点:
  • 我需要定义一个局部事件模式,如 (|CaseA|_|)为了分解MyDUCaseA ,即使已经有一个内置的模式。
  • 即使我有一个 Discriminated Union,编译器也不能告诉我我是否忘记了一个 case,因为每个模式都是 Partial Active Patterns。

  • 有没有更好的办法?

    最佳答案

    这会做你想要的吗?

    open System

    type Envelope<'a> =
    { Id : Guid
    ConversationId : Guid
    Created : DateTimeOffset
    Item : 'a }

    type RecA = { Text : string; Number : int }
    type RecB = { Text : string; Version : Version }
    type MyDU = | CaseA of RecA | CaseB of RecB

    let e =
    { Id = Guid.NewGuid();
    ConversationId = Guid.NewGuid();
    Created = DateTimeOffset.MinValue;
    Item = CaseA {Text = ""; Number = 1 } }

    match e with
    | { Item = CaseA item } as x -> sprintf "%O: %s: %O" x.Id item.Text item.Number
    | { Item = CaseB item } as x -> sprintf "%O: %s: %O" x.Id item.Text item.Version

    x 是原始值,'item' 是 RecA 或 RecB。

    关于generics - 可区分联合的通用容器上的模式匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22707830/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com