[Top] | [Contents] | [Index] | [ ? ] |
DrIFT is a type-sensitive preprocessor for Haskell. It is used to automatically generate code for new defined types.
1. Introduction 2. User Guide 3. Standard Rules 4. Rolling Your Own 5. Installation 6. Bugs and Shortcomings
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
DrIFT is a tool which parses a Haskell module for structures (data & newtype declarations) and commands. These commands cause rules to be fired on the parsed data, generating new code which is then appended to the bottom of the input file, or redirected to another. These rules are expressed as Haskell code, and it is intended that the user can add new rules as required.
DrIFT is written in pure Haskell 98, however code it generates is free to make use of extensions when appropriate. DrIFT is currently tested against hugs and ghc.
1.1 So, What Does DrIFT do? 1.2 Features 1.3 Why Do We Need DrIFT? 1.4 An Example
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
DrIFT allows derivation of instances for classes that aren't supported by the standard compilers. In addition, instances can be produced in separate modules to that containing the type declaration. This allows instances to be derived for a type after the original module has been compiled. As a bonus, simple utility functions can also be produced for types.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The original motivation for derive came from reading one of the Glasgow Parallel Haskell papers on Strategies. Strategies require producing instances of a class which reduces to normal form (called NFData). It was commented that it was a shame that instances of NFData couldn't be automatically derived; the rules to generate the instances are simple, and adding instances by hand is tiresome. Many classes' instances follow simple patterns. This is what makes coding up instances so tedious: there's no thought involved!
The idea to extend derive to work on imported types came from a discussion of the Haskell mailing list, arising from a point made by Olaf Chitil :
Why is the automatic derivation of instances for some standard classes linked to data and newtype declarations? It happened already several times to me that I needed a standard instance of a data type that I imported from a module that did not provide that instance and which I did not want to change (a library; GHC, which I mainly want to extend by further modules, not spread changes over 250 modules). When declaring a new data type one normally avoids deriving (currently) unneeded instances, because it costs program code (and maybe one even wants to enable the user of the module to define his own instances).
The third feature of derive, providing utility functions to manipulate new types, especially records was caused by finding myself writing the same sort of code over and over again. These functions couldn't be captured in a class, but have a similar form for each type they are defined on. A recent thread on the Haskell mailing list made a related point: untagging and manipulating newtypes was more cumbersome than it could be.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
{-! ... !-}
. After processing with derive the generated code
is glued on the bottom of the file, beneath a marker indicating where
the new code starts. The machine generated code is quite long, and
would really have been a drudge to type in by hand.
1.4.1 Source Code 1.4.2 After processing with derive
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
-- example script for derive module Example where import Foo {-!for Foo derive : Read,NFData !-} -- apply rules to imported type {-! global : is !-} -- global to this module {-!for Data derive : update,Show,Read!-} -- stand alone comand syntax {-!for Maybe derive : NFData !-} -- apply rules to prelude type data Data = D {name :: Name, constraints :: [(Class,Var)], vars :: [Var], body :: [(Constructor,[(Name,Type)])], derive :: [Class], statement :: Statement} data Statement = DataStmt | NewTypeStmt deriving Eq {-!derive : Ord,Show,Read !-} -- abbreviated syntax |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
module Example where import Foo {-!for Foo derive : Read,NFData !-} -- apply rules to imported type {-! global : is !-} -- global to this module {-!for Data derive : update,Show,Read!-} -- stand alone comand syntax {-!for Maybe derive : NFData !-} -- apply rules to prelude type data Data = D {name :: Name, constraints :: [(Class,Var)], vars :: [Var], body :: [(Constructor,[(Name,Type)])], derive :: [Class], statement :: Statement} data Statement = DataStmt | NewTypeStmt deriving Eq {-!derive : Ord,Show,Read !-} {-* Generated by DrIFT-v1.0 : Look, but Don't Touch. *-} isD (D aa ab ac ad ae af) = True isD _ = False instance Ord Statement where compare DataStmt (DataStmt) = EQ compare DataStmt (NewTypeStmt) = LT compare NewTypeStmt (DataStmt) = GT compare NewTypeStmt (NewTypeStmt) = EQ instance Show Statement where showsPrec d (DataStmt) = showString "DataStmt" showsPrec d (NewTypeStmt) = showString "NewTypeStmt" instance Read Statement where readsPrec d input = (\ inp -> [((DataStmt) , rest) | ("DataStmt" , rest) <- lex inp]) input ++ (\ inp -> [((NewTypeStmt) , rest) | ("NewTypeStmt" , rest) <- lex inp]) input isDataStmt (DataStmt) = True isDataStmt _ = False isNewTypeStmt (NewTypeStmt) = True isNewTypeStmt _ = False instance (NFData a) => NFData (Maybe a) where rnf (Just aa) = rnf aa rnf (Nothing) = () body_u f r@D{body} = r{body = f body} constraints_u f r@D{constraints} = r{constraints = f constraints} derive_u f r@D{derive} = r{derive = f derive} name_u f r@D{name} = r{name = f name} statement_u f r@D{statement} = r{statement = f statement} vars_u f r@D{vars} = r{vars = f vars} body_s v = body_u (const v) constraints_s v = constraints_u (const v) derive_s v = derive_u (const v) name_s v = name_u (const v) statement_s v = statement_u (const v) vars_s v = vars_u (const v) instance Show Data where showsPrec d (D aa ab ac ad ae af) = showParen (d >= 10) (showString "D" . showChar '{' . showString "name" . showChar '=' . showsPrec 10 aa . showChar ',' . showString "constraints" . showChar '=' . showsPrec 10 ab . showChar ',' . showString "vars" . showChar '=' . showsPrec 10 ac . showChar ',' . showString "body" . showChar '=' . showsPrec 10 ad . showChar ',' . showString "derive" . showChar '=' . showsPrec 10 ae . showChar ',' . showString "statement" . showChar '=' . showsPrec 10 af . showChar '}') instance Read Data where readsPrec d input = readParen (d > 9) (\ inp -> [((D aa ab ac ad ae af) , rest) | ("D" , inp) <- lex inp , ("{" , inp) <- lex inp , ("name" , inp) <- lex inp , ("=" , inp) <- lex inp , (aa , inp) <- readsPrec 10 inp , ("," , inp) <- lex inp , ("constraints" , inp) <- lex inp , ("=" , inp) <- lex inp , (ab , inp) <- readsPrec 10 inp , ("," , inp) <- lex inp , ("vars" , inp) <- lex inp , ("=" , inp) <- lex inp , (ac , inp) <- readsPrec 10 inp , ("," , inp) <- lex inp , ("body" , inp) <- lex inp , ("=" , inp) <- lex inp , (ad , inp) <- readsPrec 10 inp , ("," , inp) <- lex inp , ("derive" , inp) <- lex inp , ("=" , inp) <- lex inp , (ae , inp) <- readsPrec 10 inp , ("," , inp) <- lex inp , ("statement" , inp) <- lex inp , ("=" , inp) <- lex inp , (af , inp) <- readsPrec 10 inp , ("}" , rest) <- lex inp]) input -- Imported from other files :- instance Read Foo where readsPrec d input = (\ inp -> [((Foo) , rest) | ("Foo" , rest) <- lex inp]) input ++ (\ inp -> [((Bar) , rest) | ("Bar" , rest) <- lex inp]) input ++ (\ inp -> [((Bub) , rest) | ("Bub" , rest) <- lex inp]) input instance NFData Foo where rnf (Foo) = () rnf (Bar) = () rnf (Bub) = () |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Briefly, the way derive works is
Rules can be applied to any types defined using a data
or
newtype
statement. Rules can't be applied to types defined using
type
, as this only produces a synonym for a type. Don't
try to use rules on type synonyms.
2.1 The Command Line 2.2 Command Syntax 2.3 Emacs derive mode
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
>
is accepted: derive doesn't understand the TeX style
of literate programming using \begin{code}
.
If you've compiled up an executable from the source code (or are using
Runhugs) to run derive over a file type :-
derive filename
Alternatively, within Hugs, use :-
:s +l
(make literate scripts the default)
:l derive.lhs
(load derive)
derive ["filename"]
(run derive over filename)
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
{-!
and finish
with !-}
. (This is so they don't clash with the compiler annotations
given to GHC or HBC). There are three forms of command.
{-! for type derive :
rule1,rule2,... !-}
)
This is the basic form of derive command. It asks derive to apply the
listed rules to the specified type. If the type is parameterised,
e.g. Maybe a
, just enter the type name into the command, omitting
any type variables. DrIFT assumes that types given are currently in
scope, and will first search the current module. If it fails to find a
matching type definition, the prelude and any imported modules are also
searched. This is the only command which allows code to be generated
for a type defined in another module.
{-! derive :rule1,rule2,... !-}
)
This command is appended to the end of a data
or newtype
definition, after the deriving clause, if present. It applies the listed
rules to the type it is attached to.
{-! global :rule1,rule2,... !-}
This command applies the listed rules
to all types defined within the module. Note that this command doesn't
cause code to be generated for types imported from other modules.
For an example of these commands in use, See section 1.4 An Example.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
infix
, import
,newtype
). It
doesn't matter what position they occur within the module.
>
).
--
and {- .. -}
in the usual way.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The commands available are
M-x hwl-derive
, C-c d d
runs derive over the current
buffer, and then updates the buffer.
M-x hwl-derive-insert-standalone
, C-c d s
inserts a
template for a standalone command into the current buffer at the
cursor position.
M-x hwl-derive-insert-local
, C-c d l
inserts a template
for an abbreviated command.
M-x hwl-derive-insert-global
, C-c d g
inserts a template
for a global command
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
newtype
.
For a typenewtype Foo a = F a
,un produces the function
unFoo :: Foo a -> a
.
For a typedata Foo = Bar | Bub
, is generates
isBar :: Foo -> Bool
andisBub :: Foo -> Bool
.
For a typedata Foo a = F{bar :: a,bub :: Int}
has generates
hasbar :: Foo a-> Bool
andhasbub :: Foo a -> Bool
.
For a typedata Foo a = F{bar :: a, bub ::Int}
update generates
bar_u :: (a -> a) -> Foo a -> Foo a
and
bub_u :: (Int -> Int) -> Foo a -> Foo a
which apply a function to a field of a record, and then return the updated record.
bar_s :: a -> Foo a -> Foo a
andbub_s ::Int -> Foo a -> Foo a
are also generated, and are used to set the value of a field in a record.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If a compiled version of derive is being used, the program will then
have to be recompiled before the new rules can be used. However, if the
Runhugs standalone interpreter is used, this is not necessary. Due to
the way Runhugs searches for modules to load, a user may have many
copies of the UserRules module .The UserRules module in the current
directory will be loaded first. If that is not present, then the
HUGSPATH
environment variable is searched for the module. So it is
possible to have a default UserRules module, and specialised ones for
particular projects.
4.1 The Basic Idea 4.2 How is a Type Represented? 4.3 Pretty Printing 4.4 Utilities 4.5 Adding a new rule
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
>data Statement = DataStmt | NewTypeStmt deriving (Eq,Show) >data Data = D { name :: Name, -- type name > constraints :: [(Class,Var)], > vars :: [Var], -- Parameters > body :: [Body], > derives :: [Class], -- derived classes > statement :: Statement} > | Directive > | TypeName Name deriving (Eq,Show) >type Name = String >type Var = String >type Class = String |
A Data
type represents one parsed data
or newtype
statement. These are held in a D
constructor record (the
Directive
and TypeName
constructors are just used internally by
derive). We'll now examine each of the fields in turn.
name
holds the name of the new datatype as a string.
constraints
list the type constraints for the type variables of
the new type. e.g. for data (Eq a) => Foo a = F a
, the value of
constraints
would be [("Eq","a")]
.
vars
contains a list of the type variables in the type. For the
previous example, this would simply be ["a"]
.
body
is a list of the constructors of the type, and the
information associated with them. We'll come back to this in a moment.
derives
lists the classes that the type an instance of though
using the deriving
clause.
statement
indicates whether the type was declared using a
newtype
or data
statement
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
>data Body = Body { constructor :: Constructor, > labels :: [Name], > types :: [Type]} deriving (Eq,Show) >type Constructor = String |
The body type holds information about one of the constructors of a type.
constructor
is self-explanatory. labels
holds the names
of labels of a record. This will be blank if the constructor isn't a
record. types
contains a representation of the type of each
value within the constructor. The definition of Type
is as
follows.
>data Type = Arrow Type Type -- fn > | Apply Type Type -- application > | Var String -- variable > | Con String -- constructor > | Tuple [Type] -- tuple > | List Type -- list > deriving (Eq,Show) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Instead of producing a string as output, rules produce a value of type
Doc
. This type is defined in the Pretty Printing Library implemented
by Simon Peyton-Jones. The pretty printer ensures that the code is
formatted for readability, and also handles problems such as
indentation. Constructing output using pretty printing combinators is
easier and more structured than manipulating strings too. For those
unfamiliar with these combinators, have a look at the module
`Pretty.lhs' and the web page http://www.cse.ogi.edu/~simonpj/
or for more detail the paper The Design of a Pretty Printing
Library, J. Hughes
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
type Rule = (String,Data -> Doc)
. Once you have
written your mapping function and chosen an appropriate name for the
rule, add this tuple to the list userRules :: [Rule]
in module `UserRules.hs'. Recompile if necessary. DrIFT will then call this rule when
its name occurs in a command in an input file.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
5.1 GHC 5.2 Hugs 5.3 Runhugs 5.4 Environment Variables 5.5 HBC 5.6 Installing the Emacs DrIFT Mode
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
GHC
points to your copy of
ghc-2.02 . To compile, type make all
. You can clean up after
using make clean
. The executable produced `derive' should
be moved to a directory somewhere in your PATH
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
HUGSPATH
, then you can load and run
derive in any directory.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
runhugs
. Copy `derive' to somewhere on your PATH
, and
the remainder of the source (`*.hs',`*.lhs') to a directory in your HUGSPATH
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
DERIVEPATH
to the list of directories you
wish derive to search for modules / interfaces.
DERIVEPATH
is quite fussy about the format the list should take :-
For instance
good - /users/nww/share/hugs/lib:/users/nww/share/hugs/lib/hugs
bad - /users/nww/share/hugs/lib/: /users/nww/share/hugs/lib/hugs/
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Edit `derive.el' so that the variable hwl-derive-cmd
contains your
copy of the derive executable.
Place `derive.el' into a directory on your load-path
, byte-compile it and put the following command into your `.emacs' file:
(load "derive")
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[Top] | [Contents] | [Index] | [ ? ] |
[Top] | [Contents] | [Index] | [ ? ] |
1. Introduction
2. User Guide
3. Standard Rules
4. Rolling Your Own
5. Installation
6. Bugs and Shortcomings
[Top] | [Contents] | [Index] | [ ? ] |
Button | Name | Go to | From 1.2.3 go to |
---|---|---|---|
[ < ] | Back | previous section in reading order | 1.2.2 |
[ > ] | Forward | next section in reading order | 1.2.4 |
[ << ] | FastBack | previous or up-and-previous section | 1.1 |
[ Up ] | Up | up section | 1.2 |
[ >> ] | FastForward | next or up-and-next section | 1.3 |
[Top] | Top | cover (top) of document | |
[Contents] | Contents | table of contents | |
[Index] | Index | concept index | |
[ ? ] | About | this page |