簡體   English   中英

在Haskell中派生如何工作?

[英]How does deriving work in Haskell?

Haskell中的代數數據類型 (ADT)可以通過派生而自動成為某些類型類 (例如ShowEq )的實例。

data  Maybe a  =  Nothing | Just a
  deriving (Eq, Ord)

我的問題是,此deriving如何工作,即Haskell如何知道如何為派生的ADT實現派生的類型類的功能?

另外,為什么deriving僅限於某些類型類? 為什么我不能編寫自己的可以派生的類型類?

簡短的答案是,魔術:-)。 也就是說,自動派生已納入Haskell規范,並且每個編譯器都可以選擇以自己的方式實現它。 但是,如何使它可擴展有很多工作。

Derive是Haskell允許您編寫自己的派生機制的工具。

GHC過去提供了稱為Generic Classes的可派生類型類擴展,但由於它有些弱,因此很少使用。 現在已經將其刪除,並且正在進行整合本文所述的新的通用派生機制的工作: http : //www.dreixel.net/research/pdf/gdmh.pdf

有關更多信息,請參見:

從Haskell 98報告中:

Prelude中唯一允許派生實例的類是Eq,Ord,Enum,Bounded,Show和Read ...

這是如何派生這些類型類的描述: http : //www.haskell.org/onlinereport/derived.html#derived-appendix

可以使用模板Haskell以與派生子句類似的方式生成實例聲明。

以下示例是從Haskell Wiki偷偷竊取的:

在此示例中,我們使用以下Haskell代碼

 $(gen_render ''Body) 

產生以下實例:

 instance TH_Render Body where render (NormalB exp) = build 'normalB exp render (GuardedB guards) = build 'guardedB guards 

上面的函數gen_render定義如下。 (請注意,此代碼必須與上述用法位於單獨的模塊中)。

 -- Generate an intance of the class TH_Render for the type typName gen_render :: Name -> Q [Dec] gen_render typName = do (TyConI d) <- reify typName -- Get all the information on the type (type_name,_,_,constructors) <- typeInfo (return d) -- extract name and constructors i_dec <- gen_instance (mkName "TH_Render") (conT type_name) constructors -- generation function for method "render" [(mkName "render", gen_render)] return [i_dec] -- return the instance declaration -- function to generation the function body for a particular function -- and constructor where gen_render (conName, components) vars -- function name is based on constructor name = let funcName = makeName $ unCapalize $ nameBase conName -- choose the correct builder function headFunc = case vars of [] -> "func_out" otherwise -> "build" -- build 'funcName parm1 parm2 parm3 ... in appsE $ (varE $ mkName headFunc):funcName:vars -- put it all together -- equivalent to 'funcStr where funcStr CONTAINS the name to be returned makeName funcStr = (appE (varE (mkName "mkName")) (litE $ StringL funcStr)) 

其中使用以下功能和類型。

首先,一些類型的同義詞使代碼更具可讀性。

 type Constructor = (Name, [(Maybe Name, Type)]) -- the list of constructors type Cons_vars = [ExpQ] -- A list of variables that bind in the constructor type Function_body = ExpQ type Gen_func = Constructor -> Cons_vars -> Function_body type Func_name = Name -- The name of the instance function we will be creating -- For each function in the instance we provide a generator function -- to generate the function body (the body is generated for each constructor) type Funcs = [(Func_name, Gen_func)] 

主要的可重用功能。 我們向其傳遞函數列表以生成實例的函數。

 -- construct an instance of class class_name for type for_type -- funcs is a list of instance method names with a corresponding -- function to build the method body gen_instance :: Name -> TypeQ -> [Constructor] -> Funcs -> DecQ gen_instance class_name for_type constructors funcs = instanceD (cxt []) (appT (conT class_name) for_type) (map func_def funcs) where func_def (func_name, gen_func) = funD func_name -- method name -- generate function body for each constructor (map (gen_clause gen_func) constructors) 

以上的輔助功能。

 -- Generate the pattern match and function body for a given method and -- a given constructor. func_body is a function that generations the -- function body gen_clause :: (Constructor -> [ExpQ] -> ExpQ) -> Constructor -> ClauseQ gen_clause func_body data_con@(con_name, components) = -- create a parameter for each component of the constructor do vars <- mapM var components -- function (unnamed) that pattern matches the constructor -- mapping each component to a value. (clause [(conP con_name (map varP vars))] (normalB (func_body data_con (map varE vars))) []) -- create a unique name for each component. where var (_, typ) = newName $ case typ of (ConT name) -> toL $ nameBase name otherwise -> "parm" where toL (x:y) = (toLower x):y unCapalize :: [Char] -> [Char] unCapalize (x:y) = (toLower x):y 

還有一些借鑒了Syb III / replib 0.2的幫助程序代碼。

 typeInfo :: DecQ -> Q (Name, [Name], [(Name, Int)], [(Name, [(Maybe Name, Type)])]) typeInfo m = do d <- m case d of d@(DataD _ _ _ _ _) -> return $ (simpleName $ name d, paramsA d, consA d, termsA d) d@(NewtypeD _ _ _ _ _) -> return $ (simpleName $ name d, paramsA d, consA d, termsA d) _ -> error ("derive: not a data type declaration: " ++ show d) where consA (DataD _ _ _ cs _) = map conA cs consA (NewtypeD _ _ _ c _) = [ conA c ] {- This part no longer works on 7.6.3 paramsA (DataD _ _ ps _ _) = ps paramsA (NewtypeD _ _ ps _ _) = ps -} -- Use this on more recent GHC rather than the above paramsA (DataD _ _ ps _ _) = map nameFromTyVar ps paramsA (NewtypeD _ _ ps _ _) = map nameFromTyVar ps nameFromTyVar (PlainTV a) = a nameFromTyVar (KindedTV a _) = a termsA (DataD _ _ _ cs _) = map termA cs termsA (NewtypeD _ _ _ c _) = [ termA c ] termA (NormalC c xs) = (c, map (\\x -> (Nothing, snd x)) xs) termA (RecC c xs) = (c, map (\\(n, _, t) -> (Just $ simpleName n, t)) xs) termA (InfixC t1 c t2) = (c, [(Nothing, snd t1), (Nothing, snd t2)]) conA (NormalC c xs) = (simpleName c, length xs) conA (RecC c xs) = (simpleName c, length xs) conA (InfixC _ c _) = (simpleName c, 2) name (DataD _ n _ _ _) = n name (NewtypeD _ n _ _ _) = n name d = error $ show d simpleName :: Name -> Name simpleName nm = let s = nameBase nm in case dropWhile (/=':') s of [] -> mkName s _:[] -> mkName s _:t -> mkName t 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM