Exception 标准ML异常和资源

Exception 标准ML异常和资源,exception,resources,ml,Exception,Resources,Ml,我希望在资源使用过程中出现任何异常时释放资源 在C++中,这个任务很简单:我把释放放进析构函数中,不管发生什么,都自动调用析构函数。在Java中使用'finally'子句。在标准ML中,相同任务的实践是什么 我试图用变量模式“e”捕获所有异常并重新引发它: datatype FileReadResult = FileReadOkay of string | FileReadError fun read_file (file_path_string : string) : FileReadRes

我希望在资源使用过程中出现任何异常时释放资源

在C++中,这个任务很简单:我把释放放进析构函数中,不管发生什么,都自动调用析构函数。在Java中使用'finally'子句。在标准ML中,相同任务的实践是什么

我试图用变量模式“e”捕获所有异常并重新引发它:

datatype FileReadResult = FileReadOkay of string | FileReadError

fun read_file (file_path_string : string) : FileReadResult =
    let
        val istream = TextIO.openIn file_path_string
            (* this file is my resource *)
    in
        TextIO.closeIn istream;
        FileReadOkay "" (* the content of the file will go here *)
        handle e => (TextIO.closeIn istream; raise e)
    end
    handle Io => FileReadError
我的编译器(MLton)接受它,但因为我是ML新手,所以我在这里要求保证这确实是正确的做法|最佳实践

由于这是一种常见的设计模式,我创建了以下实用程序函数来表达它:

(* Uses the given resource in the given way while releasing it if any exception occurs. *)
fun use_resource (resource : 'Resource) (releaser : 'Resource -> unit) (usage : unit -> 'Result) : 'Result = 
    let
        val r = usage ()
    in
        releaser resource;
        r
    end
    handle e => (releaser resource; raise e)

此函数的作用与C#中的“使用”功能相同。

是的,这是通常的模式,但有两个注意事项:

  • 内部
    句柄
    仅位于代码中的
    filereadok“
    附近,它永远不会抛出。您希望在代码的较大部分周围加上括号,以便处理程序应用于所有代码
  • 您的外部处理程序捕获
    Io
    。我想您这里指的是
    IO.IO
    ,否则您将捕获每个异常(因为
    IO
    只是一个随机的新变量)
  • 如果频繁出现,也可以尝试将其抽象为函数。类似于

    (* withTextFile : string -> (TextIO.instream -> 'a) -> 'a
    fun withTextFile name f =
        let
            val is = TextIO.openIn name
        in
            (f is before TextIO.closeIn is)
            handle e => (TextIO.closeIn is; raise e)
        end
    
    (中缀运算符
    before
    计算其左侧和右侧表达式,并返回前者的结果)。像这样使用它:

    fun echo file = withTextFile file (fn is => print(TextIO.inputAll is))
    

    是的,这是通常的模式,有两个警告:

  • 内部
    句柄
    仅位于代码中的
    filereadok“
    附近,它永远不会抛出。您希望在代码的较大部分周围加上括号,以便处理程序应用于所有代码
  • 您的外部处理程序捕获
    Io
    。我想您这里指的是
    IO.IO
    ,否则您将捕获每个异常(因为
    IO
    只是一个随机的新变量)
  • 如果频繁出现,也可以尝试将其抽象为函数。类似于

    (* withTextFile : string -> (TextIO.instream -> 'a) -> 'a
    fun withTextFile name f =
        let
            val is = TextIO.openIn name
        in
            (f is before TextIO.closeIn is)
            handle e => (TextIO.closeIn is; raise e)
        end
    
    (中缀运算符
    before
    计算其左侧和右侧表达式,并返回前者的结果)。像这样使用它:

    fun echo file = withTextFile file (fn is => print(TextIO.inputAll is))
    

    特别感谢关于Io.Io错误的警告。语义的这个“变量模式”特性似乎非常容易出错——如果我有一点输入错误,那么编译器将不会发出警告,并且会在静默中执行完全不同的操作。我想我应该始终限定类型,以避免将其解释为变量模式。我同意此功能易于出错,至少对于异常处理程序(对于正常模式,这样的错误通常会导致“冗余匹配”警告)。我更喜欢s.th,比如OCaml/Haskell解决方案,它可以区分构造函数和变量的大小写。@AndreasRossberg:Standard ML Basis Library在描述了其在值标识符中字母大小写的约定(第3页)后指出,编译器可以使用这些约定来检测这个确切的错误并发出警告。编译器似乎没有领会到这一暗示,这太糟糕了@ruakh,是的,尽管至少有一个编译器(Alice ML)会发出有关违反命名约定的警告——我知道这是因为我编写了它的前端。:)特别感谢关于Io.Io错误的警告。语义的这个“变量模式”特性似乎非常容易出错——如果我有一点输入错误,那么编译器将不会发出警告,并且会在静默中执行完全不同的操作。我想我应该始终限定类型,以避免将其解释为变量模式。我同意此功能易于出错,至少对于异常处理程序(对于正常模式,这样的错误通常会导致“冗余匹配”警告)。我更喜欢s.th,比如OCaml/Haskell解决方案,它可以区分构造函数和变量的大小写。@AndreasRossberg:Standard ML Basis Library在描述了其在值标识符中字母大小写的约定(第3页)后指出,编译器可以使用这些约定来检测这个确切的错误并发出警告。编译器似乎没有领会到这一暗示,这太糟糕了@ruakh,是的,尽管至少有一个编译器(Alice ML)会发出有关违反命名约定的警告——我知道这是因为我编写了它的前端。:)