-----------------------------------------------------------------------------
-- |
-- Module : NLP.WordNet
-- Copyright : (c) Hal Daume III 2003-2004
-- License : BSD-style
--
-- Maintainer : hdaume@isi.edu
-- Stability : experimental
-- Portability : non-portable (H98 + implicit parameters)
--
-- This is the top level module to the Haskell WordNet interface.
--
-- This module is maintained at:
-- .
--
-- This is the only module in the WordNet package you need to import.
-- The others provide utility functions and primitives that this
-- module is based on.
--
-- More information about WordNet is available at:
-- .
-----------------------------------------------------------------------------
module NLP.WordNet
(
-- * The basic type system
module NLP.WordNet.Types,
-- * Top level execution functions
runWordNet,
runWordNetQuiet,
runWordNetWithOptions,
-- * Functions to manually initialize the WordNet system; these are not
-- needed if you use one of the "runWordNet" functions above.
initializeWordNet,
initializeWordNetWithOptions,
closeWordNet,
runs,
-- * The basic database access functions.
getOverview,
searchByOverview,
search,
lookupKey,
-- * The agglomeration functions
relatedBy,
closure,
closureOn,
-- * Computing lowest-common ancestor functions; the implementation
-- of these can be tuned by providing a different "Bag" implementation.
-- use "emptyQueue" for breadth-first-search (recommended) or "emptyStack"
-- for depth-first-search, or write your own.
meet,
meetPaths,
meetSearchPaths,
Bag(..),
emptyQueue,
emptyStack,
)
where
import Prelude hiding (catch)
import Data.Array
import GHC.Arr (unsafeIndex)
import GHC.Handle
import Data.Tree
import Data.IORef
import Data.Dynamic
import qualified Data.Set as Set
import Numeric (readHex, readDec)
import System.IO.Unsafe
import NLP.WordNet.Common
import NLP.WordNet.Consts
import NLP.WordNet.Util
import NLP.WordNet.Types
import qualified NLP.WordNet.PrimTypes as T
import qualified NLP.WordNet.Prims as P
-- | Takes a WordNet command, initializes the environment
-- and returns the results in the 'IO' monad. WordNet
-- warnings are printed to stderr.
runWordNet :: WN a -> IO a
runWordNet = runWordNetWithOptions Nothing Nothing
-- | Takes a WordNet command, initializes the environment
-- and returns the results in the 'IO' monad. WordNet
-- warnings are ignored.
runWordNetQuiet :: WN a -> IO a
runWordNetQuiet = runWordNetWithOptions Nothing (Just (\_ _ -> return ()))
-- | Takes a FilePath to the directory holding WordNet and
-- a function to do with warnings and a WordNet command, initializes
-- the environment and returns the results in the 'IO' monad.
runWordNetWithOptions ::
Maybe FilePath -> -- word net data directory
Maybe (String -> Exception -> IO ()) -> -- warning function (by default, warnings go to stderr)
WN a -> -- what to run
IO a
runWordNetWithOptions dd warn wn = do
wne <- P.initializeWordNetWithOptions dd warn
let a = let ?wne = wne in wn
-- P.closeWordNet wne
return a
-- | Gives you a 'WordNetEnv' which can be passed to 'runs' or used
-- as the implicit parameter to the other WordNet functions.
initializeWordNet :: IO WordNetEnv
initializeWordNet = P.initializeWordNet
-- | Takes a FilePath to the directory holding WordNet and
-- a function to do with warnings, initializes
-- the environment and returns a 'WordNetEnv' as in 'initializeWordNet'.
initializeWordNetWithOptions :: Maybe FilePath -> Maybe (String -> Exception -> IO ()) -> IO WordNetEnv
initializeWordNetWithOptions = P.initializeWordNetWithOptions
-- | Closes all the handles associated with the 'WordNetEnv'. Since
-- the functions provided in the "NLP.WordNet.WordNet" module
-- are /lazy/, you shouldn't do this until you're really done.
-- Or perhaps not at all (GC will eventually kick in).
closeWordNet :: WordNetEnv -> IO ()
closeWordNet = P.closeWordNet
-- | This simply takes a 'WordNetEnv' and provides it as the
-- implicit parameter to the WordNet command.
runs :: WordNetEnv -> WN a -> a
runs wne x = let ?wne = wne in x
-- | This takes a word and returns an 'Overview' of all its senses
-- for all parts of speech.
getOverview :: WN (Word -> Overview)
getOverview word = unsafePerformIO $ do
idxN <- unsafeInterleaveIO $ getOverview' Noun
idxV <- unsafeInterleaveIO $ getOverview' Verb
idxA <- unsafeInterleaveIO $ getOverview' Adj
idxR <- unsafeInterleaveIO $ getOverview' Adv
return (T.Overview idxN idxV idxA idxR)
where
getOverview' pos = do
strM <- P.getIndexString ?wne word pos
case strM of
Nothing -> return Nothing
Just s -> unsafeInterleaveIO $ P.indexLookup ?wne word pos
-- | This takes an 'Overview' (see 'getOverview'), a 'POS' and a 'SenseType' and returns
-- a list of search results. If 'SenseType' is 'AllSenses', there will be one
-- 'SearchResult' in the results for each valid sense. If 'SenseType' is
-- a single sense number, there will be at most one element in the result list.
searchByOverview :: WN (Overview -> POS -> SenseType -> [SearchResult])
searchByOverview overview pos sense = unsafePerformIO $
case (case pos of { Noun -> T.nounIndex ; Verb -> T.verbIndex ; Adj -> T.adjIndex ; Adv -> T.advIndex })
overview of
Nothing -> return []
Just idx -> do
let numSenses = T.indexSenseCount idx
skL <- mapMaybe id `liftM`
unsafeInterleaveIO (
mapM (\sense -> do
skey <- P.indexToSenseKey ?wne idx sense
return (liftM ((,) sense) skey)
) (sensesOf numSenses sense)
)
r <- unsafeInterleaveIO $ mapM (\ (snum, skey) ->
unsafeInterleaveIO (P.getSynsetForSense ?wne skey) >>= \v ->
case v of
Nothing -> return Nothing
Just ss -> return $ Just (T.SearchResult
(Just skey)
(Just overview)
(Just idx)
(Just (SenseNumber snum))
ss)
) skL
return (mapMaybe id r)
-- | This takes a 'Word', a 'POS' and a 'SenseType' and returns
-- the equivalent of first running 'getOverview' and then 'searchByOverview'.
search :: WN (Word -> POS -> SenseType -> [SearchResult])
search word pos sense = searchByOverview (getOverview word) pos sense
-- | This takes a 'Key' (see 'srToKey' and 'srFormKeys') and looks it
-- up in the databse.
lookupKey :: WN (Key -> SearchResult)
lookupKey (T.Key (o,p)) = unsafePerformIO $ do
ss <- unsafeInterleaveIO $ P.readSynset ?wne p o ""
return $ T.SearchResult Nothing Nothing Nothing Nothing ss
-- | This takes a 'Form' and a 'SearchResult' and returns all
-- 'SearchResult' related to the given one by the given 'Form'.
--
-- For example:
--
-- > relatedBy Antonym (head (search "happy" Adj 1))
-- > []
-- >
-- > relatedBy Hypernym (head (search "dog" Noun 1))
-- > []
relatedBy :: WN (Form -> SearchResult -> [SearchResult])
relatedBy form sr = map lookupKey $ srFormKeys sr form
-- | This is a utility function to build lazy trees from a function and a root.
closure :: (a -> [a]) -> a -> Tree a
closure f x = Node x (map (closure f) $ f x)
-- | This enables 'Form'-based trees to be built.
--
-- For example:
--
-- > take 5 $ flatten $ closureOn Antonym (head (search "happy" Adj AllSenses)))
-- > [,,,,]
--
-- > closureOn Hypernym (head (search "dog" Noun 1)))
-- > - --- --- \\
-- > --- --- \\
-- > --- --- --- brute creature fauna> --- --- \\
-- > ---