OCaml 选择部分应用程序参数
OCaml choose partial application parameter
我正在使用 OCaml 发出 http 请求。为了构造 headers,我得到了以下工作代码
let body =
let headers = Header.init () in
let headers = Header.add headers "foo" "bar" in
let uri = Uri.of_string "https://www.example.com/" in
Client.get ~headers:headers uri >>= fun (resp, body) ->
(* rest of client code irrelevant *)
然而,第 2 行和第 3 行冒犯了我(也许因为我对 OCaml 很陌生)。
所以我认为这样的事情会起作用,并且不会冒犯我脆弱的情感。
let headers = Header.init |> Header.add "foo" "bar" in
但是编译失败,因为 Header.add
的第一个 arg 应该是 Header.t
类型,我提供了一个字符串。是否有语言功能可以选择部分应用哪些参数?我已经看到 flip
可以在 Jane Street core 和 Batteries 中找到,这可以让我使用更好的语法;但是,如果我理解正确,这将翻转所有参数,我最终将首先传递值而不是键。
有什么方法可以选择部分应用哪个参数?例如,在 Scala 中,我们可以使用 'placeholder' _
;
生成一个函数
val f1 = (v1: String, v2: String, v3: String) => 42
val f2 = f1("foo", _, "baz") // f2 is a String => Int
List("a", "b", "c").map(f1(_, "bar", "baz")) // this is fine.
感谢阅读。
However, lines 2 and 3 offend me
我相信
let headers = Header.add (Header.init ()) "foo" "bar" in
比您的代码更具可读性。否则,使用不同的名称,例如
let bareheaders = Header.init () in
let headers = Header.add bareheaders "foo" "bar" in
不知道您使用的是哪个 HTTP 库,我无法解释输入错误。
您是否考虑过使用 Ocsigen 框架?
Is there a language feature to choose which args to partially apply?
首先,这些行不应该真的冒犯你,这段代码没有任何问题,你只是在提炼 header
的值。想象它是一个价值变化的历史。给同一个实体起不同的名字通常是个坏主意,因为它会混淆(阅读你的代码的人可能会假设你想要你的值的 non-linear 历史)并且是 error-prone(你可能不小心引用了错误的实体版本)。
话虽如此,当您有一长串此类作业时,它会变得很烦人(至少因为它的重复性)。在那种情况下,我通常会即时引入一个小型 DSL,例如
let (:=) property value header = Header.add header property value
现在我们可以分配几个属性,例如,
let setup = [
"foo" := "bar";
"joe" := "doe";
...
]
我们现在有了一个函数列表,我们可以很容易地将它应用到我们的header,
let init_header cmds = List.fold cmds ~init:(Header.init ()) ~f:(|>)
或者,如果我们将所有内容放在一起,
let body =
let header = init_header [
"foo" := "bar";
"joe" := "doe";
] in
...
在此特定示例中,您可以仅使用对列表而不是函数,例如,
let header = init_header [
"foo", "bar";
"joe", "doe";
] in
但是使用函数的方法效果更好,因为它可以分配不同类型的值,只要最后您可以将其细化为 header -> header
.
类型的函数
作为奖励曲目,我们刚刚实现的称为 Writer
monad :)
P.S.
Is there any way to choose which arg to partially apply? For example in Scala we can produce a function using the 'placeholder' _;
当然,使用fun
,例如
let headers =
Header.init () |> fun hdr ->
Header.add hdr "foo" "bar" |> fun hdr ->
Header.add hdr "joe" "doe" |> fun hdr ->
... in
或者,如果我们从 Scala 翻译,那么
val f2 = f1("foo", _, "baz")
将会
let f2 = fun x -> f1 "foo" x "baz"
或者,更简短地说,
let f2 x = f1 "foo" x "baz"
您似乎在使用 Cohttp
模块。您可以定义一个辅助函数 header
,它从 key/value 对的列表中构建 Header.t
:
# let header = List.fold_left (fun h x -> (Cohttp.Header.add h (fst x) (snd x)))
(Cohttp.Header.init ());;
val header : (string * string) list -> Cohttp.Header.t = <fun>
这是现有 to_list
函数的逆向函数(也许它以不同的名称存在,我找不到它)。然后就可以这样使用了:
# Cohttp.Header.to_list (header [("h1","a");("h2","b")]);;
- : (string * string) list = [("h1", "a"); ("h2", "b")]
我正在使用 OCaml 发出 http 请求。为了构造 headers,我得到了以下工作代码
let body =
let headers = Header.init () in
let headers = Header.add headers "foo" "bar" in
let uri = Uri.of_string "https://www.example.com/" in
Client.get ~headers:headers uri >>= fun (resp, body) ->
(* rest of client code irrelevant *)
然而,第 2 行和第 3 行冒犯了我(也许因为我对 OCaml 很陌生)。
所以我认为这样的事情会起作用,并且不会冒犯我脆弱的情感。
let headers = Header.init |> Header.add "foo" "bar" in
但是编译失败,因为 Header.add
的第一个 arg 应该是 Header.t
类型,我提供了一个字符串。是否有语言功能可以选择部分应用哪些参数?我已经看到 flip
可以在 Jane Street core 和 Batteries 中找到,这可以让我使用更好的语法;但是,如果我理解正确,这将翻转所有参数,我最终将首先传递值而不是键。
有什么方法可以选择部分应用哪个参数?例如,在 Scala 中,我们可以使用 'placeholder' _
;
val f1 = (v1: String, v2: String, v3: String) => 42
val f2 = f1("foo", _, "baz") // f2 is a String => Int
List("a", "b", "c").map(f1(_, "bar", "baz")) // this is fine.
感谢阅读。
However, lines 2 and 3 offend me
我相信
let headers = Header.add (Header.init ()) "foo" "bar" in
比您的代码更具可读性。否则,使用不同的名称,例如
let bareheaders = Header.init () in
let headers = Header.add bareheaders "foo" "bar" in
不知道您使用的是哪个 HTTP 库,我无法解释输入错误。
您是否考虑过使用 Ocsigen 框架?
Is there a language feature to choose which args to partially apply?
首先,这些行不应该真的冒犯你,这段代码没有任何问题,你只是在提炼 header
的值。想象它是一个价值变化的历史。给同一个实体起不同的名字通常是个坏主意,因为它会混淆(阅读你的代码的人可能会假设你想要你的值的 non-linear 历史)并且是 error-prone(你可能不小心引用了错误的实体版本)。
话虽如此,当您有一长串此类作业时,它会变得很烦人(至少因为它的重复性)。在那种情况下,我通常会即时引入一个小型 DSL,例如
let (:=) property value header = Header.add header property value
现在我们可以分配几个属性,例如,
let setup = [
"foo" := "bar";
"joe" := "doe";
...
]
我们现在有了一个函数列表,我们可以很容易地将它应用到我们的header,
let init_header cmds = List.fold cmds ~init:(Header.init ()) ~f:(|>)
或者,如果我们将所有内容放在一起,
let body =
let header = init_header [
"foo" := "bar";
"joe" := "doe";
] in
...
在此特定示例中,您可以仅使用对列表而不是函数,例如,
let header = init_header [
"foo", "bar";
"joe", "doe";
] in
但是使用函数的方法效果更好,因为它可以分配不同类型的值,只要最后您可以将其细化为 header -> header
.
作为奖励曲目,我们刚刚实现的称为 Writer
monad :)
P.S.
Is there any way to choose which arg to partially apply? For example in Scala we can produce a function using the 'placeholder' _;
当然,使用fun
,例如
let headers =
Header.init () |> fun hdr ->
Header.add hdr "foo" "bar" |> fun hdr ->
Header.add hdr "joe" "doe" |> fun hdr ->
... in
或者,如果我们从 Scala 翻译,那么
val f2 = f1("foo", _, "baz")
将会
let f2 = fun x -> f1 "foo" x "baz"
或者,更简短地说,
let f2 x = f1 "foo" x "baz"
您似乎在使用 Cohttp
模块。您可以定义一个辅助函数 header
,它从 key/value 对的列表中构建 Header.t
:
# let header = List.fold_left (fun h x -> (Cohttp.Header.add h (fst x) (snd x)))
(Cohttp.Header.init ());;
val header : (string * string) list -> Cohttp.Header.t = <fun>
这是现有 to_list
函数的逆向函数(也许它以不同的名称存在,我找不到它)。然后就可以这样使用了:
# Cohttp.Header.to_list (header [("h1","a");("h2","b")]);;
- : (string * string) list = [("h1", "a"); ("h2", "b")]