编写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
,使用第一步里提供的信息把包装在指定的目录下后更新用户包数据库