将OCaml转换为F#:F#等于Pervasives at_exit

时间:2012-09-15 23:57:45

标签: events f# ocaml

我正在将OCaml Format模块转换为F#,并将问题追溯到使用OCaml Pervasives at_exit。

val at_exit : (unit -> unit) -> unit

在程序终止时注册要调用的给定函数。当程序正常执行退出或终止时,将调用at_exit注册的函数,或者由于未被捕获例外。这些函数以“后进先出”顺序调用:最近添加at_exit的函数首先被调用。

在转换过程中,我注释掉了这一行,因为编译器没有将它标记为需要,我没想到代码中有一个事件。

我使用VS对象浏览器检查了FSharp.PowerPack.Compatibility.PervasivesModule at_exit并找不到。

我找到了how to run code "at_exit"?How do I write an exit handler for an F# application?

OCaml系列是

at_exit print_flush 

使用print_flush签名:val print_flush : (unit -> unit)

另外,在OCaml代码的调试会话期间查看它的使用时,看起来在初始化结束时和每次使用模块调用结束时都会调用at_exit

有关如何执行此操作的任何建议和提示。这将是我在F#中的第一个活动。

编辑

以下是我所学到的关于格式模块的一些知识,可以解释这个问题。

Format模块是一个函数库,用于简单OCaml值的基本漂亮打印机命令,如int,bool,string。格式模块具有print_string之类的命令,但也有一些命令可以将下一行放在有界框中,考虑新的左右边距集。所以可以写:

print_string "Hello"

open_box 0; print_string "<<";
open_box 0; print_string "p \/ q ==> r"; close_box();
print_string ">>"; close_box()

open_boxprint_string等命令由循环处理,该循环解释命令,然后决定在当前行上打印或前进到下一行。命令保存在队列中,并且有一个状态记录来保存可变值,例如左边距和右边距。

需要启动队列和状态,从调试测试用例到工作的OCaml代码似乎是在模块初始化结束时但在第一次调用Format模块中的任何函数之前完成的。通过使用at_exit机制来清除和再次为下一组命令启动队列和状态,这些机制识别出对格式模块的初始调用的最后一个匹配帧已被删除,从而触发对at_exit,它会推出队列中剩余的任何命令并重新初始化队列和状态。

因此,调用print_flush的顺序非常重要,似乎不仅仅是OCaml文档所说的内容。

1 个答案:

答案 0 :(得分:2)

这应该这样做:

module Pervasives =
    open System
    open System.Threading

    //
    let mutable private exitFunctions : (unit -> unit) list = List.empty

    //
    let mutable private exitFunctionsExecutedFlag = 0

    //
    let private tryExecuteExitFunctions _ =
        if Interlocked.CompareExchange (&exitFunctionsExecutedFlag, 1, 0) = 0 then
            // Run the exit functions in last-in-first-out order.
            exitFunctions
            |> List.iter (fun f -> f ())

    // Register handlers for events which fire when the process exits cleanly
    // or due to an exception being thrown.
    do
        AppDomain.CurrentDomain.ProcessExit.Add tryExecuteExitFunctions
        AppDomain.CurrentDomain.UnhandledException.Add tryExecuteExitFunctions

    //
    let at_exit f =
        // TODO : This function should be re-written using atomic operations
        // for thread-safety!
        exitFunctions <- f :: exitFunctions

还有一些代码来测试它:

open System

// Register a couple of handlers to test our code.
Pervasives.at_exit <| fun () ->
    Console.WriteLine "The first registered function has fired!"

Pervasives.at_exit <| fun () ->
    Console.WriteLine "The second registered function has fired!"
    TimeSpan.FromSeconds 1.0
    |> System.Threading.Thread.Sleep
    Console.WriteLine "Exiting the second registered function!"

Pervasives.at_exit <| fun () ->
    Console.WriteLine "The third registered function has fired!"

// Do some stuff in our program
printfn "blah"
printfn "foo"
printfn "bar"

(* The functions we registered with at_exit should be fired here. *)

// Uncomment this to see that our handlers work even when the
// program crashes due to an unhandled exception.
//failwith "Uh oh!"