2011-12-26 132 views
7

的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#中有递归对象的犹太方式?

回答

14

您的代码是构建递归对象的完美方式。编译器发出警告,因为它不能保证引用在被初始化之前不会被访问(这会导致运行时错误)。但是,如果您知道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)