Substitute with the version from the video

This commit is contained in:
Pyrex 2025-04-12 19:40:32 -07:00
parent 288c999c0e
commit 2e819b454f
7 changed files with 162 additions and 168 deletions

View File

@ -1,49 +1,50 @@
module Binding module Binding
( newVar, unify, instantiate ( newVar, unify, instantiate
) where ) where
import Primitives
import Terms import Terms
import Nondeterminism import Nondeterminism
import Primitives import Control.Monad
import Control.Monad
import Control.Applicative import Control.Applicative
newVar :: Name -> PrologStream Var newVar :: Name -> PrologStream Slot
newVar name = reversibly (newVarPrim name) unnewVarPrim newVar name = reversibly (newVarPrim name) unnewVarPrim
getVar :: Var -> PrologStream (Maybe Term) getVar :: Slot -> PrologStream (Maybe Term)
getVar var = liftPrologIO (getVarPrim var) getVar slot = liftPrologIO (getVarPrim slot)
setVar :: Var -> Term -> PrologStream () setVar :: Slot -> Term -> PrologStream ()
setVar var value = reversibly set unset setVar slot value = reversibly set unset
where where
set = do set = do
oldValue <- getVarPrim var oldValue <- getVarPrim slot
when (oldValue /= Nothing) (errorPrim "cannot set var that is currently set") when (oldValue /= Nothing) (errorPrim "cannot set var that is already set")
setVarPrim var (Just value) setVarPrim slot (Just value)
unset = do unset = do
oldValue <- getVarPrim var oldValue <- getVarPrim slot
when (oldValue == Nothing) (errorPrim "cannot unset var that is already unset") when (oldValue == Nothing) (errorPrim "cannot unset var that is already unset")
setVarPrim var Nothing setVarPrim slot Nothing
unify :: Term -> Term -> PrologStream () unify :: Term -> Term -> PrologStream ()
unify (Compound h0 args0) (Compound h1 args1) = do unify (Compound h0 args0) (Compound h1 args1) = do
when (h0 /= h1) empty when (h0 /= h1) empty
when (length args0 /= length args1) empty when (length args0 /= length args1) empty
forM_ (zip args0 args1) $ \(arg0, arg1) -> unify arg0 arg1 forM_ (zip args0 args1) $ \(arg0, arg1) ->
unify (Var v0) (Var v1) | v0 == v1 = return () unify arg0 arg1
unify (Var v0) (Var v1) | v0 == v1 = return () -- succeed with {} (no bindings)
unify (Var v0) t1 = do unify (Var v0) t1 = do
binding <- getVar v0 binding <- getVar v0
case binding of case binding of
Just t0 -> unify t0 t1 Just t0 -> unify t0 t1
Nothing -> setVar v0 t1 Nothing -> setVar v0 t1
unify t0 t1@(Var _) = unify t1 t0 unify t0@(Compound _ _) t1@(Var _) = unify t1 t0
instantiate :: Term -> PrologStream Term instantiate :: Term -> PrologStream Term
instantiate t@(Var v) = do instantiate t@(Var v) = do
binding <- getVar v binding <- getVar v
case binding of case binding of
Just t0 -> instantiate t0 Just t0 -> instantiate t0
Nothing -> return t Nothing -> return t
instantiate (Compound h args) = do instantiate (Compound h args) = do
instantiatedArgs <- mapM instantiate args instantiatedArgs <- mapM instantiate args
return (Compound h instantiatedArgs) return (Compound h instantiatedArgs)

View File

@ -1,6 +1,5 @@
module Classes module Classes (LiftIO(..)) where
( LiftIO (..)
) where
class Monad m => LiftIO m where class Monad m => LiftIO m where
liftIO :: IO a -> m a liftIO :: IO a -> m a

View File

