将 "abstract" 方法添加到(F#)记录

Add an "abstract" method to (an F#) record

我创建了以下记录以尝试将 C# class 转换为 F#:

    type Days = Days of int
    type Value = Value of int
    type Item = {
        Name: string
        Expires: Days
        Value: Value
    }

我还需要每个项目都有一个... "way" 到 运行 另一个尚未定义的函数 handleDevalue,它作用于项目本身以操纵项目的 Value 值。

handleDevalue 函数依赖于项目的 Expires 属性,因此每个项目的实现都会不同,唯一的公共线程是函数的名称和签名 (Item -> Item).
在我正在翻译的 C# 代码中,此方法在 Item class 上定义为 abstract 并在实例化的每个项目上被覆盖(其中每个项目都是子 class 继承自Item).

我试过的,不成功到现在:

  1. 在记录上添加抽象方法:...} with abstract handleDevalue: Item -> Item.
    1.1 失败原因:IDE告诉我"abstract can't be added here as an augmentation"(或接近相同效果的东西)。 (我对 F# 的了解还不够,甚至不知道它是什么意思,但编译器不会让它编译,所以...不)。
  2. 在记录中添加 handleDevalue 作为函数:{... HandleDevalue: Item -> Item...}.
    2.1. 失败原因:此功能依赖Expires属性。显然,记录的字段是相互独立的,此外......函数 "know" 将如何作用于哪个项目(它应该作用于项目本身)?当 "instantiating" 一条记录时(即没有 {...handleDevalue = fun this -> <some implementation code here>)实现函数时不允许使用 this 关键字。
  3. 我记得在我创建的每个项目上定义函数(无论如何我应该),但这并没有使用类型系统对我有利。
    我希望编译器强制我实现该功能并在我不执行时提醒我。

由于这些方法都失败了,我不知道如何前进。

感谢您提前提出任何建议。

我不太确定你想在这里完成什么,但我会试一试。

为什么不做类似的事情,使用有区别的联合而不是继承?

type Days = Days of int
type Value = Value of int
type Item = {
    name: string
    expires: Days
    value: Value
}
type ItemType =
| FooItem of Item
| BarItem of Item
| BazItem of Item
// ...
let deValue item =
    match item with
    | FooItem i ->
        {
             name = i.name
             expires = i.expires -1
             value = i.value -1
        } |> FooItem
    | BarItem i ->
        {
             name = i.name
             expires = i.expires -1
             value = i.value -10
        } |> BarItem
    | // etc

想想 C# 程序中实际发生了什么。

您有多个不同的 handleDevalue 实现,每个项目实例都有其中一个与之关联的实现。但是,是什么决定了哪个与哪个项目相配呢?好吧,这是由该项目的特定后代 class 决定的。好的,但是什么决定了实例化哪个后代 class?

某个地方,某个时间点,一定有一个地方以某种方式挑选后代class。让我们假设它看起来像这样:

class Item { public abstract int handleDevalue() { ... } }

class FooItem : Item { public override int handleDevalue() { ... } }

class BarItem : Item { public override int handleDevalue() { ... } }

public Item createItem(string name, Days expires, Value value) {
    if (foo) return new FooItem(name, expires, value)
    else return BarItem(name, expires, value)
}

所以,看看发生了什么:最终,创建项目的人正在选择最终使用哪个 handleDevalue 实现,然后该实现附加到项目通过 table.

方法实例

现在我们知道了这一点,我们可以在 F# 中做同样的事情。我们只需要明确附加实现即可:

type Item = {
    Name : string
    Expires : Days
    Value : Value
    handleDevalue : Item -> Item
}

let handleDevalueFoo item = ...
let handleDevalueBar item = ...

let createItem name expires value = {
    Name = name
    Expires = expires
    Value = value
    handleDevalue = if foo then handleDevalueFoo else handleDevalueBar
}