F#中的递归对象?

时间:2011-12-26 14:30:32

标签: recursion f#

这段F#代码

    let rec reformat = new EventHandler(fun _ _ ->
        b.TextChanged.RemoveHandler reformat
        b |> ScrollParser.rewrite_contents_of_rtb
        b.TextChanged.AddHandler reformat
        )
    b.TextChanged.AddHandler reformat

会产生以下警告:

  

traynote.fs(62,41):警告FS0040:通过使用延迟引用,将在运行时检查对正在定义的对象的这种和其他递归引用的初始化 - 健全性。这是因为您定义了一个或多个递归对象,而不是递归函数。使用'#nowarn“40”'或'--nowarn:40'可以抑制此警告。

是否有一种方法可以重写代码以避免此警告?或者在F#中没有犹太人使用递归对象的方法吗?

1 个答案:

答案 0 :(得分:15)

您的代码是构造递归对象的完美方式。编译器发出警告,因为它无法保证在初始化之前不会访问该引用(这会导致运行时错误)。但是,如果您知道EventHandler在构造期间没有调用提供的lambda函数(它没有),那么您可以安全地忽略该警告。

要举例说明警告确实显示问题,您可以尝试以下代码:

type Evil(f) =
  let n = f() 
  member x.N = n + 1

let rec e = Evil(fun () -> 
  printfn "%d" (e:Evil).N; 1)

Evil类在构造函数中接受一个函数,并在构造期间将其命名为 。因此,lambda函数中的递归引用在设置为值之前尝试访问e(并且您将获得运行时错误)。但是,尤其是在处理事件处理程序时,这不是问题(当您正确使用递归对象时,您会收到警告。)

如果你想摆脱警告,你可以使用显式ref值并使用null重写代码,但是你会遇到运行时错误的危险,只是没有警告和丑陋的代码:

let foo (evt:IEvent<_, _>) = 
  let eh = ref null
  eh := new EventHandler(fun _ _ -> 
    evt.RemoveHandler(!eh) )
  evt.AddHandler(!eh)