Created
June 20, 2010 12:00
-
-
Save boggle/445777 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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