美文网首页
《Real World Haskell》笔记(5):模块与库编写

《Real World Haskell》笔记(5):模块与库编写

作者: Mexplochin | 来源:发表于2019-02-08 12:52 被阅读0次
编写Json库
  • 在Haskell中用代数数据类型来表示Json数据
  • 通过模式匹配,从 JValue 中取出 Haskell 值
--file SimpleJson.hs
--以SimpleJson为例 模块整体定义如下
module SimpleJson
  (
    JValue(..)-- JValue 后的 (..) 符号表示导出 JValue 类型以及它的值构造器
    , getString
    , getInt
    , getDouble
    , getBool
    , getObject
    , getArray
    , isNull
  ) where -- where 关键字后的内容为模块的体
--模块可以只导出类型的类构造器,而不导出这个类型的值构造器,
--它允许模块对用户隐藏类型的细节,将一个类型变得抽象。
--如果用户看不见类型的值构造器,就不能使用值构造器显式创建这种类型的值,
--也不能对值进行模式匹配,只能通过相应的 API 来创建这种类型的值。

data JValue = JString String
  | JNumber Double
  | JBool  Bool
  | JNull
  | JObject [(String,JValue)]
  | JArray [JValue]
  deriving (Eq,Ord,Show)

getString::JValue->Maybe String
getString (JString s) = Just s
getString _           = Nothing

getInt (JNumber n) = Just (truncate n)
getInt _           = Nothing

getDouble (JNumber n) = Just n
getDouble _           = Nothing

getBool (JBool b) = Just b
getBool _         = Nothing

getObject (JObject o) = Just o
getObject _           = Nothing

getArray (JArray a) = Just a
getArray _          = Nothing

isNull::JValue->Bool
isNull v            = v == JNull
  • 打印Json数据,将JValue值翻译(render)成 Json 数据
    ①直接编写翻译函数,纯代码和不纯代码分离的理念非常强大
--file PutJson.hs
module PutJson where

import Data.List (intercalate)
import SimpleJson

renderJValue::JValue->String
renderJValue (JString s)=show s
renderJValue (JNumber n)=show n
renderJValue (JBool True)="true"
renderJValue (JBool False)="false"
renderJValue JNull="null"
renderJValue (JObject o)="{"++pairs o++"}"
  where pairs []=""
        pairs ps=intercalate "," (map renderPair ps)
        renderPair (k,v)=show k ++ ":"++renderJValue v
renderJValue (JArray a)="["++values a++"]"
  where values []=""
        values vs=intercalate "," (map renderJValue vs)

putJValue::JValue->IO ()
putJValue v=putStrLn (renderJValue v)
Haskell模块
  • 模块的定义必须放在其它定义之前
  • 一个 Haskell 文件可以包含一个模块定义
  • 模块定义决定模块中的哪些名字可以被外部访问
  • 模块名字必须以大写字母开头,且必须和包含这个模块的文件的基础名(不包含后缀的文件名)一致
  • 若要导出所有名字,使用module ExportEverything where
  • 若不想导出模块中的任何名字,则将导出列表留空,使用module ExportNothing () where
编译SimpleJson模块代码

使用命令ghc -c SimpleJson.hs对SimpleJson模块文件进行编译,如果省略 -c 选项,那么 ghc 就会由于试图生成完整的可执行文件而失败,因为目前的 SimpleJson.hs 没有定义 main 函数,而 ghc 在执行独立程序时会调用 main 函数。
编译完成后,生成两个新文件:

  • interface file 接口文件 后缀名.hi
    该文件以机器可读的格式保存模块中导出名字的信息
  • object file 目标文件 后缀名.o
    该文件包含已生成的机器码
载入模块和生成可执行文件

对于已编译的SimpleJson模块库,编写执行文件的程序,如下

--file Main.hs
module Main(main) where
import SimpleJson--所有 import 指令出现在模块的开头,位于其他模块代码之前
main = print (JObject [("foo", JNumber 1), ("bar", JBool False)])

要创建可执行文件, ghc 需要一个命名为 Main 的模块,并且这个模块里面还要有一个 main 函数,main 函数在程序执行时会被调用。
使用命令ghc -o simple Main.hs对Main模块文件进行编译,由于此时ghc没有使用 -c 选项,因此会尝试生成可执行程序,这个过程被称为链接;-o 选项用于指定可执行程序的名字,在 Windows 平台下,会生成一个 .exe 后缀的文件;只要提供Main.hs,ghc 可以在一条命令中自动找到所需的文件,同时进行编译和链接,然后产生可执行文件。

Haskell开发美观打印库
  • 利用undefined占位符,避免函数或类型的未定义问题,不断将占位符版本替换为可用版本
--file Prettify.hs
import SimpleJson

data Doc = ToBeDefined
  deriving (Show)

string::String->Doc
string str=undefined
-- ... ...

......

Cabal包管理
  • ghc-pkg list查看已安装包,ghc-pkg unregister卸载包
  • Cabal 将软件组织为包(package),一个包有且只能有一个库,但可有多个可执行程序
  • Cabal 要求给每个包添加描述,这些描述放在 .cabal 文件中,该文件放在包顶层目录里
  • .cabal 文件的名字和包名字相同,包含 .cabal 文件的目录名字和包名字相同
  • 包的名字必须独一无二,创建安装的包不能和系统里已经存在的某个包名字相同
  • 除了 .cabal 文件,每个包还必须包含 setup 文件,一个简单的配置文件如下
--file Setup.hs
#!/usr/bin/env runhaskell
import Distribution.Simple
main = defaultMain
  • runghc Setup configure,若不给 configure 提供任何参数,Cabal 会把包安装在系统包数据库
  • runghc Setup build,构建包
  • runghc Setup install,使用第一步里提供的信息把包装在指定的目录下后更新用户包数据库

相关文章

网友评论

      本文标题:《Real World Haskell》笔记(5):模块与库编写

      本文链接:https://www.haomeiwen.com/subject/rxonsqtx.html