首页 > 解决方案 > 如何使用 F# 制作同一记录的多个版本

问题描述

我有 3 个遵循类似模型的状态机:

- same states
- same calls for each state

只有这些调用的实现发生了变化。所有数据都包含在一个记录中,该记录还包含一个具有状态的 DU,并且还有一些其他常见参数。每个实现还将它们自己的字段添加到该记录中。

因此,我想弄清楚是否有一种方法可以制作我可以扩展的记录,但其中包含代码期望找到的许多常见字段。类似于类型接口的东西,但用于记录。

例如:

type State =
    | a
    | b


type StateMachine1StateObject =
    {
         State: State
         SomeData: ...
    }

type StateMachine2StateObject =
    {
         State: State
         SomeOtherKindOfData: ...
    }

我希望有一个状态机循环(这实际上是一个邮箱处理器循环)接收所有相同的消息,并且能够依赖于该类型中始终存在的某些字段,但记录本身可能不同因为它们包含自己的字段。

这怎么能结构化?看起来我不能使用带有记录的接口,也不能将多个记录合并为一个。

标签: f#

解决方案


如果您有很多共同的数据,则可以创建例如具有通用类型的字段的记录。

type State<'a> = {
    Name:     string
    Id:       int
    Extended: 'a
}

type Person = {
    FirstName: string
    LastName:  string
}

type Computer = {
    CPU: string
    Ghz: float
}

这样,您可以创建具有不同扩展数据的状态

// State<Person>
let x = {
    Name = "foo"
    Id   = 1
    Extended = {
        FirstName = "David"
        LastName  = "Raab"
    }
}

// State<Computer>
let y = {
    Name = "bar"
    Id   = 2
    Extended = {
        CPU = "AMD"
        Ghz = 3.6
    }
}

您还可以从一条记录转换为另一条记录。您的状态机仅接受包含所需数据的记录。例如,您有一个只需要Name和的状态机Id。你创造。

type StateA = {
    Name: string
    Id:   int
}

假设您现在使用共享密钥具有不同的状态。

type StateB = {
    Name:  string
    Id:    int
    Extra: float
}

而不是类型hackery,泛型等,最简单的方法就是从B转换为A。

let stateBtoA (record:StateB) : StateA = {
    Name = record.Name
    Id   = record.Id
}

所以你的状态机只接受StateA,而且你有一堆函数,其他类型必须转换成StateA才能传递给状态机。

如果由于某种原因您的状态机需要访问原始数据,您应该能够将其作为通用参数传递给函数调用。喜欢

    message (x:StateA) (y:'a) : 'a =
       // do something with x
       y

    message (stateBtoA data) data

因此,您的函数可以处理第一个参数,即 of StateA,例如返回第二个参数y,可能是StateB。只要您的函数与y. 它应该是通用的。


推荐阅读