问题 人们通常如何开发Haskell模块?


对不起有点笼统的问题。我是Haskell的新手,而且我是那种通过潜入问题并在我进行中弄清楚所需的部分而学习的人。所以,我已经开始开发一个旨在进行简单线性回归的Haskell模块。这是我的目录设置:

mymod/
- mymod.cabal
- src/
-- MyMod/
--- Linear.hs
--- Linear/
---- Regression.hs
--- Data.hs
--- Data/
---- Examples.hs
- tst/

我的cabal文件如下所示:

library
  exposed-modules:     MyLib.Linear, MyLib.Linear.Classifier, 
                       MyLib.Data, MyLib.Data.Examples
  build-depends:       base == 4.6.*
  hs-source-dirs:      src

现在,我正在写作 Examples 模块,本质上是一个CSV文件解析器。看起来像这样:

module Exampels (load) where

import Text.ParserCombinators.Parsec
import Control.Applicative

examples = line `endBy` eol
line = cell `sepBy` (char ',')

cell :: GenParser Char st Double
cell = rd <$> many1 (noneOf ",\n")
    where rd = read :: String -> Double

eol = char '\n'

load :: String -> Either ParseError [[Double]]
load input = parse examples "(unknown)" input

这是我写的第一部系统。我通过使用测试了这个 ghci 和 :l Examples.hs 从 mylib/src/MyLib/Data/ 其次是 load "5\n" 并验证了结果。现在我想开始编写回归逻辑,但我想结合我已编写的CSV解析器来测试这段代码。人们通常会如何测试这样的代码?

例如,在Java中,我通常会创建一个新的包,其中包含一个包含main方法的类。使用Java,这对我来说很简单,因为我理解了类路径是如何工作的,并且可以指示编译器查找我想要运行的类。我如何在Haskell中执行此操作?

谢谢!


3082
2018-02-09 04:14


起源

ghci 搜索相对于当前目录的模块,因此快速简单的技巧就是 cd 进入你的 src 目录然后从那里加载模块。 - Gabriel Gonzalez
如果您尝试使用cabal编译,ghc将被定向到 .\src\MyLib\Data\Examples.hs 并期望一个名为的模块 MyLib.Data.Examples - user2407038
我应该命名我的文件 MyLib.Data.Examples.hs 还有? - Max
我也愿意改变我的文件结构/命名约定。这个问题是要找出Haskell社区当前的最佳实践来进行开发。例如,在Java中,我将获得一个IDE,创建一个main方法,并让IDE处理类路径。在Ruby on Rails中,我将启动一个配置为在每个请求上重新加载类的本地服务器。在Haskell,我会...? - Max
@GabrielGonzalez实际上,自1.18版以来,cabal配备齐全 cabal repl 功能,在项目上运行GHCI,同时从“cabal”文件中解析所有适当的设置。 - Nikita Volkov


答案:


基本上有四种方法:编写测试,编写可执行文件,试验REPL(GHCI)和编写基准。幸运的是,最新的Cabal(1.18)支持所有这些。另外参考我有一个项目,其中 展出一些

测试

当你需要测试一些功能时,最好的方法可能就是编写一个单元测试。随着项目的发展而累积测试是其可靠性的关键。

有三个主要框架: HUnit 用于单元测试, 快速检查 用于财产测试和 文档测试 用于测试doc注释中的示例。还有圆顶框架,如 HTF,它将HUnit和QuickCheck结合在一起,让你从一些模板中解脱出来。

在Cabal中,您可以将测试套件定义为具有各自设置的单独编译单元。 这是一个例子。然后你可以运行它们 cabal test

可执行文件

在某些情况下,测试并不能真正满足要求。标准案例是一个程序,它演示了如何使用库。 这是一个例子

您也可以使用可执行文件作为沙箱来测试库的API,但同样,更聪明的方法是编写测试。

您可以使用运行可执行文件 cabal run [name],如果需要消除歧义(即,当您有多个时),“name”指定可执行文件名称。

REPL(GHCI)

主要好处是,当您加载内部模块,运行其功能并在更新时重新加载它们时,它允许您以“实时”模式试验项目模块的API。这可能对分析API很有用,但我个人发现上面的两种方法涵盖了我可能需要的大部分内容。

你可以用你的项目运行GHCI cabal repl [name]

基准

标准 是用于基准测试的单一主导库。与上面类似,您可以在cabal中声明您的基准可执行文件 benchmark [name] 块。然后你可以运行它们 cabal bench


13
2018-02-09 07:45



谢谢!这篇文章很棒,回答了我的很多问题。 - Max
就个人而言,我一直在写作时使用ghci,用于临时测试和实验。在用其他语言开发时,我非常想念它。然而,这是一个很好的答案。 - not my job