OCaml中没有where子句的长函数
在OCaml中编写以下代码的惯用方式具有更好的可读性?OCaml中没有where子句的长函数
let big_function arg =
let big_helper_fn acc = function
| p -> ...
...
...
... foo(arg)
...
...
| _ -> ...
in
let small_helper_1 a b =
...
...
in
let small_helper_2 a b =
...
...
in
fold big_function default_acc
%> small_helper_1 aa1
%> small_helper_2 aa2
吊装内功能以外可以有两个原因是不期望的:
- 人们可能需要通过几个参数明确地,而不是直接访问(用上面
foo(arg)
示出)。如果有更多的参数,这会变得很麻烦,比如需要3个参数,big_helper_fn
使用所有参数,并且累加器也是3元素的元组。 - 辅助函数在比所需范围更大的范围内变得不必要地可见。当人们仅仅撇开模块时,他们可能会分心,因为与重要的相同的压痕深度。
如果OCaml有第一类where
子句,这不会是一个问题。我确实找到了一个PPX和另一个Github repo。
请按照您的答案建议的方法,提供一本书/风格指南/大型项目的官方文档/名称的参考。
编辑:我用这个代码示例遇到的麻烦是可读性受损,因为的实际定义与顶部的原始let big_function ...
语句分开很多。所以我正在寻找一种更可读的惯用选择。
你的代码看起来已经很OCamlish了。许多大型项目都是以这种方式编写的:请参阅OCaml编译器实现本身。我喜欢在Haskell中使用where
,但是我个人反对使用非纯语言的任意where
,因为它可能会使副作用的排序非常混乱,这可能会导致很难修复的错误。将where
的定义仅限制在非扩展表达式中是可以的,但我不确定您提到的PPX是否执行这样的检查。
我从来没有做过这个,但你可以首先把“主”任务使用let rec
:
let big_function arg =
let rec go() =
fold big_helper_fn default_acc
%> small_helper_1 aa1
%> small_helper_2 aa2
and big_helper_fn acc = function
..
and small_helper_1 a b =
..
and small_helper_2 a b =
..
in
go()
或者,你可以使用本地模块:
module BigFunctionHelpers(A : sig val arg : t end) = struct
open A
let big_helper_fn acc = function ... foo(arg) ...
let small_helper_1 a b = ...
let small_helper_2 a b = ...
end
let big_function arg =
let module H = BigFunctionHelpers(struct let arg = arg end) in
let open H in
fold big_helper_fn default_acc
%> small_helper_1 aa1
%> small_helper_2 aa2
我这样做有时候在用父函数提取许多参数的本地定义时。
我问这个的原因是因为我写的代码似乎有点难以阅读,因为'big_function'的实际定义与原来的'let in'语句分开了很多。你的第二个解决方案看起来不错,但它又不像“where”那样在语法上重量轻。 – theindigamer
很难说不知道辅助函数中发生了什么,但一般来说可以将它们作为*函数提取。这有几个优点:
- 更容易阅读
- 它减少的范围变量的数量在每一点上,减少指的是错误的变量的风险(可以折叠/累加器发生)
- 这使得它可以测试内部函数
有一些缺点,如你所说:
- 将有更多*的功能,所以不可能有命名冲突(你可以使用一个模块来帮助与)
- 你需要传递更多变量明确,而不是通过关闭。我会说这是一个优势,因为这会使耦合更加明显。这是传递较少数据的机会(例如,一个字段而不是整个记录)。
关于测试的观点很有用;我没有想到这一点。虽然我不确定第二个优点,如果你无论如何都通过父范围隐式地传递参数,它并不重要。 OTOH,如果外部函数的参数没有被直接使用,那么我同意最好将较小的函数移出。 – theindigamer
什么的(%>)运算符吗? – user3240588
@ user3240588,它构成功能从左至右(而不是通常的从右到左)。 – theindigamer
我真的不明白这个问题。你的代码已经在OCaml中有效并且相当习惯。 “where”子句只会颠倒声明的顺序(这在OCaml中是非常不惯用的)。 – Drup