module Language.DSKanren.Core ( Term(..)
, Var
, Neq
, (===)
, (=/=)
, fresh
, conj
, disconj
, Predicate
, failure
, success
, currentGoal
, run ) where
import Control.Monad.Logic
import Data.String
import qualified Data.Map as M
newtype Var = V Integer deriving (Eq, Ord)
instance Show Var where
show (V i) = '_' : show i
suc :: Var -> Var
suc (V i) = V (i + 1)
data Term = Var Var
| Atom String
| Pair Term Term
deriving Eq
instance Show Term where
show t = case t of
Var v -> show v
Atom a -> '\'' : a
Pair l r -> "(" ++ show l ++ ", " ++ show r ++ ")"
instance IsString Term where
fromString = Atom
type Sol = M.Map Var Term
canonize :: Sol -> Term -> Term
canonize sol t = case t of
Atom a -> Atom a
Pair l r -> canonize sol l `Pair` canonize sol r
Var i -> maybe (Var i) (canonize $ M.delete i sol) $ M.lookup i sol
notIn :: Var -> Term -> Bool
notIn v t = case t of
Var v' -> v /= v'
Atom _ -> True
Pair l r -> notIn v l && notIn v r
extend :: Var -> Term -> Sol -> Sol
extend = M.insert
unify :: Term -> Term -> Sol -> Maybe Sol
unify l r sol= case (l, r) of
(Atom a, Atom a') | a == a' -> Just sol
(Pair h t, Pair h' t') -> unify h h' sol >>= unify t t'
(Var i, Var j) | i == j -> Just sol
(Var i, t) | i `notIn` t -> Just (extend i t sol)
(t, Var i) | i `notIn` t -> Just (extend i t sol)
_ -> Nothing
type Neq = (Term, Term)
data State = State { sol :: Sol
, var :: Var
, neq :: [Neq] }
newtype Predicate = Predicate {unPred :: State -> Logic State}
checkNeqs :: State -> Logic State
checkNeqs s@State{..} = foldr go (return s) neq
where go (l, r) m =
case unify (canonize sol l) (canonize sol r) sol of
Nothing -> m
Just badSol -> if domain badSol == domain sol then mzero else m
domain = M.keys
(===) :: Term -> Term -> Predicate
(===) l r = Predicate $ \s@State {..} ->
case unify (canonize sol l) (canonize sol r) sol of
Just sol' -> checkNeqs s{sol = sol'}
Nothing -> mzero
(=/=) :: Term -> Term -> Predicate
(=/=) l r = Predicate $ \s@State{..} -> checkNeqs s{neq = (l, r) : neq}
fresh :: (Term -> Predicate) -> Predicate
fresh withTerm =
Predicate $ \State{..} ->
unPred (withTerm $ Var var) $ State sol (suc var) neq
conj :: Predicate -> Predicate -> Predicate
conj p1 p2 = Predicate $ \s -> unPred p1 s >>- unPred p2
disconj :: Predicate -> Predicate -> Predicate
disconj p1 p2 = Predicate $ \s -> unPred p1 s `interleave` unPred p2 s
failure :: Predicate
failure = Predicate $ const mzero
success :: Predicate
success = Predicate return
currentGoal :: Term
currentGoal = Var (V 0)
run :: (Term -> Predicate) -> [(Term, [Neq])]
run mkProg = map answer . observeAll $ prog
where prog = unPred (fresh mkProg) (State M.empty (V 0) [])
answer State{..} = (canonize sol . Var $ V 0, neq)