graph evaluator, rewrite, general updates

This commit is contained in:
Mark Harrah 2010-06-07 08:53:21 -04:00
parent ec19be6152
commit 5eed8ccbef
18 changed files with 810 additions and 23 deletions

View File

@ -102,6 +102,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
{
override def scratch = true
override def consoleClasspath = testClasspath
override def compileOptions = super.compileOptions ++ compileOptions("-Xelide-below", "3000")
}
trait Licensed extends BasicScalaProject
{

View File

@ -0,0 +1,44 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
trait CompletionService[A, R]
{
def submit(node: A, work: () => R): () => R
def take(): R
}
import java.util.concurrent.{Callable, CompletionService => JCompletionService, Executor, Executors, ExecutorCompletionService}
object CompletionService
{
def apply[A, T](poolSize: Int): (CompletionService[A,T], () => Unit) =
{
val pool = Executors.newFixedThreadPool(2)
(apply[A,T]( pool ), () => pool.shutdownNow() )
}
def apply[A, T](x: Executor): CompletionService[A,T] =
apply(new ExecutorCompletionService[T](x))
def apply[A, T](completion: JCompletionService[T]): CompletionService[A,T] =
new CompletionService[A, T] {
def submit(node: A, work: () => T) = {
val future = completion.submit { new Callable[T] { def call = work() } }
() => future.get()
}
def take() = completion.take().get()
}
def manage[A, T](service: CompletionService[A,T])(setup: A => Unit, cleanup: A => Unit): CompletionService[A,T] =
wrap(service) { (node, work) => () =>
setup(node)
try { work() }
finally { cleanup(node) }
}
def wrap[A, T](service: CompletionService[A,T])(w: (A, () => T) => (() => T)): CompletionService[A,T] =
new CompletionService[A,T]
{
def submit(node: A, work: () => T) = service.submit(node, w(node, work))
def take() = service.take()
}
}

318
tasks/Execute.scala Normal file
View File

@ -0,0 +1,318 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
// TODO: Incomplete needs to be parameterized with A[_] and have val node
import Node._
import Types._
import Execute._
import scala.annotation.tailrec
import scala.collection.{mutable, JavaConversions}
import mutable.Map
object Execute
{
trait Part1of2K[M[_[_], _], A[_]] { type Apply[T] = M[A, T] }
type NodeT[A[_]] = Part1of2K[Node, A]
def idMap[A,B]: Map[A, B] = JavaConversions.asMap(new java.util.IdentityHashMap[A,B])
def pMap[A[_], B[_]]: PMap[A,B] = new DelegatingPMap[A, B](idMap)
private[sbt] def completed(p: => Unit): Completed = new Completed {
def process() { p }
}
}
sealed trait Completed {
def process(): Unit
}
final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: A ~> NodeT[A]#Apply )
{
type Strategy = CompletionService[A[_], Completed]
private[this] val forward = idMap[A[_], IDSet[A[_]] ]
private[this] val reverse = idMap[A[_], Iterable[A[_]] ]
private[this] val callers = pMap[A, Compose[IDSet,A]#Apply ]
private[this] val state = idMap[A[_], State]
private[this] val viewCache = pMap[A, NodeT[A]#Apply]
private[this] val results = pMap[A, Result]
private[this] type State = State.Value
private[this] object State extends Enumeration {
val Pending, Running, Calling, Done = Value
}
import State.{Pending, Running, Calling, Done}
def dump: String = "State: " + state.toString + "\n\nResults: " + results + "\n\nCalls: " + callers + "\n\n"
def run[T](root: A[T])(implicit strategy: Strategy) =
{
assert(state.isEmpty, "Execute already running/ran.")
addNew(root)
processAll()
assert( results contains root, "No result for root node." )
results(root)
}
def processAll()(implicit strategy: Strategy)
{
@tailrec def next()
{
pre {
assert( !reverse.isEmpty, "Nothing to process." )
assert( state.values.exists( _ == Running ), "Nothing running")
}
(strategy.take()).process()
if( !reverse.isEmpty ) next()
}
next()
post {
assert( reverse.isEmpty, "Did not process everything." )
assert( complete, "Not all state was Done." )
}
}
def call[T](node: A[T], target: A[T])(implicit strategy: Strategy)
{
if(checkCycles) cycleCheck(node, target)
pre {
assert( running(node) )
readyInv( node )
}
state(node) = Calling
addChecked(target)
addCaller(node, target)
post {
assert( calling(node) )
assert( callers(target) contains node )
readyInv( node )
}
}
def retire[T](node: A[T], result: Result[T])(implicit strategy: Strategy)
{
pre {
assert( running(node) | calling(node) )
readyInv( node )
}
results(node) = result
state(node) = Done
remove( reverse, node ) foreach { dep => notifyDone(node, dep) }
callers.remove( node ).flatten.foreach { c => retire(c, result) }
post {
assert( done(node) )
assert( results(node) == result )
readyInv( node )
assert( ! (reverse contains node) )
assert( ! (callers contains node) )
}
}
def notifyDone( node: A[_], dependent: A[_] )(implicit strategy: Strategy)
{
val f = forward(dependent)
f -= node
if( f.isEmpty ) {
remove(forward, dependent)
ready( dependent )
}
}
/** Ensures the given node has been added to the system.
* Once added, a node is pending until its inputs and dependencies have completed.
* Its computation is then evaluated and made available for nodes that have it as an input.*/
def addChecked[T](node: A[T])(implicit strategy: Strategy)
{
if( !added(node)) addNew(node)
post { addedInv( node ) }
}
/** Adds a node that has not yet been registered with the system.
* If all of the node's dependencies have finished, the node's computation scheduled to run.
* The node's dependencies will be added (transitively) if they are not already registered.
* */
def addNew[T](node: A[T])(implicit strategy: Strategy)
{
pre { newPre(node) }
val v = register( node )
val deps = dependencies(v)
val active = deps filter notDone
if( active.isEmpty)
ready( node )
else
{
forward(node) = IDSet(active)
val newD = active filter isNew
newD foreach { x => addNew(x) }
active foreach { addReverse(_, node) }
}
post {
addedInv( node )
assert( running(node) ^ pending(node) )
if( running(node) ) runningInv( node )
if( pending(node) ) pendingInv( node )
}
}
/** Called when a pending 'node' becomes runnable. All of its dependencies must be done. This schedules the node's computation with 'strategy'.*/
def ready[T]( node: A[T] )(implicit strategy: Strategy)
{
pre {
assert( pending(node) )
readyInv( node )
assert( reverse contains node )
}
state(node) = Running
submit(node)
post {
readyInv( node )
assert( reverse contains node )
assert( running( node ) )
}
}
/** Enters the given node into the system. */
def register[T](node: A[T]): Node[A, T] =
{
state(node) = Pending
reverse(node) = Seq()
viewCache.getOrUpdate(node, view(node))
}
def incomplete(in: A[_]): Option[(A[_], Incomplete)] =
results(in) match {
case Value(v) => None
case Inc(inc) => Some( (in, inc) )
}
/** Send the work for this node to the provided Strategy. */
def submit[T]( node: A[T] )(implicit strategy: Strategy)
{
val v = view(node)
val rs: v.Inputs#Map[Result] = v.inputs.map(results)
val ud = v.unitDependencies.flatMap(incomplete)
strategy.submit( node, () => work(node, v.work(rs, ud)) )
}
/** Evaluates the computation 'f' for 'node'.
* This returns a Completed instance, which contains the post-processing to perform after the result is retrieved from the Strategy.*/
def work[T](node: A[T], f: => Either[A[T], T])(implicit strategy: Strategy): Completed =
{
val result =
try { Right(f) }
catch {
case i: Incomplete => Left(i)
case e => Left( Incomplete(Incomplete.Error, directCause = Some(e)) )
}
completed {
result match {
case Left(i) => retire(node, Inc(i))
case Right(Right(v)) => retire(node, Value(v))
case Right(Left(target)) => call(node, target)
}
}
}
def remove[K, V](map: Map[K, V], k: K): V = map.remove(k).getOrElse(error("Key '" + k + "' not in map :\n" + map))
def addReverse(node: A[_], dependent: A[_]): Unit = reverse(node) ++= Seq(dependent)
def addCaller[T](caller: A[T], target: A[T]): Unit = callers.getOrUpdate(target, IDSet.create[A[T]]) += caller
def dependencies(node: A[_]): Iterable[A[_]] = dependencies(view(node))
def dependencies(v: Node[A, _]): Iterable[A[_]] = v.unitDependencies ++ v.inputs.toList
// Contracts
def addedInv(node: A[_]): Unit = topologicalSort(node) foreach addedCheck
def addedCheck(node: A[_])
{
assert( added(node), "Not added: " + node )
assert( viewCache contains node, "Not in view cache: " + node )
dependencyCheck( node )
}
def dependencyCheck( node: A[_] )
{
dependencies( node ) foreach { dep =>
def onOpt[T](o: Option[T])(f: T => Boolean) = o match { case None => false; case Some(x) => f(x) }
def checkForward = onOpt( forward.get(node) ) { _ contains dep }
def checkReverse = onOpt( reverse.get(dep) ){ _.toSet contains node }
assert( done(dep) ^ ( checkForward && checkReverse ) )
}
}
def pendingInv(node: A[_])
{
assert( atState(node, Pending) )
assert( dependencies( node ) exists notDone )
}
def runningInv( node: A[_] )
{
assert( dependencies( node ) forall done )
assert( ! (forward contains node) )
}
def newPre(node: A[_])
{
isNew(node)
assert(!(reverse contains node))
assert(!(forward contains node))
assert(!(callers contains node))
assert(!(viewCache contains node))
assert(!(results contains node))
}
def topologicalSort(node: A[_]): Seq[A[_]] =
{
val seen = IDSet.create[A[_]]
def visit(n: A[_]): List[A[_]] =
(seen process n)( List[A[_]]() ) {
node :: (List[A[_]]() /: dependencies(n) ) { (ss, dep) => visit(dep) ::: ss}
}
visit(node).reverse
}
def readyInv(node: A[_])
{
assert( dependencies(node) forall done )
assert( ! ( forward contains node ) )
}
// cyclic reference checking
def cycleCheck[T](node: A[T], target: A[T])
{
if(node eq target) cyclic(node, target, "Cannot call self")
val all = IDSet.create[A[T]]
def allCallers(n: A[T]): Unit = (all process n)(()) { callers.get(n).flatten.foreach(allCallers) }
allCallers(node)
if(all contains target) cyclic(node, target, "Cyclic reference")
}
def cyclic[T](caller: A[T], target: A[T], msg: String) = throw new Incomplete(message = Some(msg), directCause = Some( new CyclicException(caller, target, msg) ) )
final class CyclicException[T](val caller: A[T], val target: A[T], msg: String) extends Exception(msg)
// state testing
def pending(d: A[_]) = atState(d, Pending)
def running(d: A[_]) = atState(d, Running)
def calling(d: A[_]) = atState(d, Calling)
def done(d: A[_]) = atState(d, Done)
def notDone(d: A[_]) = !done(d)
def atState(d: A[_], s: State) = state.get(d) == Some(s)
def isNew(d: A[_]) = !added(d)
def added(d: A[_]) = state contains d
def complete = state.values.forall(_ == Done)
import scala.annotation.elidable
import elidable._
@elidable(ASSERTION) def pre(f: => Unit) = f
@elidable(ASSERTION) def post(f: => Unit) = f
}

View File

@ -3,4 +3,10 @@
*/
package sbt
trait Incomplete
import Incomplete.{Error, Value => IValue}
final case class Incomplete(tpe: IValue = Error, message: Option[String] = None, causes: Seq[Incomplete] = Nil, directCause: Option[Throwable] = None)
extends Exception(message.orNull, directCause.orNull)
object Incomplete extends Enumeration {
val Skipped, Error = Value
}

View File

@ -9,27 +9,27 @@ import Types._
trait Node[A[_], T]
{
type Inputs <: MList[A]
type Results <: Inputs#Map[Result]
type Results = Inputs#Map[Result]
val inputs: Inputs
def unitDependencies: Iterable[A[_]]
def work(results: Results, units: Iterable[Incomplete]): Either[A[T], T]
def work(results: Results, units: UnitResults[A]): Either[A[T], T]
}
object Node
{
def pure[T](f: => T): PureNode[T]= map[Id, T, MNil](MNil, Nil)((_,_) => f)
/*def pure[T](f: () => T): PureNode[T]= map[Id, T, MNil](MNil, Nil)((_,_) => f() )
def map[A[_], T, Inputs0 <: MList[A]](inputs0: Inputs0, deps0: Iterable[A[_]])(work0: (Inputs0#Map[Result], Iterable[Incomplete]) => T):
Node[A,T] { type Inputs = Inputs0; type Results = Inputs0#Map[Result] } =
def map[A[_], T, Inputs0 <: MList[A]](inputs0: Inputs0, deps0: Iterable[A[_]])(work0: (Inputs0#Map[Result], UnitResults[A]) => T):
Node[A,T] { type Inputs = Inputs0 } =
new Node[A,T] {
type Inputs = Inputs0
type Results = Inputs0#Map[Result]
val inputs = inputs0
def unitDependencies = deps0
def work(results: Results, units: Iterable[Incomplete]) = Right(work0(results, units))
def work(results: Results, units: UnitResults[A]) = Right(work0(results, units))
}
type PureNode[T] = Node[Id, T] { type Inputs = MNil; type Results = MNil }
type PureNode[T] = Node[Id, T] { type Inputs = MNil; type Results = MNil }*/
type UnitResults[A[_]] = Iterable[(A[_], Incomplete)]
}

View File

@ -3,7 +3,23 @@
*/
package sbt
// used instead of Either[Throwable, T] for type inference
// used instead of Either[Incomplete, T] for type inference
/** Result of completely evaluating a task.*/
sealed trait Result[+T]
final case class Exc(cause: Throwable) extends Result[Nothing]
final case class Value[+T](value: T) extends Result[T]
/** Indicates the task did not complete normally and so it does not have a value.*/
final case class Inc(cause: Incomplete) extends Result[Nothing]
/** Indicates the task completed normally and produced the given `value`.*/
final case class Value[+T](value: T) extends Result[T]
object Result
{
type Id[X] = X
val tryValue = new (Result ~> Id) {
def apply[T](r: Result[T]): T =
r match {
case Value(v) => v
case Inc(i) => throw i
}
}
}

114
tasks/Test.scala Normal file
View File

@ -0,0 +1,114 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Types._
import Node._
import Task._
import Execute._
sealed trait Task[+T]
sealed case class Pure[+T](eval: () => T) extends Task[T]
sealed case class Mapped[+T, In <: MList[Task]](in: In, f: In#Map[Result] => T) extends Task[T]
sealed case class MapAll[+T, In <: MList[Task]](in: In, f: In#Map[Result]#Raw => T) extends Task[T]
sealed case class FlatMapAll[+T, In <: MList[Task]](in: In, f: In#Map[Result]#Raw => Task[T]) extends Task[T]
sealed case class FlatMapped[+T, In <: MList[Task]](in: In, f: In#Map[Result] => Task[T]) extends Task[T]
object Task
{
implicit val taskToNode = new (Task ~> NodeT[Task]#Apply) {
def apply[T](t: Task[T]): Node[Task, T] = t match {
case Pure(eval) => toNode[T, MNil](MNil, _ => Right(eval()) )
case Mapped(in, f) => toNode[T, in.type](in, right f )
case MapAll(in, f) => toNode[T, in.type](in, right (f compose all) )
case FlatMapAll(in, f) => toNode[T, in.type](in, left (f compose all) )
case FlatMapped(in, f) => toNode[T, in.type](in, left f )
}
}
def toNode[T, In <: MList[Task]](in: In, f: In#Map[Result] => Either[Task[T], T]): Node[Task, T] = new Node[Task, T] {
type Inputs = In
val inputs = in
def unitDependencies = Nil
def work(results: Results, units: UnitResults[Task]) = f(results)
}
def pure[T](name: String)(f: => T): Pure[T] = new Pure(f _) { override def toString = name }
def mapped[T, In0 <: MList[Task]](name: String)(in0: In0)(f0: In0#Map[Result] => T): Mapped[T, In0] = new Mapped(in0, f0) { override def toString = name }
def flat[T, In0 <: MList[Task]](name: String)(in0: In0)(f0: In0#Map[Result] => Task[T]): FlatMapped[T, In0] = new FlatMapped(in0, f0) { override def toString = name }
def mapAll[T, In0 <: MList[Task]](name: String)(in0: In0)(f0: In0#Map[Result]#Raw => T): MapAll[T, In0] = new MapAll(in0, f0) { override def toString = name }
def flatAll[T, In0 <: MList[Task]](name: String)(in0: In0)(f0: In0#Map[Result]#Raw => Task[T]): FlatMapAll[T, In0] = new FlatMapAll(in0, f0) { override def toString = name }
def all[In <: MList[Result]]: In => In#Raw = in =>
{
val incs = in.toList.collect { case Inc(i) => i }
if(incs.isEmpty) in.down(Result.tryValue) else throw Incomplete(causes = incs)
}
}
object Test
{
val a = pure("a")(3)
val b = pure[Boolean]("b")(error("test"))
val b2 = pure("b2")(true)
val c = pure("x")("asdf")
val i3 = a :^: b :^: c :^: MNil
val i32 = a :^: b2 :^: c :^: MNil
val fh= (_: Int :+: Boolean :+: String :+: HNil) match
{ case aa :+: bb :+: cc :+: HNil => aa + " " + bb + " " + cc }
val h1 = mapAll("h1")(i3)(fh)
val h2 = mapAll("h2")(i32)(fh)
val f: i3.Map[Result] => Any = {
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => aa + " " + bb + " " + cc
case x =>
val cs = x.toList.collect { case Inc(x) => x } // workaround for double definition bug
throw Incomplete(causes = cs)
}
val d2 = mapped("d2")(i32)(f)
val f2: i3.Map[Result] => Task[Any] = {
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => new Pure(() => aa + " " + bb + " " + cc)
case x => d3
}
lazy val d = flat("d")(i3)(f2)
val f3: i3.Map[Result] => Task[Any] = {
case Value(aa) :^: Value(bb) :^: Value(cc) :^: MNil => new Pure(() => aa + " " + bb + " " + cc)
case x => d2
}
lazy val d3= flat("d3")(i3)(f3)
def d4(i: Int): Task[Int] = flat("d4")(MNil){ _ => val x = math.random; if(x < 0.01) pure(x.toString)(i); else d4(i+1) }
lazy val pureEval =
new (Pure ~> Result) {
def apply[T](p: Pure[T]): Result[T] =
try { Value(p.eval()) }
catch { case e: Exception => throw Incomplete(Incomplete.Error, directCause = Some(e)) }
}
lazy val resultA = d.f( d.in.map(pureEval) )
def execute[T](root: Task[T]) = {
val (service, shutdown) = CompletionService[Task[_], Completed](2)
implicit val wrapped = CompletionService.manage(service)(x => println("Starting: " + x), x => println("Finished: " + x) )
val x = new Execute[Task](true)(taskToNode)
try { x.run(root) } finally { shutdown(); println(x.dump) }
}
def go()
{
def run[T](root: Task[T]) =
println("Result : " + execute(root))
run(a)
run(b)
run(b2)
run(c)
run(d)
run(d2)
run( d4(0) )
run(h1)
run(h2)
}
}

View File

@ -0,0 +1,45 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
/** A mutable set interface that uses object identity to test for set membership.*/
trait IDSet[T]
{
def apply(t: T): Boolean
def contains(t: T): Boolean
def += (t: T): Unit
def ++=(t: Iterable[T]): Unit
def -= (t: T): Boolean
def all: collection.Iterable[T]
def isEmpty: Boolean
def foreach(f: T => Unit): Unit
def process[S](t: T)(ifSeen: S)(ifNew: => S): S
}
object IDSet
{
implicit def toTraversable[T]: IDSet[T] => Traversable[T] = _.all
def apply[T](values: T*): IDSet[T] = apply(values)
def apply[T](values: Iterable[T]): IDSet[T] =
{
val s = create[T]
s ++= values
s
}
def create[T]: IDSet[T] = new IDSet[T] {
private[this] val backing = new java.util.IdentityHashMap[T, AnyRef]
private[this] val Dummy: AnyRef = ""
def apply(t: T) = contains(t)
def contains(t: T) = backing.containsKey(t)
def foreach(f: T => Unit) = all foreach f
def += (t: T) = backing.put(t, Dummy)
def ++=(t: Iterable[T]) = t foreach +=
def -= (t:T) = if(backing.remove(t) eq null) false else true
def all = collection.JavaConversions.asIterable(backing.keySet)
def isEmpty = backing.isEmpty
def process[S](t: T)(ifSeen: S)(ifNew: => S) = if(contains(t)) ifSeen else { this += t ; ifNew }
override def toString = backing.toString
}
}

View File

@ -7,6 +7,11 @@ import Types._
sealed trait MList[+M[_]]
{
// For converting MList[Id] to an HList
// This is useful because type inference doesn't work well with Id
type Raw <: HList
def down(implicit ev: M ~> Id): Raw
type Map[N[_]] <: MList[N]
def map[N[_]](f: M ~> N): Map[N]
@ -14,6 +19,9 @@ sealed trait MList[+M[_]]
}
final case class MCons[H, +T <: MList[M], +M[_]](head: M[H], tail: T) extends MList[M]
{
type Raw = H :+: tail.Raw
def down(implicit f: M ~> Id): Raw = HCons(f(head), tail.down(f))
type Map[N[_]] = MCons[H, tail.Map[N], N]
def map[N[_]](f: M ~> N) = MCons( f(head), tail.map(f) )
@ -23,6 +31,9 @@ final case class MCons[H, +T <: MList[M], +M[_]](head: M[H], tail: T) extends ML
}
sealed class MNil extends MList[Nothing]
{
type Raw = HNil
def down(implicit f: Nothing ~> Id) = HNil
type Map[N[_]] = MNil
def map[N[_]](f: Nothing ~> N) = MNil
@ -30,4 +41,4 @@ sealed class MNil extends MList[Nothing]
def toList = Nil
}
object MNil extends MNil
object MNil extends MNil

View File

@ -0,0 +1,43 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Types._
trait PMap[K[_], V[_]] extends (K ~> V)
{
def apply[T](k: K[T]): V[T]
def get[T](k: K[T]): Option[V[T]]
def update[T](k: K[T], v: V[T]): Unit
def contains[T](k: K[T]): Boolean
def remove[T](k: K[T]): Option[V[T]]
def getOrUpdate[T](k: K[T], make: => V[T]): V[T]
}
object PMap
{
implicit def toFunction[K[_], V[_]](map: PMap[K,V]): K[_] => V[_] = k => map(k)
}
abstract class AbstractPMap[K[_], V[_]] extends PMap[K,V]
{
def apply[T](k: K[T]): V[T] = get(k).get
def contains[T](k: K[T]): Boolean = get(k).isDefined
}
import collection.mutable.Map
/** Only suitable for K that is invariant in its parameter.
* Option and List keys are not, for example, because None <:< Option[String] and None <: Option[Int].*/
class DelegatingPMap[K[_], V[_]](backing: Map[K[_], V[_]]) extends AbstractPMap[K,V]
{
def get[T](k: K[T]): Option[V[T]] = cast[T]( backing.get(k) )
def update[T](k: K[T], v: V[T]) { backing(k) = v }
def remove[T](k: K[T]) = cast( backing.remove(k) )
def getOrUpdate[T](k: K[T], make: => V[T]) = cast[T]( backing.getOrElseUpdate(k, make) )
private[this] def cast[T](v: V[_]): V[T] = v.asInstanceOf[V[T]]
private[this] def cast[T](o: Option[V[_]]): Option[V[T]] = o map cast[T]
override def toString = backing.toString
}

View File

@ -0,0 +1,31 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Types._
// Used to emulate ~> literals
trait Param[A[_], B[_]]
{
type T
def in: A[T]
def ret(out: B[T])
def ret: B[T]
}
object Param
{
implicit def pToT[A[_], B[_]](p: Param[A,B] => Unit): A~>B = new (A ~> B) {
def apply[s](a: A[s]): B[s] = {
val v: Param[A,B] { type T = s} = new Param[A,B] { type T = s
def in = a
private var r: B[T] = _
def ret(b: B[T]) {r = b}
def ret: B[T] = r
}
p(v)
v.ret
}
}
}

View File

@ -0,0 +1,42 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Types._
trait Rewrite[A[_]]
{
def apply[T](node: A[T], rewrite: Rewrite[A]): A[T]
}
object Rewrite
{
def Id[A[_]]: Rewrite[A] = new Rewrite[A] { def apply[T](node: A[T], rewrite: Rewrite[A]) = node }
implicit def specificF[T](f: T => T): Rewrite[Const[T]#Apply] = new Rewrite[Const[T]#Apply] {
def apply[S](node:T, rewrite: Rewrite[Const[T]#Apply]): T = f(node)
}
implicit def pToRewrite[A[_]](p: Param[A,A] => Unit): Rewrite[A] = toRewrite(Param.pToT(p))
implicit def toRewrite[A[_]](f: A ~> A): Rewrite[A] = new Rewrite[A] {
def apply[T](node: A[T], rewrite:Rewrite[A]) = f(node)
}
def compose[A[_]](a: Rewrite[A], b: Rewrite[A]): Rewrite[A] =
new Rewrite[A] {
def apply[T](node: A[T], rewrite: Rewrite[A]) =
a(b(node, rewrite), rewrite)
}
implicit def rewriteOps[A[_]](outer: Rewrite[A]): RewriteOps[A] =
new RewriteOps[A] {
def (g: A ~> A): Rewrite[A] = compose(outer, g)
def andThen(g: A ~> A): Rewrite[A] = compose(g, outer)
def (g: Rewrite[A]): Rewrite[A] = compose(outer, g)
def andThen(g: Rewrite[A]): Rewrite[A] = compose(g, outer)
}
def apply[A[_], T](value: A[T])(implicit rewrite: Rewrite[A]): A[T] = rewrite(value, rewrite)
}
trait RewriteOps[A[_]]
{
def andThen(g: A ~> A): Rewrite[A]
def (g: A ~> A): Rewrite[A]
def (g: Rewrite[A]): Rewrite[A]
}

View File

@ -7,14 +7,26 @@ trait TypeFunctions
{
type Id[X] = X
trait Const[A] { type Apply[B] = A }
trait P1of2[M[_,_], A] { type Apply[B] = M[A,B] }
trait Down[M[_]] { type Apply[B] = Id[M[B]] }
trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] }
trait P1of2[M[_,_], A] { type Apply[B] = M[A,B]; type Flip[B] = M[B, A] }
trait ~>[-A[_], +B[_]]
{
def apply[T](a: A[T]): B[T]
}
def Id: Id ~> Id =
new (Id ~> Id) { def apply[T](a: T): T = a }
final val left = new (Id ~> P1of2[Left, Nothing]#Flip) { def apply[T](t: T) = Left(t) }
final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) }
final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) }
}
object TypeFunctions extends TypeFunctions
object TypeFunctions extends TypeFunctions
trait ~>[-A[_], +B[_]]
{ outer =>
def apply[T](a: A[T]): B[T]
// directly on ~> because of type inference limitations
final def [C[_]](g: C ~> A): C ~> B = new (C ~> B) { def apply[T](c: C[T]) = outer.apply(g(c)) }
final def [C,D](g: C => D)(implicit ev: D <:< A[D]): C => B[D] = i => apply(ev(g(i)) )
final def fn[T] = (t: A[T]) => apply[T](t)
}
object ~>
{
import TypeFunctions._
val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a }
implicit def tcIdEquals: (Id ~> Id) = Id
}

View File

@ -8,4 +8,4 @@ object Types extends TypeFunctions
val :^: = MCons
val :+: = HCons
type :+:[H, T <: HList] = HCons[H,T]
}
}

View File

@ -0,0 +1,17 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Types._
// compilation test
object LiteralTest {
def x[A[_],B[_]](f: A ~> B) = f
import Param._
val f = x { (p: Param[Option,List]) => p.ret( p.in.toList ) }
val a: List[Int] = f( Some(3) )
val b: List[String] = f( Some("aa") )
}

View File

@ -0,0 +1,19 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Types._
object MTest {
val f = new (Option ~> List) { def apply[T](o: Option[T]): List[T] = o.toList }
val x = Some(3) :^: Some("asdf") :^: MNil
val y = x map f
val m1a = y match { case List(3) :^: List("asdf") :^: MNil => println("true") }
val m1b = (List(3) :^: MNil) match { case yy :^: MNil => println("true") }
val head = new (List ~> Id) { def apply[T](xs: List[T]): T = xs.head }
val z = y.map[Id](head).down
val m2 = z match { case 3 :+: "asdf" :+: HNil => println("true") }
}

View File

@ -0,0 +1,18 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Types._
// compilation test
object PMapTest
{
val mp = new DelegatingPMap[Some, Id](new collection.mutable.HashMap)
mp(Some("asdf")) = "a"
mp(Some(3)) = 9
val x = Some(3) :^: Some("asdf") :^: MNil
val y = x.map[Id](mp)
val z = y.down
z match { case 9 :+: "a" :+: HNil => println("true") }
}

View File

@ -0,0 +1,50 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import Types._
object RewriteTest
{
// dist and add0 show the awkwardness when not just manipulating superstructure:
// would have to constrain the parameters to Term to be instances of Zero/Eq somehow
val dist: Rewrite[Term] = (p: Param[Term, Term]) => p.ret( p.in match {
case Add(Mult(a,b),Mult(c,d)) if a == c=> Mult(a, Add(b,d))
case x => x
})
val add0: Rewrite[Term] = (p: Param[Term, Term]) => p.ret( p.in match {
case Add(V(0), y) => y
case Add(x, V(0)) => x
case x => x
})
val rewriteBU= new Rewrite[Term] {
def apply[T](node: Term[T], rewrite: Rewrite[Term]) = {
def r[T](node: Term[T]) = rewrite(node, rewrite)
node match {
case Add(x, y) => Add(r(x), r(y))
case Mult(x, y) => Mult(r(x), r(y))
case x => x
}
}
}
val d2 = dist add0 rewriteBU
implicit def toV(t: Int): V[Int] = V(t)
implicit def toVar(s: String): Var[Int] = Var[Int](s)
val t1: Term[Int] = Add(Mult(3,4), Mult(4, 5))
val t2: Term[Int] = Add(Mult(4,4), Mult(4, 5))
val t3: Term[Int] = Add(Mult(Add("x", 0),4), Mult("x", 5))
println( Rewrite(t1)(d2) )
println( Rewrite(t2)(d2) )
println( Rewrite(t3)(d2) )
}
sealed trait Term[T]
final case class Add[T](a: Term[T], b: Term[T]) extends Term[T]
final case class Mult[T](a: Term[T], b: Term[T]) extends Term[T]
final case class V[T](v: T) extends Term[T]
final case class Var[T](name: String) extends Term[T]