@ -1,13 +1,9 @@
module Database module Database where
( Clause(..) import Nondeterminism
, Database(..)
, seekAndInstantiate, seek
) where
import Binding
import Nondeterminism
import Terms import Terms
import Control.Monad
import Control.Applicative import Control.Applicative
import Binding
import Control.Monad
data Clause = Clause Term [Term] data Clause = Clause Term [Term]
@ -22,9 +18,9 @@ seekAndInstantiate goal db = do
seek :: Term -> Database -> PrologStream () seek :: Term -> Database -> PrologStream ()
seek goal db = do seek goal db = do
foldr (<|>) mzero (map doClause (clauses db)) foldr (<|>) empty (map doClause (clauses db))
where where
doClause clause = do doClause clause = do
Clause chead body <- clause Clause chead body <- clause
unify goal chead unify goal chead
forM_ body $ \newGoal -> seek newGoal db forM_ body $ \newGoal -> seek newGoal db

View File

@ -1,37 +1,36 @@
{-# LANGUAGE InstanceSigs #-}
module Main where module Main where
import Database
import Binding import Nondeterminism
import Classes import Classes
import Database
import Nondeterminism
import Terms import Terms
import Binding
demoProgram :: Database demoProgram :: Database
demoProgram = Database demoProgram = Database
{ clauses = { clauses =
[ do -- act(sam, 'goes to law school')
who <- newVar "who" [ return $ Clause (Compound "act" [Compound "Sam" [], Compound "goes to law school" []]) []
action <- newVar "action" , return $ Clause (Compound "vampire_behavior" [Compound "drinks blood" []]) []
return $ Clause (Compound "act" [Var who, Var action]) , return $ Clause (Compound "vampire_behavior" [Compound "spits venom" []]) []
, do
-- act(Who, Action) :- is_vampire(Who), vampire_behavior(Action).
who <- newVar "Who"
action <- newVar "Action"
return $ Clause (Compound "act" [Var who, Var action])
[ Compound "is_vampire" [Var who] [ Compound "is_vampire" [Var who]
, Compound "vampire_behavior" [Var action] , Compound "vampire_behavior" [Var action]
] ]
, do , do
progenitor <- newVar "progenitor" -- is_vampire(Progeny) :- has_progenitor(Progenitor, Progeny), is_vampire(Progenitor)>
progeny <- newVar "progeny" progenitor <- newVar "Progenitor"
progeny <- newVar "Progeny"
return $ Clause (Compound "is_vampire" [Var progeny]) return $ Clause (Compound "is_vampire" [Var progeny])
[ Compound "has_progenitor" [Var progenitor, Var progeny] [ Compound "has_progenitor" [Var progenitor, Var progeny]
, Compound "is_vampire" [Var progenitor] , Compound "is_vampire" [Var progenitor]
] ]
, return $ Clause (Compound "act" [Compound "Sam" [], Compound "goes to law school" []]) []
, return $ Clause (Compound "vampire_behavior" [Compound "drinks blood" []]) []
, return $ Clause (Compound "vampire_behavior" [Compound "spits venom" []]) []
, return $ Clause (Compound "is_vampire" [Compound "Nyeogmi" []]) [] , return $ Clause (Compound "is_vampire" [Compound "Nyeogmi" []]) []
, return $ Clause (Compound "has_progenitor" [Compound "Nyeogmi" [], Compound "Pyrex" []]) []
, return $ Clause (Compound "has_progenitor" [Compound "Sam" [], Compound "Alex" []]) [] , return $ Clause (Compound "has_progenitor" [Compound "Sam" [], Compound "Alex" []]) []
-- , return $ Clause (Compound "is_vampire" [Compound "Sam" []]) [] , return $ Clause (Compound "is_vampire" [Compound "Sam" []]) []
] ]
} }
@ -40,11 +39,11 @@ main = do
thread <- newThread runDemoProgram thread <- newThread runDemoProgram
_ <- takeAllRemainingSolutions thread _ <- takeAllRemainingSolutions thread
return () return ()
where where
runDemoProgram = do runDemoProgram :: PrologStream ()
who <- newVar "who" runDemoProgram = do
what <- newVar "what" who <- newVar "who"
let goal = Compound "act" [Var who, Var what] what <- newVar "what"
seek goal demoProgram let goal = Compound "act" [Var who, Var what]
instantiatedGoal <- instantiate goal goal' <- seekAndInstantiate goal demoProgram
liftIO $ putStrLn ("Got " ++ show instantiatedGoal) liftIO $ putStrLn ("Got " ++ show goal')

View File

@ -1,54 +1,68 @@
{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE InstanceSigs, PartialTypeSignatures #-}
module Nondeterminism module Nondeterminism
( PrologStream ( PrologStream
, newThread , newThread
, takeNextSolution, takeAllRemainingSolutions , takeNextSolution, takeAllRemainingSolutions
, reversibly , reversibly, liftPrologIO
, liftPrologIO
) where ) where
import Primitives (Globals, PrologIO(..), newGlobals) import Primitives
import Control.Applicative import Classes
import Control.Applicative
import Control.Monad import Control.Monad
import Classes (LiftIO (..)) import Data.IORef
import GHC.IORef
data Suspend a = Yield a | Suspend data Suspend a = Yield a | Suspend
data PrologStream a
= PMaybe (Suspend a) (PrologIO (PrologStream a))
| PNo
reversibly :: PrologIO a -> PrologIO () -> PrologStream a
reversibly forward backward =
PMaybe Suspend $ forward >>= \fwdValue -> return $
PMaybe (Yield fwdValue) $ backward >>= \_ -> return $
PNo
liftPrologIO :: PrologIO a -> PrologStream a
liftPrologIO forward = reversibly forward (return ())
instance LiftIO PrologStream where
liftIO :: IO a -> PrologStream a
liftIO x = liftPrologIO (liftIO x)
instance Functor Suspend where instance Functor Suspend where
fmap f (Yield a) = Yield (f a) fmap f (Yield a) = Yield (f a)
fmap _ Suspend = Suspend fmap _ Suspend = Suspend
data PrologStream a
= PYes (Suspend a) (PrologIO (PrologStream a))
| PNo
instance Functor PrologStream where instance Functor PrologStream where
fmap :: (a -> b) -> PrologStream a -> PrologStream b fmap :: (a -> b) -> PrologStream a -> PrologStream b
fmap f (PYes a next) = PYes (fmap f a) (fmap (fmap f) next) fmap f (PMaybe a as)
= PMaybe (fmap f a) (fmap (fmap f) as)
fmap _ PNo = PNo fmap _ PNo = PNo
instance Applicative PrologStream where
pure :: a -> PrologStream a
pure x = PYes (Yield x) (pure PNo)
(<*>) :: PrologStream (a -> b) -> PrologStream a -> PrologStream b
f <*> x = f >>= (\f' -> x >>= (\x' -> return (f' x')))
instance Monad PrologStream where instance Monad PrologStream where
return :: a -> PrologStream a return :: a -> PrologStream a
return = pure return = pure
(>>=) :: PrologStream a -> (a -> PrologStream b) -> PrologStream b (>>=) :: PrologStream a -> (a -> PrologStream b) -> PrologStream b
PNo >>= _ = PNo PNo >>= _ = PNo
PYes (Yield x) xs >>= f = f x <|> PYes Suspend (fmap (>>= f) xs) (>>=) (PMaybe (Yield x) xs) f = f x <|> PMaybe Suspend (fmap (>>= f) xs)
PYes Suspend xs >>= f = PYes Suspend (fmap (>>= f) xs) (>>=) (PMaybe Suspend xs) f = PMaybe Suspend (fmap (>>= f) xs)
instance Alternative PrologStream where instance Applicative PrologStream where
pure :: a -> PrologStream a
pure x = PMaybe (Yield x) (pure PNo)
(<*>) :: PrologStream (a -> b) -> PrologStream a -> PrologStream b
fs <*> xs = do { f <- fs; x <- xs; return (f x) }
instance Alternative PrologStream where
empty :: PrologStream a empty :: PrologStream a
empty = PNo empty = PNo
(<|>) :: PrologStream a -> PrologStream a -> PrologStream a (<|>) :: PrologStream a -> PrologStream a -> PrologStream a
(PYes x xs) <|> next = PYes x (liftM2 (<|>) xs (return next)) (PMaybe x xsFuture) <|> next = PMaybe x merged
where
merged = do { xs <- xsFuture; return (xs <|> next) }
PNo <|> next = next PNo <|> next = next
instance MonadPlus PrologStream where instance MonadPlus PrologStream where
@ -58,6 +72,12 @@ instance MonadPlus PrologStream where
mplus :: PrologStream a -> PrologStream a -> PrologStream a mplus :: PrologStream a -> PrologStream a -> PrologStream a
mplus = (<|>) mplus = (<|>)
squeeze :: PrologStream a -> PrologIO (Maybe a, PrologStream a)
squeeze PNo = return (Nothing, PNo)
squeeze (PMaybe Suspend rest) = do { v <- rest; squeeze v }
squeeze (PMaybe (Yield x) rest)
= return (Just x, PMaybe Suspend rest)
data Thread a = Thread data Thread a = Thread
{ threadGlobals :: Globals { threadGlobals :: Globals
, threadStream :: IORef (PrologStream a) , threadStream :: IORef (PrologStream a)
@ -67,16 +87,16 @@ newThread :: PrologStream a -> IO (Thread a)
newThread stream = do newThread stream = do
globals <- newGlobals globals <- newGlobals
streamRef <- newIORef stream streamRef <- newIORef stream
return $ Thread return $ Thread
{ threadGlobals = globals { threadGlobals = globals
, threadStream = streamRef , threadStream = streamRef
} }
takeNextSolution :: Thread a -> IO (Maybe a) takeNextSolution :: Thread a -> IO (Maybe a)
takeNextSolution (Thread { threadGlobals = globals, threadStream = streamRef }) = do takeNextSolution thread = do
stream <- readIORef streamRef stream <- readIORef (threadStream thread)
(value, newStream) <- runPrologIO (squeeze stream) globals (value, newStream) <- runPrologIO (squeeze stream) (threadGlobals thread)
writeIORef streamRef newStream writeIORef (threadStream thread) newStream
return value return value
takeAllRemainingSolutions :: Thread a -> IO [a] takeAllRemainingSolutions :: Thread a -> IO [a]
@ -84,21 +104,4 @@ takeAllRemainingSolutions thread = do
value <- takeNextSolution thread value <- takeNextSolution thread
case value of case value of
Nothing -> return [] Nothing -> return []
Just x -> fmap (x:) (takeAllRemainingSolutions thread) Just x -> fmap (x:) (takeAllRemainingSolutions thread)
squeeze :: PrologStream a -> PrologIO (Maybe a, PrologStream a)
squeeze PNo = return (Nothing, PNo)
squeeze (PYes Suspend rest) = rest >>= squeeze
squeeze (PYes (Yield x) rest) = fmap ((,) (Just x)) rest
reversibly :: PrologIO a -> PrologIO () -> PrologStream a
reversibly forward backward =
PYes Suspend $ forward >>= \v -> return $
PYes (Yield v) $ backward >>= \_ -> return $
PNo
liftPrologIO :: PrologIO a -> PrologStream a
liftPrologIO forward = reversibly forward (return ())
instance LiftIO PrologStream where
liftIO x = liftPrologIO (liftIO x)

View File

@ -1,20 +1,25 @@
{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE InstanceSigs #-}
module Primitives module Primitives
( PrologIO(..) ( Globals(..)
, Globals , PrologIO(..)
, newGlobals , newGlobals
, newVarPrim, unnewVarPrim , newVarPrim, unnewVarPrim, getVarPrim, setVarPrim, errorPrim
, getVarPrim, setVarPrim ) where
, errorPrim import Classes
)
where
import Data.IORef
import qualified Data.IntMap.Strict as M import qualified Data.IntMap.Strict as M
import Data.IORef
import Terms import Terms
import Classes (LiftIO(..))
newtype PrologIO a = PrologIO { runPrologIO :: Globals -> IO a } newtype PrologIO a = PrologIO { runPrologIO :: Globals -> IO a }
data Globals = Globals
{ values :: IORef (M.IntMap Term)
, names :: IORef (M.IntMap Name)
, nextSlot :: IORef Int
}
instance LiftIO PrologIO where
liftIO :: IO a -> PrologIO a
liftIO io = PrologIO (\_ -> io)
instance Functor PrologIO where instance Functor PrologIO where
fmap :: (a -> b) -> PrologIO a -> PrologIO b fmap :: (a -> b) -> PrologIO a -> PrologIO b
@ -24,17 +29,19 @@ instance Applicative PrologIO where
pure :: a -> PrologIO a pure :: a -> PrologIO a
pure x = PrologIO (\_ -> pure x) pure x = PrologIO (\_ -> pure x)
(<*>) :: PrologIO (a -> b) -> PrologIO a -> PrologIO b
(PrologIO f) <*> (PrologIO x) = PrologIO ( (PrologIO f) <*> (PrologIO x) = PrologIO (
\globals -> do \globals -> do
f' <- f globals f' <- f globals
x' <- x globals x' <- x globals
pure (f' x') return (f' x')
) )
instance Monad PrologIO where instance Monad PrologIO where
return :: a -> PrologIO a return :: a -> PrologIO a
return = pure return = pure
(>>=) :: PrologIO a -> (a -> PrologIO b) -> PrologIO b
(PrologIO a) >>= f = PrologIO ( (PrologIO a) >>= f = PrologIO (
\globals -> do \globals -> do
a' <- a globals a' <- a globals
@ -42,44 +49,33 @@ instance Monad PrologIO where
b' globals b' globals
) )
data Globals = Globals newGlobals :: IO Globals
{ values :: IORef (M.IntMap Term)
, names :: IORef (M.IntMap Name)
, nextVar :: IORef Int
}
newGlobals :: IO (Globals)
newGlobals = do newGlobals = do
vs <- newIORef (M.empty) vs <- newIORef M.empty
ns <- newIORef (M.empty) ns <- newIORef M.empty
next <- newIORef 0 next <- newIORef 0
return $ Globals { values = vs, names = ns, nextVar = next } return Globals { values=vs, names=ns, nextSlot=next }
newVarPrim :: Name -> PrologIO Var newVarPrim :: Name -> PrologIO Slot
newVarPrim name = PrologIO $ \globals -> do newVarPrim name = PrologIO $ \globals -> do
var <- readIORef (nextVar globals) slot <- readIORef (nextSlot globals)
modifyIORef (names globals) (M.insert var name) modifyIORef (names globals) (M.insert slot name)
modifyIORef (nextVar globals) succ modifyIORef (nextSlot globals) (\s -> s + 1)
return (VInt var) return (Slot slot)
unnewVarPrim :: PrologIO () unnewVarPrim :: PrologIO ()
unnewVarPrim = PrologIO $ \globals -> do unnewVarPrim = PrologIO $ \globals -> do
modifyIORef (nextVar globals) pred modifyIORef (nextSlot globals) (\s -> s - 1)
var <- readIORef (nextVar globals) slot <- readIORef (nextSlot globals)
modifyIORef (names globals) (M.delete var) modifyIORef (names globals) (M.delete slot)
return ()
getVarPrim :: Var -> PrologIO (Maybe Term) getVarPrim :: Slot -> PrologIO (Maybe Term)
getVarPrim (VInt var) = PrologIO $ \globals -> getVarPrim (Slot slot) = PrologIO $ \globals ->
fmap (M.lookup var) (readIORef (values globals)) fmap (M.lookup slot) (readIORef (values globals))
setVarPrim :: Var -> Maybe Term -> PrologIO () setVarPrim :: Slot -> Maybe Term -> PrologIO ()
setVarPrim (VInt var) term = PrologIO $ \globals -> do setVarPrim (Slot slot) term = PrologIO $ \globals ->
modifyIORef (values globals) (M.alter (\_ -> term) var) modifyIORef (values globals) (M.alter (\_ -> term) slot)
return ()
errorPrim :: String -> PrologIO a errorPrim :: String -> PrologIO a
errorPrim message = error message errorPrim message = error message
instance LiftIO PrologIO where
liftIO x = PrologIO (\_ -> x)

View File

@ -1,17 +1,17 @@
module Terms( module Terms
Atom, ( Atom
Name, , Name
Term(..), , Term(..)
Var(..), , Slot(..)
) where ) where
type Atom = String type Atom = String
type Name = String type Name = String
data Term data Term
= Compound Atom [Term] -- functor, args = Compound Atom [Term]
| Var Var | Var Slot
deriving (Eq, Show) deriving (Eq, Show)
newtype Var = VInt Int newtype Slot = Slot Int
deriving (Ord, Eq, Show) deriving (Eq, Show)