简体   繁体   English

在Haskell中派生如何工作?

[英]How does deriving work in Haskell?

Algebraic Data Types (ADTs) in Haskell can automatically become instances of some typeclasse s (like Show , Eq ) by deriving from them. Haskell中的代数数据类型 (ADT)可以通过派生而自动成为某些类型类 (例如ShowEq )的实例。

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

My question is, how does this deriving work, ie how does Haskell know how to implement the functions of the derived typeclass for the deriving ADT? 我的问题是,此deriving如何工作,即Haskell如何知道如何为派生的ADT实现派生的类型类的功能?

Also, why is deriving restricted to certain typeclasses only? 另外,为什么deriving仅限于某些类型类? Why can't I write my own typeclass which can be derived? 为什么我不能编写自己的可以派生的类型类?

The short answer is, magic :-). 简短的答案是,魔术:-)。 This is to say that automatic deriving is baked into the Haskell spec, and every compiler can choose to implement it in its own way. 也就是说,自动派生已纳入Haskell规范,并且每个编译器都可以选择以自己的方式实现它。 There's lots of work on how to make it extensible however. 但是,如何使它可扩展有很多工作。

Derive is a tool for Haskell to let you write your own deriving mechanisms. Derive是Haskell允许您编写自己的派生机制的工具。

GHC used to provide a derivable type class extension called Generic Classes , but it was rarely used, as it was somewhat weak. GHC过去提供了称为Generic Classes的可派生类型类扩展,但由于它有些弱,因此很少使用。 That has now been taken out, and work is ongoing to integrate a new generic deriving mechanism as described in this paper: http://www.dreixel.net/research/pdf/gdmh.pdf 现在已经将其删除,并且正在进行整合本文所述的新的通用派生机制的工作: http : //www.dreixel.net/research/pdf/gdmh.pdf

For more on this, see: 有关更多信息,请参见:

From the Haskell 98 report: 从Haskell 98报告中:

The only classes in the Prelude for which derived instances are allowed are Eq, Ord, Enum, Bounded, Show, and Read... Prelude中唯一允许派生实例的类是Eq,Ord,Enum,Bounded,Show和Read ...

Here's the description of how to derive these type classes: http://www.haskell.org/onlinereport/derived.html#derived-appendix 这是如何派生这些类型类的描述: http : //www.haskell.org/onlinereport/derived.html#derived-appendix

It is possible to use Template Haskell to generate instance declarations in a similar way to deriving-clauses. 可以使用模板Haskell以与派生子句类似的方式生成实例声明。

The following example is shamelessly stolen from the Haskell Wiki : 以下示例是从Haskell Wiki偷偷窃取的:

In this example we use the following Haskell code 在此示例中,我们使用以下Haskell代码

 $(gen_render ''Body) 

to produce the following instance: 产生以下实例:

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

The function gen_render above is defined as follows. 上面的函数gen_render定义如下。 (Note that this code must be in separate module from the above usage). (请注意,此代码必须与上述用法位于单独的模块中)。

 -- 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)) 

Which uses the following functions and types. 其中使用以下功能和类型。

First some type synonyms to make the code more readable. 首先,一些类型的同义词使代码更具可读性。

 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)] 

The main reusable function. 主要的可重用功能。 We pass it the list of functions to generate the functions of the instance. 我们向其传递函数列表以生成实例的函数。

 -- 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) 

A helper function of the above. 以上的辅助功能。

 -- 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 

And some borrowed helper code taken from Syb III / replib 0.2. 还有一些借鉴了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