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

编写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,使用第一步里提供的信息把包装在指定的目录下后更新用户包数据库
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容