在Haskell中如何派生工作?

Haskell中的代数数据类型(ADT)可以通过派生它们自动变成某些类型类的实例(如ShowEq )。

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

我的问题是,这是如何deriving工作的,即Haskell如何知道如何实现派生类型类派生ADT的函数?

此外,为什么只能deriving到某些类型类? 为什么我不能编写我自己可以派生的类型类?


简短的回答是,魔术:-)。 这就是说,自动派生被烘焙到Haskell规范中,并且每个编译器都可以选择以自己的方式实现它。 但是,如何使其可扩展性还有很多工作要做。

派生是Haskell的一个工具,可以让你编写自己的派生机制。

GHC用于提供一个名为Generic Classes的可扩展类型扩展,但它很少使用,因为它有点弱。 现在已经取消了,正在进行工作以整合本文所述的新的通用推导机制:http://www.dreixel.net/research/pdf/gdmh.pdf

有关更多信息,请参阅:

  • GHC wiki:http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/GenericDeriving
  • Haskell wiki:http://www.haskell.org/haskellwiki/Generics
  • Hackage:http://hackage.haskell.org/package/generic-deriving

  • 从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
    
    链接地址: http://www.djcxy.com/p/43021.html

    上一篇: How does deriving work in Haskell?

    下一篇: Evaluating List to Weak Head Normal Form