2011-12-26 3 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이 구성 중에 제공된 람다 함수를 호출하지 않는다는 것을 알고 있으면 경고를 무시해도 안전합니다.

는 경고가 실제로 문제를 보여주는 예제를 제공하기 위해, 다음과 같은 코드를 시도 할 수 있습니다 :

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

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

Evil 클래스는 생성자에서 함수를 받아 건설 중에 를 호출합니다. 결과적으로 람다 함수의 재귀 참조는 값으로 설정되기 전에 e에 액세스하려고 시도합니다 (런타임 오류가 발생합니다). 그러나 특히 이벤트 핸들러를 사용하여 작업 할 때는 문제가되지 않으며 재귀 객체를 올바르게 사용할 때주의를 기울여야합니다. 당신이 경고를 없애려면

그냥 경고없이 추악한 코드와 함께, 당신은 명시 적 ref 값을 사용하고 null를 사용하여 코드를 다시 작성할 수 있습니다,하지만 당신은 런타임 오류의 동일한 위험에있을거야 :

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