mirror of https://github.com/sbt/sbt.git
graph evaluator, rewrite, general updates
This commit is contained in:
parent
ec19be6152
commit
5eed8ccbef
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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]
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -8,4 +8,4 @@ object Types extends TypeFunctions
|
|||
val :^: = MCons
|
||||
val :+: = HCons
|
||||
type :+:[H, T <: HList] = HCons[H,T]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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") )
|
||||
}
|
||||
|
|
@ -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") }
|
||||
}
|
||||
|
|
@ -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") }
|
||||
}
|
||||
|
|
@ -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]
|
||||
Loading…
Reference in New Issue