F# - "self-filling" 类型 属性

F# - "self-filling" type property

在 C# 中,我可以这样:

public class A {
    ...
    private List<int> _items;
    public List<int> Items{
        get {
            if (_items == null){
                _items = DAL.FetchFromDB();
            }
            return _items;
        }
    }
}

这样,我可以实例化class A,当我请求Items时

  1. 我保证得到它,而无需明确填写列表。
  2. 我避免重复调用数据库

F# 的等效结构是什么? 我不确定如何制作这样的类型...

我不一定要可变列表;我想知道在 F# 中执行此类操作的标准形式是什么,因为我只是在学习这门语言。

正如 ildjarn 在上面的评论中指出的那样,您可以以等效的方式使用 lazy 关键字:

type A () =
    let items = lazy (DAL.FetchFromDB ())
    member this.Items with get () = items.Value

但是,问题是它是 'idiomatic' F#,还是一般的 'good practice'?

Items 属性 不是 referentially transparent(因为它不是确定性的),函数式编程往往非常强调引用透明性。

承认:F# 不是函数式语言;它是一种功能优先语言。不过,这意味着要充分利用它,您应该在设计中采用类似的功能优先方法。

您自己的代码越实用,您从 F# 中获得的价值就越大。

您编写的 F# 代码越是命令式、隐式或面向对象,您从中获得的收益就越少。

因此,尝试将 C# 逐字翻译成 F# 代码并不总是有意义的。可以,但是你会从中得到什么吗?

最重要的是,可以将您的 C# 转换为类似于 F# 中的内容,但您应该考虑这是否是一个好主意。使用 lazy 关键字本质上没有错,但 隐含性 是我重新考虑的问题。

这是对 F# 的一对一翻译:

type A() =
    let mutable _items = null
    member this.items 
        with get() = 
            if _items = null then _items <- DAL.FetchFromDB()
            _items

let a = A()

let x = a.items  // db access here
let y = a.items

有更好(和更短)的方法可以使用对象在 F# 中编写等效代码(请参阅其他答案),但您根本不需要创建对象,您可以简单地定义一个函数和其他函数已经指出您可以使用 lazy 关键字:

let items = 
    let v = lazy DAL.FetchFromDB()
    fun () -> v.Value

let x = items()  // db access here
let y = items()

或者直接使用标准惰性值,我更喜欢这个,因为你在 items 类型中明确表示你的值是惰性计算的,而不是将它隐藏在 magic 对象 属性 或函数:

let items = lazy DAL.FetchFromDB()

let x = items.Value  // db access here
let y = items.Value

所以现在 items 的类型是 Lazy<'List<'T>>,它总是告诉你该值将被延迟计算,除此之外,在这种情况下代码实际上更短。