Skip to content

Instantly share code, notes, and snippets.

@bkyrlach
Last active December 21, 2015 04:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bkyrlach/6253215 to your computer and use it in GitHub Desktop.
Save bkyrlach/6253215 to your computer and use it in GitHub Desktop.
Some thoughts on implementing a logic DSL in Scala.
object Jealousy {
import Scalog._
def main(args: Array[String]): Unit = {
implicit val knowledge =
Functor("loves", "vincent", "mia") ::
Functor("loves", "marsellus", "mia") ::
Functor("loves", "pumpkin", "honey_bunny") ::
Functor("loves", "honey_bunny", "pumpkin") ::
Rule(Functor("jealous", "X", "Y"), Functor("loves", "X", "Z") and Functor("loves", "Y", "Z")) ::
Nil
println(query(Functor("jealous", "marsellus" ,"W")))
}
}
import java.util.UUID
abstract class LogicElement {
def and(t: LogicElement): And = this match {
case And(terms) => And(t :: terms)
case _ => And(t :: this :: Nil)
}
}
case class And(terms: List[LogicElement]) extends LogicElement
case class Atom[A](val v: A) extends LogicElement
case class Variable(val name: String) extends LogicElement
case class Functor(val name: String, val terms: LogicElement*) extends LogicElement {
def arity: Int = terms.size
}
case class Rule(impliciation: LogicElement, condition: LogicElement) extends LogicElement
case class LogicList(val elems: LogicElement*) extends LogicElement
case object Underscore extends LogicElement
object Scalog {
type Context = Map[String, LogicElement]
implicit def any2atom[A](a: A): Atom[A] = Atom(a)
implicit def string2element(s: String): LogicElement = if(s.head.isUpper) Variable(s) else Atom(s)
def bound(v: Variable, c: Context): Boolean = c.contains(v.name)
def unifyVarWithTerm(v: Variable, t: LogicElement)(implicit kb: List[LogicElement], c: Context): (Boolean, Context) = {
if(bound(v, c)) {
unify(c(v.name), t)
} else {
(true, c + (v.name -> t))
}
}
def unify(t1: LogicElement, t2: LogicElement)(implicit kb: List[LogicElement], c: Context): (Boolean, Context) = (t1, t2) match {
case (Atom(x), Atom(y)) => (x == y, c)
case (f1: Functor, f2: Functor) => {
if(f1.arity == f2.arity && f1.name == f2.name) {
f1.terms.zip(f2.terms).foldLeft((false, c))((acc, t) => unify(t._1, t._2)(kb, acc._2))
} else (false, c)
}
case (t: LogicElement, r: Rule) => {
val ur = unify(t, r.impliciation)
if(ur._1) query(r.condition)(kb, ur._2) else (false, c)
}
case (v1: Variable, v2: Variable) => {
if(!(c.contains(v1.name) && c.contains(v2.name))) {
val newV = new Variable(UUID.randomUUID.toString)
(true, (c + (v1.name -> newV)) + (v2.name -> newV))
} else (false, c)
}
case (v: Variable, t: LogicElement) => unifyVarWithTerm(v, t)
case (t: LogicElement, v: Variable) => unifyVarWithTerm(v, t)
case (l1: LogicList, l2: LogicList) => {
val ur = unify(l1.elems.head, l2.elems.head)
if(ur._1) {
if(l1.elems.size > 2 && l2.elems.size > 2) unify(LogicList(l1.elems.tail:_*), LogicList(l2.elems.tail:_*))(kb, ur._2) else {
if(l1.elems.size == 2) {
unify(l1.elems.tail.head, LogicList(l2.elems.tail:_*))(kb, ur._2)
} else {
unify(LogicList(l1.elems.tail:_*), l2.elems.tail.head)(kb, ur._2)
}
}
} else ur
}
case (t: LogicElement, Underscore) => (true, c)
case _ => (false, c)
}
def query(t: LogicElement)(implicit kb: List[LogicElement], c: Context = Map.empty[String, LogicElement]): (Boolean, Context) = t match {
case And(terms) => {
terms.foldLeft((true, c)){(acc, t) =>
val ur = kb.foldLeft((false, acc._2)){ (acc, k) => if(acc._1) acc else unify(t, k)(kb, acc._2) }
(acc._1 && ur._1, ur._2)
}
}
case _ => kb.foldLeft((false, c)){ (acc, k) => if(acc._1) acc else unify(t, k) }
}
}
@bkyrlach
Copy link
Author

Output for Jealousy...

(true,Map(X -> Atom(marsellus), Y -> Variable(b432a265-55ef-459c-a9b8-2c4e03e0b0f6), W -> Variable(b432a265-55ef-459c-a9b8-2c4e03e0b0f6), b432a265-55ef-459c-a9b8-2c4e03e0b0f6 -> Atom(vincent), Z -> Atom(mia)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment