单子文件 I/O
Monadic File I/O
有很多关于如何读取和写入文件的示例,但许多帖子似乎已经过时,are too complicated, or are not 'safe' (1, 2)(它们 throw/raise 例外)。来自 Rust,我想用像 result
.
这样的 monadic 明确地处理所有错误
以下是 'safe-er' 的尝试,因为打开 read/write 不会 throw/raise。但不确定关闭是否会失败。有没有更简洁、可能更安全的方法来做到这一点?
(* opam install core batteries *)
open Stdio
open Batteries
open BatResult.Infix
let read_safe (file_path: string): (string, exn) BatPervasives.result =
(try let chan = In_channel.create file_path in Ok(chan)
with (e: exn) -> Error(e))
>>= fun chan ->
let res_strings =
try
let b = In_channel.input_lines chan in
Ok(b)
with (e: exn) -> Error(e) in
In_channel.close chan;
BatResult.map (fun strings -> String.concat "\n" strings) res_strings
let write_safe (file_path: string) (text: string) : (unit, exn) BatPervasives.result =
(try
(let chan = Out_channel.create file_path in Ok(chan))
with (e: exn) -> Error(e))
>>= fun chan ->
let res =
(try let b = Out_channel.output_string chan text in Ok(b)
with (e: exn) -> Error(e)) in
Out_channel.close chan;
res
let () =
let out =
read_safe "test-in.txt"
>>= fun str -> write_safe "test-out.txt" str in
BatResult.iter_error (fun e -> print_endline (Base.Exn.to_string e)) out
作为Janestreet工业级标准库的一部分的Stdio
库已经提供了这样的功能,当然是安全的,例如In_channel.read_all reads the contents of the file to a string and corresponding Out_channel.write_all将其写入文件,所以我们可以实现一个 cp
实用程序,
(* file cp.ml *)
(* file cp.ml *)
open Base
open Stdio
let () = match Sys.get_argv () with
| [|_cp; src; dst |] ->
Out_channel.write_all dst
~data:(In_channel.read_all src)
| _ -> invalid_arg "Usage: cp src dst"
要构建和 运行 代码,请将其放入 cp.ml
文件(最好放在一个全新的目录中),然后 运行
dune init exe cp --libs=base,stdio
此命令将 bootstrap 您的项目使用 dune。然后你可以运行你的程序
dune exec ./cp.exe cp.ml cp.copy.ml
这里是 link 到 OCaml Documentation Hub,这将使您更容易在 OCaml 中找到有趣的库。
此外,如果您想将引发异常的函数转换为 returns 错误的函数,您可以使用 Result.try_with,例如,
let safe_read file = Result.try_with @@ fun () ->
In_channel.read_all file
这是基于 的完整安全解决方案,仅使用 Base
库。
open Base
open Base.Result
open Stdio
let read_safe (file_path: string) =
Result.try_with @@ fun () ->
In_channel.read_all file_path
let write_safe (file_path: string) (text: string) =
Result.try_with @@ fun () ->
Out_channel.write_all ~data:text file_path
let () =
let out =
read_safe "test-in.txt"
>>= fun str ->
write_safe "test-out.txt" str in
iter_error out ~f:(fun e -> print_endline (Base.Exn.to_string e))
您可以在 OCaml 中读取和写入文件,而无需其他标准库。您需要的一切都已内置于 OCaml 附带的 Stdlib 中。
下面是一个读取文件的示例,同时确保文件描述符在发生异常时安全关闭:。从那里您可以编写一个类似的函数来使用相应的函数 open_out
、out_channel_length
和 output
.
来写入文件
这些以 OCaml 的 bytes
类型读写文件内容,即可变字节串。但是,它们可能会抛出异常。这可以。在 OCaml 中,异常是廉价且易于处理的。然而,有时人们出于某种原因不喜欢它们。因此,现在给抛出异常的函数添加后缀 _exn
有点惯例。所以假设你这样定义上面提到的两个函数:
val get_contents_exn : string -> bytes
val set_contents_exn : string -> bytes -> unit
现在您(或任何人)可以轻松地包装它们并 return 一个 result
值,就像 Rust 一样。但是,由于我们在 OCaml 中有多态变体,我们利用它来组合可以 return result
值的函数,如下所述:https://keleshev.com/composable-error-handling-in-ocaml
所以你可以这样包装它们:
let get_contents filename =
try Ok (get_contents_exn filename) with exn -> Error (`Exn exn)
let set_contents filename contents =
try Ok (set_contents_exn filename contents) with exn -> Error (`Exn exn)
现在这些有以下类型:
val get_contents : string -> (bytes, [> `Exn of exn]) result
val set_contents : string -> bytes -> (unit, [> `Exn of exn]) result
并且它们可以与其他函数组合在一起,return result
值具有多态变体错误通道。
我想在这里提出的一点是为您的用户提供这两种方式,这样他们就可以选择对他们有意义的方式——例外或 result
s。
有很多关于如何读取和写入文件的示例,但许多帖子似乎已经过时,are too complicated, or are not 'safe' (1, 2)(它们 throw/raise 例外)。来自 Rust,我想用像 result
.
以下是 'safe-er' 的尝试,因为打开 read/write 不会 throw/raise。但不确定关闭是否会失败。有没有更简洁、可能更安全的方法来做到这一点?
(* opam install core batteries *)
open Stdio
open Batteries
open BatResult.Infix
let read_safe (file_path: string): (string, exn) BatPervasives.result =
(try let chan = In_channel.create file_path in Ok(chan)
with (e: exn) -> Error(e))
>>= fun chan ->
let res_strings =
try
let b = In_channel.input_lines chan in
Ok(b)
with (e: exn) -> Error(e) in
In_channel.close chan;
BatResult.map (fun strings -> String.concat "\n" strings) res_strings
let write_safe (file_path: string) (text: string) : (unit, exn) BatPervasives.result =
(try
(let chan = Out_channel.create file_path in Ok(chan))
with (e: exn) -> Error(e))
>>= fun chan ->
let res =
(try let b = Out_channel.output_string chan text in Ok(b)
with (e: exn) -> Error(e)) in
Out_channel.close chan;
res
let () =
let out =
read_safe "test-in.txt"
>>= fun str -> write_safe "test-out.txt" str in
BatResult.iter_error (fun e -> print_endline (Base.Exn.to_string e)) out
作为Janestreet工业级标准库的一部分的Stdio
库已经提供了这样的功能,当然是安全的,例如In_channel.read_all reads the contents of the file to a string and corresponding Out_channel.write_all将其写入文件,所以我们可以实现一个 cp
实用程序,
(* file cp.ml *)
(* file cp.ml *)
open Base
open Stdio
let () = match Sys.get_argv () with
| [|_cp; src; dst |] ->
Out_channel.write_all dst
~data:(In_channel.read_all src)
| _ -> invalid_arg "Usage: cp src dst"
要构建和 运行 代码,请将其放入 cp.ml
文件(最好放在一个全新的目录中),然后 运行
dune init exe cp --libs=base,stdio
此命令将 bootstrap 您的项目使用 dune。然后你可以运行你的程序
dune exec ./cp.exe cp.ml cp.copy.ml
这里是 link 到 OCaml Documentation Hub,这将使您更容易在 OCaml 中找到有趣的库。
此外,如果您想将引发异常的函数转换为 returns 错误的函数,您可以使用 Result.try_with,例如,
let safe_read file = Result.try_with @@ fun () ->
In_channel.read_all file
这是基于 Base
库。
open Base
open Base.Result
open Stdio
let read_safe (file_path: string) =
Result.try_with @@ fun () ->
In_channel.read_all file_path
let write_safe (file_path: string) (text: string) =
Result.try_with @@ fun () ->
Out_channel.write_all ~data:text file_path
let () =
let out =
read_safe "test-in.txt"
>>= fun str ->
write_safe "test-out.txt" str in
iter_error out ~f:(fun e -> print_endline (Base.Exn.to_string e))
您可以在 OCaml 中读取和写入文件,而无需其他标准库。您需要的一切都已内置于 OCaml 附带的 Stdlib 中。
下面是一个读取文件的示例,同时确保文件描述符在发生异常时安全关闭:open_out
、out_channel_length
和 output
.
这些以 OCaml 的 bytes
类型读写文件内容,即可变字节串。但是,它们可能会抛出异常。这可以。在 OCaml 中,异常是廉价且易于处理的。然而,有时人们出于某种原因不喜欢它们。因此,现在给抛出异常的函数添加后缀 _exn
有点惯例。所以假设你这样定义上面提到的两个函数:
val get_contents_exn : string -> bytes
val set_contents_exn : string -> bytes -> unit
现在您(或任何人)可以轻松地包装它们并 return 一个 result
值,就像 Rust 一样。但是,由于我们在 OCaml 中有多态变体,我们利用它来组合可以 return result
值的函数,如下所述:https://keleshev.com/composable-error-handling-in-ocaml
所以你可以这样包装它们:
let get_contents filename =
try Ok (get_contents_exn filename) with exn -> Error (`Exn exn)
let set_contents filename contents =
try Ok (set_contents_exn filename contents) with exn -> Error (`Exn exn)
现在这些有以下类型:
val get_contents : string -> (bytes, [> `Exn of exn]) result
val set_contents : string -> bytes -> (unit, [> `Exn of exn]) result
并且它们可以与其他函数组合在一起,return result
值具有多态变体错误通道。
我想在这里提出的一点是为您的用户提供这两种方式,这样他们就可以选择对他们有意义的方式——例外或 result
s。