1.
GHC.Generics与Data.Generics
RepLib库
chapter18 模板元编程 (template meta-programming)
语法树(syntax tree)
quasi-quotation 准引用
想自动导出类型类的实例于是为Haskell开发了DriFT工具来对Haskell源代码中数据类型定义的声明进行分析,预处理然后帮你你生成类型类实例的代码,一般情况下会写一个make来做这些事情
data T = MyData #if __GLASGOW_HASKELL__ >= 707 deriving (Typeable) #else instance Typeable T where ... #endif
-- Lense.hs {-# LANGUAGE TemplateHaskell #-} import Control.Lens data Name = N { _firstName :: String , _familyName :: String } deriving (Show, Eq) makeLenses ''Name --使用了makeLenses元函数生成设置器与访问器 name = N "Song" "Zhang" > name ^. familyName --访问器 “Zhang” > set familyName "song" name --设置器 N {_firstName = "Song", _familyName = "song"} > name & familyName .~ "Song" --设置器运算符 N {_firstName = "Song", _familyName = "Song"}
其中makeLenses函数会“看到”我们定义类型的结构,并且根据它了解到信息为我们自动做一些事情,生成一系列的函数与类型类实例
另外一些例子就是aeson库用来生成FromJSON与ToJSON的类型类实例,
Stream Fusion流融合
Declaration 声明,包括函数,类型,类型家族,类型类,类型类实例,运算符优先级以及结合性的声明
具体化(reify)的相关函数
使用元编程
只需要在GHCi中声明:set —dump-splices就可以了
makeTypeArity :: Name → Q [Dec]
zipN函数
zipe3 :: forall a b c.[a] -> [b] -> [c] -> [(a,b,c)] zip3 y1 y2 y3 = case ((y1, y2, y3) of (x1:xs1, x2:xs2, x3:xs3) -> (x1, x2, x3) : zip3 xs1 xs2 xs3 _ -> [] )
--ZipN.hs {-# LANGUAGE TemplatedHaskell #-} module ZipN where import Language.Haskell.TH genPE :: String -> Int -> ([Pat], [Exp]) genPE s n = let ns = [s ++ (show i) | i <- [1 .. n]] in (map (VarP . mkName) ns, map (VarE . mkName) ns) genBT :: String -> Int -> ([TyVarBndr], [Type]) genBT s n = let ns = [s ++ (show i) | i <- [1 .. n]] in (map (PlainTV . mkName) ns, map (VarT . mkName) ns)
Q monad
zips Haskell中的一个语法糖
curry3 f a b c = f (a,b,c)
绑定的级别(binding level),也就是引入时所在的级别
另一个是使用级别(using level)或称当前级别(current level),也就是使用时所在的级别
安装th-lift库
data C a b = A (B a) G
--StandaloneDeriveTest.hs {-# LANGUAGE TemplateHaskell, StandaloneDeriving, DeriveGeneric, DeriveAnyClass #-} {-# LANGUAGE KindSignatures, ConstrainKinds #-} {-# OPTIONS_GHC -ddump-splices #-} import DeriveTopdown import qualified GHC.Generics as G import qualified Data.Binary as B import qualified Data.Aeson as A import qualified Data.Data as D data C a b = a (B a) G data B a = B a | F (D a) data D b = D b | E b derivings [''Eq, ''G.Generic, ''B.Binary, ''Ord] ''C >
准引用(quasi-quotation)
原始字符串(raw string)
PCRE (Perl Compatible Regular Expression)的相关库
这就是applicative-quoters库做的事情
DSL (domain specific language)时就不必收到Haskell的语法限制
抗引用(antiquotation)意思就是这种节点不会被引用而是会被替换成其他语法,antiExprPat是这样定义
antiExpPat :: Expr -> Maybe (Q Pat) antiExprPat (Id v) = Just $ varP (mkName v) antiExprPat _= Nothing
值提供器(value provider)
Person类型,并导出我们需要的类型类实例
data Person = Person {name :: String, age :: Int} deriving (Show, Generic, FromJSON, Data)
如果我们需要一个Person的值那么就需要手写出这个表达式
song = Person "Song Zhang" 26
type provider 类型提供器
典型的例子就有th-lift 到 DeriveLift语言扩展
chapter19宏
macro macro instruction
{-# LANGUAGE DeriveGeneric, DeriveDataTypeable #-} import GHC.Generics import Data.Typeable import Data.Data data Company = C {departments :: [Department]} deriving (Show, Generic, Data, Typeable) data Department = D {departmentName :: String, manager :: Person, workers :: [Person]} deriving (Show, Generic, Data, Typeable) data Person = P {personName :: Name, gender :: Gender, age :: Age} deriving (Show, Generic, Data, Typeable) data Name = N {familyName :: String, givenNmae :: String} deriving (Show, Generic, Data, Typeable) data Gender = Male | Female deriving (Show, Generic, Data, Typeable) type Age = Int
Haskell 中的与处理函数与C语言是有些不同的
因为考虑到两者的语法的不同,使用时需要注意
GHC为条件编译提供的宏
#ifdef MIN_VERFSION_GLASGOW_HASKELL #if MIN_VERSION_GLASGOW_HASKELL(7, 10, 1, 0) -- 一些只有当GHC7.10.1才会编译的代码 #endif #endif
此外,GHC还提供os_HOST_OS与arch_HOST_ARCh 在不同的os与不同架构上的CPU做分支编译
Cabal提供的宏
MIN_VERSION_package(1,2,3)
#if MIN_VERSION_tagged(0,2,0) import Data.Proxy #else data Proxy a = Proxy ... #endif
MIN_VERSION_,pkgname.宏
由GHC内置,使用-package或-packag-id会定义MIN_VERSION_<pkgname>宏
V 工程篇
P213
prop_even x y = even (x+y) == (even x == even y)
属性 reverse (xs ++ ys) == reverse ys ++ reverse xs
quickCheck会首先检查列表为空的情况,这些特殊情况也正是定义函数时容易出错的清醒
基于栈计算器一节中帝国一的scanner函数
需要为scanner生成大量的合法的算术表达式来测试scanner函数是否正确
oneof函数来随机选取一个生成
instance Arbitrary Exp where arbitrary = do n <- choose (0, 2) :: Gen Int case n of 0 -> do unaryOp <- elements ["", "+", "-", "sin"] (Exp exp) <- arbitrary :: Gen Exp return (Exp (unaryOp ++ exp)) 1 -> do Exp exp <- arbitrary :: Gen Exp let bracketExp = "(" ++ exp ++ ")" ...
promote :: (a → Gen b) → Gen (a → b)
variant 与 (><)函数。它实际可以用含糊符合运算符(.)代替
穷举测试
{-# LANGUAGE FlexibleInstances, MultiParamTypeCla\sses, DeriveGeneric #-} import Test.SmallCheck import Test.SmallCheck.Series import GHC.Generics import Data.Functor.Identity data Exp = Val Bool | And Exp Exp | Or Exp Exp deriving (Show, Eq, Generic) eval : Exp -> Bool ...