在OCaml中设计大型项目

在OCaml中编写大型软件项目的最佳实践是什么?

你如何构建你的项目?

OCaml的哪些特性应该也不应该被用来简化代码管理? 例外? 一流的模块? GADTs? 对象类型?

建立系统? 测试框架? 宬?

我发现了很好的haskell建议,我认为为OCaml提供类似的服务会很好。


我将在我熟悉的条件下回答一个中等规模的项目,即100K到1M的源代码行和10个开发人员。 这是我们现在正在使用的项目,用于2013年8月两个月前开始的一个项目。

构建系统和代码组织:

  • 一个源代码可用的shell脚本为我们的项目定义了PATH和其他变量
  • 在我们项目的根目录下的一个.ocamlinit文件在启动顶层会话时会加载一堆库
  • omake,这是快速的(与平行构建-j选项); 但我们避免制作疯狂的定制omake插件
  • 一个根Makefile包含所有必需的目标(设置,构建,测试,清理等)
  • 一个级别的子目录,而不是两个
  • 大多数子目录构建到OCaml库中
  • 一些子目录包含其他东西(设置,脚本等)
  • OCAMLPATH包含项目的根源; 每个库子目录生成一个META文件,使用#require从顶层访问项目的所有OCaml部分。
  • 只为整个项目构建一个OCaml可执行文件(节省大量链接时间;仍然不知道为什么)
  • 库是通过使用opam的安装脚本安装的
  • 本地opam软件包是为不在官方opam存储库中的软件而制作的
  • 我们使用一个opam开关,这是一个以我们的项目命名的别名,避免与同一台机器上的其他项目发生冲突
  • 源代码编辑:

  • 带有opam软件包的ocma-indent和ocp-index的emacs
  • 源头控制和管理:

  • 我们使用git和github
  • 所有新代码都通过github pull请求进行同行评审
  • 非opam非github库的tarball存储在一个单独的git存储库中(如果历史记录太大,可能会被吹掉)
  • github上存在的流血边缘库被分成我们的github帐户并通过我们自己的本地opam软件包进行安装
  • 使用OCaml:

  • OCaml不会弥补糟糕的编程习惯; 教好口味超出了这个答案的范围。 http://ocaml.org/learn/tutorials/guidelines.html是一个很好的起点。
  • OCaml 4.01.0使得它比以前更容易重用记录字段标签和变体构造函数(即type t1 = {x:int} type t2 = {x:int;y:int} let t1_of_t2 ({x}:t2) : t1 = {x}现在可以工作)
  • 我们尝试在我们自己的代码中不使用camlp4语法扩展
  • 我们不使用类和对象,除非某个外部库需要它
  • 理论上,自OCaml 4.01.0以来,我们应该更喜欢经典变体而不是多变体变体
  • 我们使用异常来指示错误并让他们愉快地度过,直到我们的主服务器循环捕获它们并将它们解释为“内部错误”(默认),“错误请求”或其他内容
  • 例如Exit或Not_found等异常可以在本地使用,但在模块接口中我们更愿意使用选项。
  • 库,协议,框架:

  • 我们使用电池处理OCaml标准库中缺失的所有商品功能; 其余的我们有一个“util”库
  • 我们使用Lwt进行异步编程,没有语法扩展,绑定操作符(>> =)是我们使用的唯一操作符(如果您必须知道,我们不情愿使用camlp4预处理来更好地对绑定点进行异常跟踪)。
  • 我们使用HTTP和JSON与第三方软件进行通信,我们期望每一个现代服务都能提供这样的API
  • 为了服务HTTP,我们在nginx后面运行我们自己的SCGI服务器(ocaml-scgi)
  • 作为一个HTTP客户端,我们使用Cohttp
  • 对于JSON序列化,我们使用atdgen
  • “云”服务:

  • 我们使用了相当多的这些软件,因为它们通常便宜,易于互动,并为我们解决可伸缩性和维护问题。
  • 测试:

  • 我们有一个用于快速测试的make / omake目标,另一个用于慢速测试
  • 快速测试是单元测试; 每个模块可以提供“测试”功能; 一个test.ml文件运行测试列表
  • 慢测试是那些涉及运行多个服务的测试; 这些都是专门为我们的项目制作的,但它们尽可能地覆盖了作为生产服务。 除了我们设法不干扰生产的云服务之外,一切都在Linux或MacOS上本地运行。
  • 设置这一切是相当的工作,特别是对不熟悉OCaml的人。 目前还没有框架能够完成所有这些工作,但至少你可以选择这些工具。


    绿洲

    要添加到Pavel答案:

    免责声明:我是OASIS的作者。

    OASIS也有oasis2opam,可以帮助快速创建OPAM包和oasis2debian创建Debian包。 如果您想要创建一个“发布”目标,以自动执行上传程序包的大部分任务,这非常有用。

    OASIS还附带一个名为oasis-dist.ml的脚本,可以自动创建tarball以供上传。

    在https://github.com/ocaml.org查看所有这些内容。

    测试

    我使用OUnit来完成我所有的测试。 如果你习惯于xUnit测试,这很简单而且非常高效。

    来源控制/管理

    免责声明:我是forge.ocamlcore.org(又名forge.oo)的所有者/维护者,

    如果你想使用git,我推荐使用github。 这对审查非常有效。

    如果您使用darcs或subversion,则可以在forge.oo上创建一个帐户

    在这两种情况下都有一个公共邮件列表,您必须发送所有提交通知,以便每个人都可以看到并检查它们。 您可以在forge.oo上使用Google群组或邮寄名单

    我建议有一个很好的网页(github或forge.oo),并在每次提交时创建OCamldoc文档。 如果您有庞大的代码库,这将帮助您从一开始就使用OCamldoc生成的文档(并快速修复它)。

    当你进入稳定阶段时,我建议创建tarball。 不要只依靠检查最新的git / svn版本。 这个技巧在过去为我节省了几个小时的工作。 正如马丁所说,将所有tarball存储在一个中心位置(一个git存储库是一个好主意)。


    这可能不会完全回答你的问题,但这是我关于构建环境的经验:

    我非常欣赏OASIS。 它有一套很好的功能,不仅可以帮助构建项目,还可以编写文档和支持测试环境。

    构建系统

  • OASIS从规范( _oasis文件)生成setup.ml文件,该文件基本上用作构建脚本。 它接受-configure-build-test-distclean标志。 我在处理不同的GNU和其他通常使用Makefiles的项目时习惯了他们,我发现可以在这里自动使用它们,这很方便。
  • Makefile文件。 除了生成setup.ml ,还可以使用上述所有选项生成Makefile。
  • 结构体

    通常,由OASIS构建的项目至少有三个目录: src_buildscriptstests

  • 在前一个目录中,所有源文件都存储在一个目录中:source(.ml)和interface(.mli)文件一起存储。 可能是如果项目太大,值得引入更多的子目录。
  • _build目录受OASIS构建系统的影响。 它存储源文件和目标文件,我喜欢这些构建文件不会受到源文件的干扰,所以我可以很容易地删除它,以免出现问题。
  • 我在scripts目录中存储了多个shell脚本。 其中一些用于测试执行和接口文件生成。
  • 所有用于测试的输入和输出文件存储在单独的目录中。
  • 接口/文档

    接口文件(.mli)的使用对我来说具有优点和缺点。 它确实有助于查找类型错误,但是如果您有它们,则在对代码进行更改或改进时,也必须对它们进行编辑。 有时忘记这会导致令人讨厌的错误。

    但我喜欢接口文件的主要原因是文档。 我使用ocamldoc自动生成(OASIS支持带-doc标志的此功能)html页面。 在我看来,只需在界面中编写描述每个函数的注释就足够了,而不是在代码中插入注释。 在OCaml中,函数通常简短且简洁,如果需要在那里插入额外的注释,可能会更好地分割函数。

    还要注意ocamlc-i标志。 编译器可以自动生成模块的接口文件。

    测试

    我没有找到支持测试的合理解决方案(我想有一些ocamltest应用程序),这就是为什么我使用自己的脚本来执行和验证用例。 幸运的是,OASIS支持时执行自定义命令setup.ml与运行-test标志。

    我很长一段时间不使用OASIS,如果有人知道其他很酷的功能,我也想知道它们。

    另外,它并不知道OPAM,它绝对值得一看。 没有它,安装和管理新软件包是一场噩梦。

    链接地址: http://www.djcxy.com/p/42865.html

    上一篇: designing large projects in OCaml

    下一篇: What are the best Haskell libraries to operationalize a program?