Skip to content

Instantly share code, notes, and snippets.

@boggle
Created June 20, 2010 12:00
Show Gist options
  • Save boggle/445777 to your computer and use it in GitHub Desktop.
Save boggle/445777 to your computer and use it in GitHub Desktop.
/**
* Dynamic Pattern Matching in the spirit of
* Geller Hirschfeld Bracha 2010
*
* @author Stefan Plantikow <[email protected]>
*/
package be.bolder.dymatch
/**
* Instances classify bindings as successful match and may construct a pattern's "failed" counterpart
*/
trait Judge[B] {
def isSuccessfulMatch(binding: B): Boolean
def failed(binding: B): B
}
/**
* Pattern for matching subjects of type S into bindings of type B
*/
abstract class Pattern[-S, +B](implicit judge: Judge[B]) extends (S => B) {
/**
* New pattern that wraps result binding with f
*/
def ==>[C: Judge](f: B => C): Pattern[S, C] = {
val thisPattern = this
return Pat[S, C]({ (subj: S) =>
val binding = thisPattern(subj)
if (judge.isSuccessfulMatch(binding))
f(binding)
else {
val result = f(binding)
Pattern.judge(result).failed(result)
}
})
}
/**
* Pattern alternative, first successfully matching pattern wins
*/
def <|>[T <: S, C >: B: Judge](other: Pattern[T, C]): Pattern[T, C] = {
val thisPattern = this
return Pat[T, C]({ (subj: T) =>
val binding = thisPattern(subj)
if (judge.isSuccessfulMatch(binding)) binding else other(subj)
})
}
/**
* Parallel pattern execution, result bindings are merged by calling merge
*/
def <+>[T <: S, C >: B: Judge, D: Judge](merge: (B, C) => D)(other: Pattern[T, C]): Pattern[T, D] = {
val thisPattern = this
return Pat[T, D]({ (subj: T) =>
val fst = thisPattern(subj)
val snd = other(subj)
val mrg = merge(fst, snd)
if (judge.isSuccessfulMatch(fst) && Pattern.judge(snd).isSuccessfulMatch(snd))
mrg
else
Pattern.judge(mrg).failed(mrg)
})
}
/**
* Pattern sequencing/chaining
*/
def ::>[C: Judge](fby: Pattern[B, C]): Pattern[S, C] = {
val thisPattern = this
return Pat[S, C]({ (subj: S) =>
val binding = thisPattern(subj)
val result = fby(binding)
if (judge.isSuccessfulMatch(binding) == Pattern.judge(result).isSuccessfulMatch(result))
result
else
Pattern.judge(result).failed(result)
})
}
}
trait InvJudge[B] extends Judge[B] {
def inv(pat: B): B
}
abstract class InvPattern[-S, +B](implicit invJudge: InvJudge[B]) extends Pattern[S, B] {
/**
* Apply pattern and return inverted binding
*/
lazy val ! : InvPattern[S, B] = {
val thisPattern = this
new InvPattern[S, B] {
override lazy val ! : InvPattern[S, B] = thisPattern
def apply(subj: S): B = invJudge.inv(thisPattern(subj))
}
}
}
object Pattern {
/**
* Helper for obtaining the judge of a binding
*/
def judge[X](binding: X)(implicit judge: Judge[X]): Judge[X] = judge
class OptionJudge[A] extends Judge[Option[A]] {
def isSuccessfulMatch(binding: Option[A]) = binding match {
case None => false
case Some(_) => true
}
def failed(binding: Option[A]): Option[A] = None.asInstanceOf[Option[A]]
}
implicit def option2judge[X](opt: Option[X]): Judge[Option[X]] = new OptionJudge
}
case class Pat[-S, +B: Judge](val matchFun: S => B) extends Pattern[S, B] { def apply(subj: S): B = matchFun(subj) }
object InvPattern {
/**
* Helper for obtaining the invJudge of a binding from context
*/
def invJudge[X](binding: X)(implicit invJudge: InvJudge[X]): InvJudge[X] = invJudge
class EitherJudge[A] extends InvJudge[Either[A, A]] {
def isSuccessfulMatch(binding: Either[A, A]) = binding.isRight
def failed(binding: Either[A, A]): Either[A, A] = if (binding.isRight) inv(binding) else binding
def inv(binding: Either[A, A]): Either[A, A] = binding.swap
}
implicit def either2judge[X](either: Either[X, X]): Judge[Either[X, X]] = new EitherJudge[X]()
implicit object booleanJudge extends Judge[Boolean] {
def isSuccessfulMatch(b: Boolean) = b
def failed(b: Boolean) = false
def inv(b: Boolean) = !b
}
implicit object intJudge extends Judge[Int] {
def isSuccessfulMatch(i: Int) = i > 0
def failed(i: Int) = if (isSuccessfulMatch(i)) inv(i) else i
def inv(i: Int) = - i + 1
}
}
case class InvPat[-S, +B: InvJudge](val matchFun: S => B) extends InvPattern[S, B] {
def apply(subj: S): B = matchFun(subj)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment