F#:将seq <'A>转换为seq <'B>的最快方法

Eho*_*ret 2 f# casting marten

我正在将Marten用作事件存储,尤其是要获取事件流。

type AccountCreation = {
    Owner: string
    AccountId: Guid
    CreatedAt: DateTimeOffset
    StartingBalance: decimal
}

type AccountEvents =
    | AccountCreated of AccountCreation
    | AccountCredited of Transaction
    | AccountDebited of Transaction

let settings = {
    Host = "localhost"
    DatabaseName = "postgres"
    UserName = "root"
    Password = "root"
    EventTypes = eventTypes
}
use store = createDocumentStore settings
use session = store.LightweightSession()

let khalidId = Guid.NewGuid()
let billId = Guid.NewGuid()

let khalid = AccountEvents.AccountCreated({
    Owner = "Khalid Abuhakmeh"
    AccountId = khalidId
    StartingBalance = 1000m
    CreatedAt = DateTimeOffset.UtcNow
})

let bill = {
    Owner = "Bill Boga"
    AccountId = billId
    StartingBalance = 0m
    CreatedAt = DateTimeOffset.UtcNow
}

session.Events.Append(khalidId, khalid) |> ignore
session.Events.Append(billId, bill) |> ignore

session.SaveChanges()

let stream = session.Events.FetchStream()
Run Code Online (Sandbox Code Playgroud)

streamIReadOnlyList<IEvent>IEvent定义为:

public interface IEvent
{
    Guid Id { get; set; }
    int Version { get; set; }
    long Sequence { get; set; }
    object Data { get; }
    Guid StreamId { get; set; }
    string StreamKey { get; set; }
    DateTimeOffset Timestamp { get; set; }
    string TenantId { get; set; }
    void Apply<TAggregate>(TAggregate state, IAggregator<TAggregate> aggregator) where TAggregate : class, new();
}
Run Code Online (Sandbox Code Playgroud)

我想将每个属性转换IEventAccountEvents,如果Data属性的基础类型是AccountEvents(如果不是,则不会在生成的序列中生成该项目)。

在C#中,我将仅使用关键字as来实现这一目标,但在F#中,我不确定(就性能而言)最快的F#-ish方式是哪种方法。

我结束了以下代码:

let seqCastOption<'T> sequence =
    sequence
    |> Seq.map(fun x ->
        match box x with
        | :? 'T as value -> Some value
        | _ -> None)

let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
    let stream = session.Events.FetchStream(id)
    stream
    |> Seq.map(fun x -> x.Data)
    |> seqCastOption<'T>
    |> Seq.filter (fun x -> x.IsSome)
    |> Seq.map(fun x -> x.Value)
Run Code Online (Sandbox Code Playgroud)

但这似乎很“昂贵”,我想知道转换.DataOption<AccountEvents>+ 的步骤是否IsSome可以一次完成。

The*_*Fox 6

Seq.choose对于这种情况,了解rmunn答案中提到的功能非常有用,但是对于这种确切情况,我建议您使用内置的.NET方法Enumerable.OfType<'T>,它完全可以满足您的要求,并且可能已经过优化:

open System.Linq

let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
    let stream = session.Events.FetchStream(id)
    stream
    |> Seq.map(fun x -> x.Data)
    |> Enumerable.OfType<'T>
Run Code Online (Sandbox Code Playgroud)