问题描述
我正在使用 OCaml 发出 http 请求。为了构建标题,我最终得到了以下工作代码
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
类型,并且我提供了一个字符串。是否有语言功能可以选择部分应用哪些参数?我已经看到可以在 Jane Street core 和 Batteries 中找到的 flip
可以让我使用更好的语法;但是,如果我理解正确,这将翻转所有参数,我最终将首先传递值而不是键。
有没有办法选择部分应用哪个arg?例如,在 Scala 中,我们可以使用“占位符”_
;
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.
感谢阅读。
解决方法
然而,第 2 行和第 3 行冒犯了我
我相信
let headers = Header.add (Header.init ()) "foo" "bar" in
比您的代码更具可读性。否则,使用不同的名称,例如
let bareheaders = Header.init () in
let headers = Header.add bareheaders "foo" "bar" in
如果不知道您使用的是哪个 HTTP 库,我无法解释输入错误。
您是否考虑过使用 Ocsigen 框架?
是否有语言功能可以选择部分应用哪些参数?
,首先,这些行不应该真的冒犯到您,这段代码没有任何问题,您只是在完善 header
的值。想象它是一个价值变化的历史。给同一个实体赋予不同的名称通常是一个坏主意,因为它会造成混淆(阅读您代码的人可能会假设您想要值的非线性历史)并且容易出错(您可能会不小心引用错误的版本)实体)。
话虽如此,当你有一个很长的这样的任务序列时,它会变得很烦人(至少因为它的重复性)。在这种情况下,我通常会即时引入一个小型 DSL,例如,
let (:=) property value header = Header.add header property value
现在我们可以分配几个属性,例如,
let setup = [
"foo" := "bar";
"joe" := "doe";
...
]
我们现在有一个函数列表,我们可以很容易地将它应用到我们的标题中,使用
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 :)
附言
有没有办法选择部分应用哪个arg?例如,在 Scala 中,我们可以使用“占位符” _ 生成一个函数;
当然,使用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
,用于从键/值对列表构建 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")]