我有一个Discriminated Union(“DU”)类型和一个属性OR方法,它根据DU实例计算某些东西。我试图实现一种模式,其中实例属性在第一次请求时执行计算,然后记住结果 - 类似于面向对象术语中的单例模式。
我发现这种挑战没有本地实例变量的帮助来存储事物的状态......
我尝试过对方法进行简单的memoization,但后来我遇到了没有(在实例中)存储memoized结果的问题。
注意:我的应用程序中会有很多DU类型的实例。
// Terrible mutable variable needed alongside the DU to achieve a singleton-like pattern
let mutable result: int option = None
type DU =
| Good of int
| Bad of int
with
// behaves like a Singleton
member this.GetSomething =
match result with
| None ->
printfn "Big bad side-effect to let us know it's a first time"
// big bad calculation that we only want to do once
let x = 1 + 1
// "memoize" it
result <- Some x
x
| Some y -> y
let instance = Good 1
let f1 = instance.GetSomething // first time
let f2 = instance.GetSomething // second call - no side effect1
答案 0 :(得分:5)
您无法记住不可变值的 ,因为记忆涉及更改和维护状态。
显然,你可以在外部中创建一个不可变的值:
let smth = getSomething "foo"
只要您重复使用smth
而不是再次呼叫getSomething "foo"
,您就基本上会记住结果。如果getSomething
为referentially transparent,这是安全的。否则,它不是。
从OP中发布的示例代码中,您看起来更像是lazy initialization,您可以从Lazy<T>
class获取。但是,您仍然需要一个对象来存储Lazy<T>
实例。
open System
type MyObject() =
let result =
lazy
printfn "Big bad side-effect to let us know it's a first time"
// big bad calculation that we only want to do once
1 + 1
member this.GetSomething = result.Value
正如您所看到的,在F#中,您还可以使用lazy
表达式。
> let mo = MyObject ();;
val mo : MyObject
> let smth1 = mo.GetSomething;;
Big bad side-effect to let us know it's a first time
val smth1 : int = 2
> let smth2 = mo.GetSomething;;
val smth2 : int = 2
MyObject
类可能看起来是不可变的,但这只是因为状态保存在lazy
表达式中。
答案 1 :(得分:0)
这样就可以让DU内部变得懒惰。 因为类型是懒惰的。
type DU =
| Good of Lazy<int> // Lazy not lazy
| Bad of Lazy<int>
type MyObject() =
let result =
lazy(
printfn "Big bad side-effect to let us know it's a first time"
// big bad calculation that we only want to do once
1 + 1) |> Good
member this.GetSomething = result
let mo = MyObject()
let f() =
match mo.GetSomething with
| Good x -> x.Value
| Bad y -> y.Value
f()
f()
输出
Big bad side-effect to let us know it's a first time
val it : int = 2
>
val it : int = 2