haskell 模块化

Haskell模块化

  1. 模块名和文件基础名一致
  2. 用括号包围的是导出列表(list of exports)。 where 关键字之后的内容为模块的体。
-- file: ch05/SimpleJSON.hs
module SimpleJSON
    (
        JValue(..)
    ,   getString
    ,   getInt
    ,   getDouble
    ,   getBool
    ,   getObject
    ,   getArray
    ,   isNull
    ) where

跟在 JValue 之后的 (..) 符号表示导出 JValue 类型以及它的所有值构造器。
事实上,模块甚至可以只导出类型的名字(类构造器),而不导出这个类型的值构造器。这种能力非常重要:它允许模块对用户隐藏类型的细节,将一个类型变得抽象。如果用户看不见类型的值构造器,他就没办法对类型的值进行模式匹配,也不能使用值构造器显式创建这种类型的值

  1. 如果省略掉模块定义中的导出部分,那么所有名字都会被导出:
module ExportEverything where
  1. 如果不想导出模块中的任何名字,那么可以将导出列表留空,仅保留一对括号:
module ExportNothing () where

编译

$ ghc -c SimpleJSON.hs
$ ls
SimpleJSON.hi  SimpleJSON.hs  SimpleJSON.o

-c 表示让 ghc 只生成目标代码。如果省略 -c 选项,那么 ghc 就会试图生成一个完整的可执行文件,这会失败,因为目前的 SimpleJSON.hs 还没有定义 main 函数,而 GHC 在执行一个独立程序时会调用这个 main 函数。

在编译完成之后,会生成两个新文件。其中 SimpleJSON.hi 是接口文件(interface file), ghc 以机器可读的格式,将模块中导出名字的信息保存在这个文件。而 SimpleJSON.o 则是目标文件(object file),它包含了已生成的机器码。

-- file: ch05/Main.hs
module Main (main) where
import SimpleJSON
main = print (JObject [("foo", JNumber 1), ("bar", JBool False)])

Main.hs 的名字和 main 函数的命名是有特别含义的,要创建一个可执行文件, ghc 需要一个命名为 Main 的模块,并且这个模块里面还要有一个 main 函数,而 main 函数在程序执行时会被调用。

ghc -o simple Main.hs

包管理

ghc-pkg 命令来查看包数据库
###
* 数据库,是因为 GHC 区分所有用户都能使用的系统包(system-wide packages)和只有当前用户才能使用的用户包(per-user packages)。 用户数据库(per-user database)是我们没有管理员权限也可以安装包
* ghc-pkg list 列出已安装的包

创建,安装包

  • .cabal
  • Setup.hs
-- file: ch05/Setup.hs
#!/usr/bin/env runhaskell
import Distribution.Simple
main = defaultMain
  • 配置,构建,安装
runghc Setup configure --prefix=$HOME --user
runghc Setup build
runghc Setup install

如果我们不给 configure 提供任何参数,Cabal 会把我们的包安装在系统包数据库里。如果想安装在指定目录下和用户包数据库内,我们需要提供更多的信息。