Toolmaking libraries

From HaskellWiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Basic concept

Writing tools involves duplicating work that gets done over and over, and which is affected by the existence of extensions. So let's define interfaces for libraries that make this easier and build some initial implementations, while letting new Haskell implementations with new extensions provide their own implementations that hopefully make the extensions less painful to deal with.

Views

Different tools need different information about code - for example, haddock doesn't care about terms at all, whereas other tools might want to know about dependencies and yet others might perform abstract interpretations on code. So we should provide different views of a source file, with different modules implementing them.

  1. Apps like Haddock need to see the bindings present in a module and some information about those bindings, but no terms. Some term-relevant analyses could still be usefully provided - so long as they're done on-demand, apps that don't need them won't be restricted by tighter well-formedness constraints.
  2. Other applications could use a desugared 'core' language (and preferably means to find the corresponding source for a given piece of core code). At least two variants would make sense:
    • An untyped core. Producing such a language isn't too hard, though there are arguments to be had as to how to handle type classes.
    • An explicitly typed core. This could be trickier - the Right Thing would appear to be a core sufficiently expressive to handle type-preserving translations from the numerous extensions kicking around. Type classes are again an issue, in that much of the work involved with them is in resolving which instance to use - this can be dealt with, but requires some thought.

Proposed interfaces

A sketch proposal for a "just the names" interface (... where I've not filled in concrete details yet, feel free to add in the obvious):

-- [Item] corresponds to the order the items appear in the source
data Module = Module Identifier [Export] [Item]
data Export = ...
data Item = Comment String |
            Function FuncDec |
            Fixity FixityDec |
            Type TypeDec |
            Class ClassDec |
            Instance InstanceDec |
            ...

data FuncDec = FuncDec Identifier (Maybe Type)
data FixityDec = FixDec Fixity Int 
data Fixity = FLeft | FRight | FNon
data TypeDec = TypeDec {name :: Identifier,
                        parms,
                        constructors :: [DataConDec],
                        ...} |
               TypeSynonym ... |
               NewType ...
data ClassDec = ... -- keep abstract
data InstanceDec = ... -- keep abstract

data Type = ... -- keep abstract

-- parse source
readSource :: String -> Module

{- queries on Module -}
funcs :: Module -> [FuncDec]
types :: Module -> [TypeDec]
classes :: Module -> [ClassDec]
instances :: Module -> [InstanceDec]

{- compatibility - extended/custom version of lib will want additional such queries -}
class MaybeH98 dec where
  isH98 :: dec -> Bool

-- instances of MaybeH98 for all Dec structures

{- queries on InstanceDec -}
-- print out the 'head' of the instance, "instance <class> <parms>"
showInstanceHead :: InstanceDec -> String
showInstance :: InstanceDec -> String -- use for Show instance

{- queries on FuncDec -}
-- clearly I have done some handwaving, as FuncDecs'd need tying to their source for this
inferType :: FuncDec -> Type

{- queries on Type -}
showType :: Type -> String -- use for Show instance, should be legit Haskell code

-- Any other useful queries want adding?