Haskell Module Tools
As part of an on-going project,
here is a small collection of tools
that perform different tasks on Haskell modules
—
they are all still quite unpolished,
but useful in certain situations.
All these tools have been programmed on top of the GHC API, currently
using GHC-6.10.4,
and rely on GHC for module loading and type checking.
- Listing of Module Exports:
HsExports
ModuleName
attempts to list all exports of module ModuleName,
no matter whether it is a source module (locatable from the current directory)
or a library module in an exposed package.
Example output for Data.Maybe
Found interface for Data.Maybe at /usr/local/packages/ghc-6.10.4/lib/ghc-6.10.4/base-4.1.0.0/Data/Maybe.hi
Data.Maybe exports:
DataCon: Just :: forall a. a -> Maybe a
TyCon: Maybe :: * -> *
DataCon: Nothing :: forall a. Maybe a
Identifier: catMaybes :: forall a. [Maybe a] -> [a]
Identifier: fromJust :: forall a. Maybe a -> a
Identifier: fromMaybe :: forall a. a -> Maybe a -> a
Identifier: isJust :: forall a. Maybe a -> Bool
Identifier: isNothing :: forall a. Maybe a -> Bool
Identifier: listToMaybe :: forall a. [a] -> Maybe a
Identifier: mapMaybe :: forall a b. (a -> Maybe b) -> [a] -> [b]
Identifier: maybe :: forall b a. b -> (a -> b) -> Maybe a -> b
Identifier: maybeToList :: forall a. Maybe a -> [a]
=========== Globals ===============
instance Monad [Maybe] = $f1
$f1 :: Monad Maybe
instance Functor [Maybe] = $f2
$f2 :: Functor Maybe
instance Eq [Maybe] = $f3
$f3 :: forall a. (Eq a) => Eq (Maybe a)
instance Ord [Maybe] = $f4
$f4 :: forall a. (Ord a) => Ord (Maybe a)
=========== Package InstEnv ===============
instance (Eq a) => Eq (Maybe a) -- Defined in Data.Maybe
$f3 :: forall a. (Eq a) => Eq (Maybe a)
instance Monad Maybe -- Defined in Data.Maybe
$f1 :: Monad Maybe
instance Functor Maybe -- Defined in Data.Maybe
$f2 :: Functor Maybe
instance (Ord a) => Ord (Maybe a) -- Defined in Data.Maybe
$f4 :: forall a. (Ord a) => Ord (Maybe a)
=========== Home InstEnv ===============
Current limitations: The types of prelude names,
and re-exported instances are not yet listed. While I am trying to find
out how to locate these re-exported instances,
the instances defined in ModuleName are listed twice,
as found via two different interfaces.
- Wrapping Restricted Datatypes:
Wrap
ModuleName WrapperName1 ... WrapperNamen
expects fully qualified function names (typically of unique
(existential or GADT) data constructors)
for WrapperNamei,
and produces a module derived from ModuleName
by including definitions for all exported values for which
(a component of) the result type can be wrapped in one of
the WrapperNamei,
or for which
(a component of) one of the argument types can be wrapped in the
inverse of one of the WrapperNamei that are data
constructors.
This is useful for “quasi-restricted types”,
for example the FinSet
defined in the following module:
{-# LANGUAGE GADTs, KindSignatures #-}
module FinSet where
import Data.Set (Set)
data FinSet :: * -> *
where FinSet :: (Ord a) => Set a -> FinSet a
Since this GADT constructor encapsulates an Ord
context,
wrapping functions from Data.Set
with
the FinSet
constructor produces an interface essentially like that
of Data.Set
, but with far fewer Ord
constraints.
The wrap tool includes the type signatures of the original values in
comments, but does not attempt to generate signatures for the
results — we can inspect those types for example
using HsExports
, see below.
Example result module
Data.Set.FinSet
from the invocation “Wrap Data.Set FinSet.FinSet
”:
{-# LANGUAGE NoMonomorphismRestriction #-}
module Data.Set.FinSet
( Data.Set.FinSet.findMin
, Data.Set.FinSet.findMax
, Data.Set.FinSet.deleteMin
, Data.Set.FinSet.deleteMax
, Data.Set.FinSet.showTreeWith
, Data.Set.FinSet.member
, Data.Set.FinSet.isSubsetOf
, Data.Set.FinSet.fromList
, Data.Set.FinSet.difference
, Data.Set.FinSet.toAscList
, Data.Set.FinSet.insert
, Data.Set.FinSet.unions
, Data.Set.FinSet.union
, (Data.Set.FinSet.\\)
, Data.Set.FinSet.delete
, Data.Set.FinSet.deleteFindMax
, Data.Set.FinSet.deleteFindMin
, Data.Set.FinSet.elems
, Data.Set.FinSet.empty
, Data.Set.FinSet.filter
, Data.Set.FinSet.fold
, Data.Set.FinSet.fromAscList
, Data.Set.FinSet.fromDistinctAscList
, Data.Set.FinSet.intersection
, Data.Set.FinSet.isProperSubsetOf
, Data.Set.FinSet.map
, Data.Set.FinSet.mapMonotonic
, Data.Set.FinSet.maxView
, Data.Set.FinSet.minView
, Data.Set.FinSet.notMember
, Data.Set.FinSet.null
, Data.Set.FinSet.partition
, Data.Set.FinSet.showTree
, Data.Set.FinSet.singleton
, Data.Set.FinSet.size
, Data.Set.FinSet.split
, Data.Set.FinSet.splitMember
, Data.Set.FinSet.toList
, Data.Set.FinSet.valid
) where
import qualified Data.Set
import FinSet (FinSet(FinSet))
{- Data.Set.findMin :: forall a. Set a -> a -}
findMin (FinSet a) = Data.Set.findMin a
{- Data.Set.findMax :: forall a. Set a -> a -}
findMax (FinSet a) = Data.Set.findMax a
{- Data.Set.deleteMin :: forall a. Set a -> Set a -}
deleteMin (FinSet a) = FinSet (Data.Set.deleteMin a)
{- Data.Set.deleteMax :: forall a. Set a -> Set a -}
deleteMax (FinSet a) = FinSet (Data.Set.deleteMax a)
{- Data.Set.showTreeWith :: forall a.
(Show a) =>
Bool -> Bool -> Set a -> String -}
showTreeWith a b (FinSet c) = Data.Set.showTreeWith a b c
{- Data.Set.member :: forall a. (Ord a) => a -> Set a -> Bool -}
member a (FinSet b) = Data.Set.member a b
{- Data.Set.isSubsetOf :: forall a.
(Ord a) =>
Set a -> Set a -> Bool -}
isSubsetOf (FinSet a) (FinSet b) = Data.Set.isSubsetOf a b
{- Data.Set.fromList :: forall a. (Ord a) => [a] -> Set a -}
fromList a = FinSet (Data.Set.fromList a)
{- Data.Set.difference :: forall a.
(Ord a) =>
Set a -> Set a -> Set a -}
difference (FinSet a) (FinSet b) = FinSet (Data.Set.difference a b)
{- Data.Set.toAscList :: forall a. Set a -> [a] -}
toAscList (FinSet a) = Data.Set.toAscList a
{- Data.Set.insert :: forall a. (Ord a) => a -> Set a -> Set a -}
insert a (FinSet b) = FinSet (Data.Set.insert a b)
{- Data.Set.unions :: forall a. (Ord a) => [Set a] -> Set a -}
unions a = FinSet (Data.Set.unions a)
{- Data.Set.union :: forall a.
(Ord a) =>
Set a -> Set a -> Set a -}
union (FinSet a) (FinSet b) = FinSet (Data.Set.union a b)
{- (Data.Set.\\) :: forall a. (Ord a) => Set a -> Set a -> Set a -}
(\\) (FinSet a) (FinSet b) = FinSet ((Data.Set.\\) a b)
{- Data.Set.delete :: forall a. (Ord a) => a -> Set a -> Set a -}
delete a (FinSet b) = FinSet (Data.Set.delete a b)
{- Data.Set.deleteFindMax :: forall a. Set a -> (a, Set a) -}
deleteFindMax (FinSet a) = let wrap ~((,) b c) = (,) b (FinSet c)
in wrap (Data.Set.deleteFindMax a)
{- Data.Set.deleteFindMin :: forall a. Set a -> (a, Set a) -}
deleteFindMin (FinSet a) = let wrap ~((,) b c) = (,) b (FinSet c)
in wrap (Data.Set.deleteFindMin a)
{- Data.Set.elems :: forall a. Set a -> [a] -}
elems (FinSet a) = Data.Set.elems a
{- Data.Set.empty :: forall a. Set a -}
empty = FinSet Data.Set.empty
{- Data.Set.filter :: forall a.
(Ord a) =>
(a -> Bool) -> Set a -> Set a -}
filter a (FinSet b) = FinSet (Data.Set.filter a b)
{- Data.Set.fold :: forall a b. (a -> b -> b) -> b -> Set a -> b -}
fold a b (FinSet c) = Data.Set.fold a b c
{- Data.Set.fromAscList :: forall a. (Eq a) => [a] -> Set a -}
fromAscList a = FinSet (Data.Set.fromAscList a)
{- Data.Set.fromDistinctAscList :: forall a. [a] -> Set a -}
fromDistinctAscList a = FinSet (Data.Set.fromDistinctAscList a)
{- Data.Set.intersection :: forall a.
(Ord a) =>
Set a -> Set a -> Set a -}
intersection (FinSet a) (FinSet b) = FinSet (Data.Set.intersection a b)
{- Data.Set.isProperSubsetOf :: forall a.
(Ord a) =>
Set a -> Set a -> Bool -}
isProperSubsetOf (FinSet a) (FinSet b) = Data.Set.isProperSubsetOf a b
{- Data.Set.map :: forall a b.
(Ord a, Ord b) =>
(a -> b) -> Set a -> Set b -}
map a (FinSet b) = FinSet (Data.Set.map a b)
{- Data.Set.mapMonotonic :: forall a b.
(a -> b) -> Set a -> Set b -}
mapMonotonic a (FinSet b) = FinSet (Data.Set.mapMonotonic a b)
{- Data.Set.maxView :: forall a. Set a -> Maybe (a, Set a) -}
maxView (FinSet a) = let wrap Nothing = Nothing
wrap (Just ~((,) b c)) = Just ((,) b (FinSet c))
in wrap (Data.Set.maxView a)
{- Data.Set.minView :: forall a. Set a -> Maybe (a, Set a) -}
minView (FinSet a) = let wrap Nothing = Nothing
wrap (Just ~((,) b c)) = Just ((,) b (FinSet c))
in wrap (Data.Set.minView a)
{- Data.Set.notMember :: forall a. (Ord a) => a -> Set a -> Bool -}
notMember a (FinSet b) = Data.Set.notMember a b
{- Data.Set.null :: forall a. Set a -> Bool -}
null (FinSet a) = Data.Set.null a
{- Data.Set.partition :: forall a.
(Ord a) =>
(a -> Bool) -> Set a -> (Set a, Set a) -}
partition a (FinSet b) = let wrap ~((,) c d) = (,) (FinSet c) (FinSet d)
in wrap (Data.Set.partition a b)
{- Data.Set.showTree :: forall a. (Show a) => Set a -> String -}
showTree (FinSet a) = Data.Set.showTree a
{- Data.Set.singleton :: forall a. a -> Set a -}
singleton a = FinSet (Data.Set.singleton a)
{- Data.Set.size :: forall a. Set a -> Int -}
size (FinSet a) = Data.Set.size a
{- Data.Set.split :: forall a.
(Ord a) =>
a -> Set a -> (Set a, Set a) -}
split a (FinSet b) = let wrap ~((,) c d) = (,) (FinSet c) (FinSet d)
in wrap (Data.Set.split a b)
{- Data.Set.splitMember :: forall a.
(Ord a) =>
a -> Set a -> (Set a, Bool, Set a) -}
splitMember a (FinSet b) = let wrap ~((,,) c d e) = (,,) (FinSet c) d (FinSet e)
in wrap (Data.Set.splitMember a b)
{- Data.Set.toList :: forall a. Set a -> [a] -}
toList (FinSet a) = Data.Set.toList a
{- Data.Set.valid :: forall a. (Ord a) => Set a -> Bool -}
valid (FinSet a) = Data.Set.valid a

HsExports
output for Data.Set.FinSet
— note that GHC derives Ord
constraints only for a
small number of functions.
Data.Set.FinSet exports:
Identifier: \\ :: forall t. FinSet t -> FinSet t -> FinSet t
Identifier: delete :: forall a. a -> FinSet a -> FinSet a
Identifier: deleteFindMax :: forall t. FinSet t -> (t, FinSet t)
Identifier: deleteFindMin :: forall t. FinSet t -> (t, FinSet t)
Identifier: deleteMax :: forall t. FinSet t -> FinSet t
Identifier: deleteMin :: forall t. FinSet t -> FinSet t
Identifier: difference :: forall t.
FinSet t -> FinSet t -> FinSet t
Identifier: elems :: forall t. FinSet t -> [t]
Identifier: empty :: forall a. (Ord a) => FinSet a
Identifier: filter :: forall a. (a -> Bool) -> FinSet a -> FinSet a
Identifier: findMax :: forall t. FinSet t -> t
Identifier: findMin :: forall t. FinSet t -> t
Identifier: fold :: forall a b. (a -> b -> b) -> b -> FinSet a -> b
Identifier: fromAscList :: forall a. (Ord a) => [a] -> FinSet a
Identifier: fromDistinctAscList :: forall a.
(Ord a) =>
[a] -> FinSet a
Identifier: fromList :: forall a. (Ord a) => [a] -> FinSet a
Identifier: insert :: forall a. a -> FinSet a -> FinSet a
Identifier: intersection :: forall t.
FinSet t -> FinSet t -> FinSet t
Identifier: isProperSubsetOf :: forall t.
FinSet t -> FinSet t -> Bool
Identifier: isSubsetOf :: forall t. FinSet t -> FinSet t -> Bool
Identifier: map :: forall a a1.
(Ord a1) =>
(a -> a1) -> FinSet a -> FinSet a1
Identifier: mapMonotonic :: forall a a1.
(Ord a1) =>
(a -> a1) -> FinSet a -> FinSet a1
Identifier: maxView :: forall t. FinSet t -> Maybe (t, FinSet t)
Identifier: member :: forall a. a -> FinSet a -> Bool
Identifier: minView :: forall t. FinSet t -> Maybe (t, FinSet t)
Identifier: notMember :: forall a. a -> FinSet a -> Bool
Identifier: null :: forall t. FinSet t -> Bool
Identifier: partition :: forall a.
(a -> Bool) -> FinSet a -> (FinSet a, FinSet a)
Identifier: showTree :: forall t. (Show t) => FinSet t -> String
Identifier: showTreeWith :: forall t.
(Show t) =>
Bool -> Bool -> FinSet t -> String
Identifier: singleton :: forall a. (Ord a) => a -> FinSet a
Identifier: size :: forall t. FinSet t -> Int
Identifier: split :: forall a.
a -> FinSet a -> (FinSet a, FinSet a)
Identifier: splitMember :: forall a.
a -> FinSet a -> (FinSet a, Bool, FinSet a)
Identifier: toAscList :: forall t. FinSet t -> [t]
Identifier: toList :: forall t. FinSet t -> [t]
Identifier: union :: forall t. FinSet t -> FinSet t -> FinSet t
Identifier: unions :: forall a. (Ord a) => [Set a] -> FinSet a
Identifier: valid :: forall t. FinSet t -> Bool
=========== Globals ===============
=========== Package InstEnv ===============
=========== Home InstEnv ===============
Current limitations: Wrappers are not applied inside
opaque or recursive types.
Generating Class Instances from Implementations:
Instantiate
ClassModuleName ImplModuleName
attempts to implement all classes exported by ClassModuleName
with instances assembled from values (typically mostly functions)
exported from ImplModuleName.
Both module names can refer to either a source module (locatable from the current directory)
or a library module in an exposed package.
Example: Given
the class module C2
designed
for restricted collection type constructors c
,
module C2 where
class C2a c where
null :: c a -> Bool
size :: c a -> Int
class C2b c where
member :: a -> c a -> Bool
delete :: a -> c a -> c a
split :: a -> c a -> (c a,c a)
class C2c c where
elems :: c a -> [a]
fold :: (a -> b -> b) -> b -> c a -> b
minView :: c a -> Maybe (a, c a)
maxView :: c a -> Maybe (a, c a)
class C2d c where
filter :: (a -> Bool) -> c a -> c a
partition :: (a -> Bool) -> c a -> (c a,c a)
class C2e c where
fromList :: [a] -> c a
class C2f c where
empty :: c a
class C2g c where
deleteMin :: c a -> c a
deleteMax :: c a -> c a
class C2h c where
union :: c a -> c a -> c a
difference :: c a -> c a -> c a
intersection :: c a -> c a -> c a
class C2i c where
unions :: [c a] -> c a
class C2j c where
findMin :: c a -> a
findMax :: c a -> a
toList :: c a -> [a]
class C2k c where
map :: (a -> b) -> c a -> c b
and the wrapped module Data.Set.FinSet
from above,
the call
Instantiate C2 Data.Set.FinSet
produces a result module containing instances for the classes where this
is possible
(C2a
–C2d
, C2g
, C2h
,
and C2j
),
and explanations where an instance is impossible:

Instantiate
result module Data.Set.FinSet.C2
.
module Data.Set.FinSet.C2 where
import C2
import qualified Data.Set.FinSet
import FinSet (FinSet)
{-
instance C2k FinSet where
{-
class member: map :: forall (c :: * -> *).
(C2k c) =>
forall a b. (a -> b) -> c a -> c b
implementation: map :: forall a a1.
(Ord a1) =>
(a -> a1) -> FinSet a -> FinSet a1
unsatisfiable constraints: (Ord a1)
-}
-}
instance C2j FinSet where
findMin = Data.Set.FinSet.findMin
findMax = Data.Set.FinSet.findMax
toList = Data.Set.FinSet.toList
{-
instance C2i c where
{- type mismatch: unions :: [c a] -> c a
unions :: [Set a] -> FinSet a -}
-}
instance C2h FinSet where
union = Data.Set.FinSet.union
difference = Data.Set.FinSet.difference
intersection = Data.Set.FinSet.intersection
instance C2g FinSet where
deleteMin = Data.Set.FinSet.deleteMin
deleteMax = Data.Set.FinSet.deleteMax
{-
instance C2f FinSet where
{-
class member: empty :: forall (c :: * -> *).
(C2f c) =>
forall a. c a
implementation: empty :: forall a. (Ord a) => FinSet a
unsatisfiable constraints: (Ord a)
-}
-}
{-
instance C2e FinSet where
{-
class member: fromList :: forall (c :: * -> *).
(C2e c) =>
forall a. [a] -> c a
implementation: fromList :: forall a.
(Ord a) =>
[a] -> FinSet a
unsatisfiable constraints: (Ord a)
-}
-}
instance C2d FinSet where
filter = Data.Set.FinSet.filter
partition = Data.Set.FinSet.partition
instance C2c FinSet where
elems = Data.Set.FinSet.elems
fold = Data.Set.FinSet.fold
minView = Data.Set.FinSet.minView
maxView = Data.Set.FinSet.maxView
instance C2b FinSet where
member = Data.Set.FinSet.member
delete = Data.Set.FinSet.delete
split = Data.Set.FinSet.split
instance C2a FinSet where
null = Data.Set.FinSet.null
size = Data.Set.FinSet.size
Download
Wolfram Kahl