mirror of https://github.com/sbt/sbt.git
Cats-like typeclasses
This commit is contained in:
parent
1d027ec655
commit
00eba85d98
|
|
@ -1,5 +1,6 @@
|
|||
version = 2.3.2
|
||||
edition = 2019-10
|
||||
version = 3.2.1
|
||||
runner.dialect = scala3
|
||||
|
||||
maxColumn = 100
|
||||
project.git = true
|
||||
project.excludeFilters = [ "\\Wsbt-test\\W", "\\Winput_sources\\W", "\\Wcontraband-scala\\W" ]
|
||||
|
|
@ -7,7 +8,7 @@ lineEndings = preserve
|
|||
|
||||
# https://docs.scala-lang.org/style/scaladoc.html recommends the JavaDoc style.
|
||||
# scala/scala is written that way too https://github.com/scala/scala/blob/v2.12.2/src/library/scala/Predef.scala
|
||||
docstrings = JavaDoc
|
||||
docstrings.style = Asterisk
|
||||
|
||||
# This also seems more idiomatic to include whitespace in import x.{ yyy }
|
||||
spaces.inImportCurlyBraces = true
|
||||
|
|
@ -18,7 +19,7 @@ align.openParenCallSite = false
|
|||
align.openParenDefnSite = false
|
||||
|
||||
# For better code clarity
|
||||
danglingParentheses = true
|
||||
danglingParentheses.preset = true
|
||||
|
||||
trailingCommas = preserve
|
||||
|
||||
|
|
|
|||
41
build.sbt
41
build.sbt
|
|
@ -180,8 +180,8 @@ def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[_]] = Def settings
|
|||
val scriptedSbtReduxMimaSettings = Def.settings(mimaPreviousArtifacts := Set())
|
||||
|
||||
lazy val sbtRoot: Project = (project in file("."))
|
||||
// .enablePlugins(ScriptedPlugin)
|
||||
.aggregate(nonRoots: _*)
|
||||
// .aggregate(nonRoots: _*)
|
||||
.aggregate(collectionProj)
|
||||
.settings(
|
||||
minimalSettings,
|
||||
onLoadMessage := {
|
||||
|
|
@ -256,49 +256,20 @@ lazy val bundledLauncherProj =
|
|||
|
||||
/* ** subproject declarations ** */
|
||||
|
||||
val collectionProj = (project in file("internal") / "util-collection")
|
||||
val collectionProj = (project in file("util-collection"))
|
||||
.dependsOn(utilPosition)
|
||||
.settings(
|
||||
name := "Collections",
|
||||
testedBaseSettings,
|
||||
utilCommonSettings,
|
||||
Util.keywordsSettings,
|
||||
name := "Collections",
|
||||
libraryDependencies ++= Seq(sjsonNewScalaJson.value),
|
||||
libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
|
||||
case Some((2, major)) if major <= 12 => Seq()
|
||||
case _ => Seq("org.scala-lang.modules" %% "scala-parallel-collections" % "0.2.0")
|
||||
case _ => Seq(scalaPar)
|
||||
}),
|
||||
mimaSettings,
|
||||
mimaBinaryIssueFilters ++= Seq(
|
||||
// Added private[sbt] method to capture State attributes.
|
||||
exclude[ReversedMissingMethodProblem]("sbt.internal.util.AttributeMap.setCond"),
|
||||
// Dropped in favour of kind-projector's inline type lambda syntax
|
||||
exclude[MissingClassProblem]("sbt.internal.util.TypeFunctions$P1of2"),
|
||||
// Dropped in favour of kind-projector's polymorphic lambda literals
|
||||
exclude[MissingClassProblem]("sbt.internal.util.Param"),
|
||||
exclude[MissingClassProblem]("sbt.internal.util.Param$"),
|
||||
// Dropped in favour of plain scala.Function, and its compose method
|
||||
exclude[MissingClassProblem]("sbt.internal.util.Fn1"),
|
||||
exclude[DirectMissingMethodProblem]("sbt.internal.util.TypeFunctions.toFn1"),
|
||||
exclude[DirectMissingMethodProblem]("sbt.internal.util.Types.toFn1"),
|
||||
// Instead of defining foldr in KList & overriding in KCons,
|
||||
// it's now abstract in KList and defined in both KCons & KNil.
|
||||
exclude[FinalMethodProblem]("sbt.internal.util.KNil.foldr"),
|
||||
exclude[DirectAbstractMethodProblem]("sbt.internal.util.KList.foldr"),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Init*.*"),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Settings0.*"),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#INode.*"),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.TypeFunctions.*"),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings.*"),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Settings.*"),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#MixedNode.*"),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.EvaluateSettings#BindNode.this"),
|
||||
exclude[IncompatibleSignatureProblem](
|
||||
"sbt.internal.util.EvaluateSettings#BindNode.dependsOn"
|
||||
),
|
||||
exclude[IncompatibleSignatureProblem]("sbt.internal.util.Types.some")
|
||||
),
|
||||
)
|
||||
.dependsOn(utilPosition)
|
||||
|
||||
// Command line-related utilities.
|
||||
val completeProj = (project in file("internal") / "util-complete")
|
||||
|
|
|
|||
|
|
@ -18,16 +18,17 @@ object ContextUtil {
|
|||
|
||||
/**
|
||||
* Constructs an object with utility methods for operating in the provided macro context `c`.
|
||||
* Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types.
|
||||
* Callers should explicitly specify the type parameter as `c.type` in order to preserve the path
|
||||
* dependent types.
|
||||
*/
|
||||
def apply[C <: blackbox.Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c: C)
|
||||
|
||||
/**
|
||||
* Helper for implementing a no-argument macro that is introduced via an implicit.
|
||||
* This method removes the implicit conversion and evaluates the function `f` on the target of the conversion.
|
||||
* Helper for implementing a no-argument macro that is introduced via an implicit. This method
|
||||
* removes the implicit conversion and evaluates the function `f` on the target of the conversion.
|
||||
*
|
||||
* Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro that uses this
|
||||
* method, the result of this method is `f(<Tree of someValue>)`.
|
||||
* Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro
|
||||
* that uses this method, the result of this method is `f(<Tree of someValue>)`.
|
||||
*/
|
||||
def selectMacroImpl[T: c.WeakTypeTag](
|
||||
c: blackbox.Context
|
||||
|
|
@ -46,10 +47,9 @@ object ContextUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* Utility methods for macros. Several methods assume that the context's universe is a full compiler
|
||||
* (`scala.tools.nsc.Global`).
|
||||
* This is not thread safe due to the underlying Context and related data structures not being thread safe.
|
||||
* Use `ContextUtil[c.type](c)` to construct.
|
||||
* Utility methods for macros. Several methods assume that the context's universe is a full compiler
|
||||
* (`scala.tools.nsc.Global`). This is not thread safe due to the underlying Context and related
|
||||
* data structures not being thread safe. Use `ContextUtil[c.type](c)` to construct.
|
||||
*/
|
||||
final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
||||
import ctx.universe.{ Apply => ApplyTree, _ }
|
||||
|
|
@ -64,20 +64,20 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
lazy val alist: Symbol = alistType.typeSymbol.companion
|
||||
lazy val alistTC: Type = alistType.typeConstructor
|
||||
|
||||
/** Modifiers for a local val.*/
|
||||
/** Modifiers for a local val. */
|
||||
lazy val localModifiers = Modifiers(NoFlags)
|
||||
|
||||
def getPos(sym: Symbol) = if (sym eq null) NoPosition else sym.pos
|
||||
|
||||
/**
|
||||
* Constructs a unique term name with the given prefix within this Context.
|
||||
* (The current implementation uses Context.freshName, which increments
|
||||
* Constructs a unique term name with the given prefix within this Context. (The current
|
||||
* implementation uses Context.freshName, which increments
|
||||
*/
|
||||
def freshTermName(prefix: String) = TermName(ctx.freshName("$" + prefix))
|
||||
|
||||
/**
|
||||
* Constructs a new, synthetic, local ValDef Type `tpe`, a unique name,
|
||||
* Position `pos`, an empty implementation (no rhs), and owned by `owner`.
|
||||
* Constructs a new, synthetic, local ValDef Type `tpe`, a unique name, Position `pos`, an empty
|
||||
* implementation (no rhs), and owned by `owner`.
|
||||
*/
|
||||
def freshValDef(tpe: Type, pos: Position, owner: Symbol): ValDef = {
|
||||
val SYNTHETIC = (1 << 21).toLong.asInstanceOf[FlagSet]
|
||||
|
|
@ -91,8 +91,8 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
lazy val parameterModifiers = Modifiers(Flag.PARAM)
|
||||
|
||||
/**
|
||||
* Collects all definitions in the tree for use in checkReferences.
|
||||
* This excludes definitions in wrapped expressions because checkReferences won't allow nested dereferencing anyway.
|
||||
* Collects all definitions in the tree for use in checkReferences. This excludes definitions in
|
||||
* wrapped expressions because checkReferences won't allow nested dereferencing anyway.
|
||||
*/
|
||||
def collectDefs(
|
||||
tree: Tree,
|
||||
|
|
@ -119,8 +119,8 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
}
|
||||
|
||||
/**
|
||||
* A reference is illegal if it is to an M instance defined within the scope of the macro call.
|
||||
* As an approximation, disallow referenced to any local definitions `defs`.
|
||||
* A reference is illegal if it is to an M instance defined within the scope of the macro call. As
|
||||
* an approximation, disallow referenced to any local definitions `defs`.
|
||||
*/
|
||||
def illegalReference(defs: collection.Set[Symbol], sym: Symbol, mType: Type): Boolean =
|
||||
sym != null && sym != NoSymbol && defs.contains(sym) && {
|
||||
|
|
@ -131,8 +131,8 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
}
|
||||
|
||||
/**
|
||||
* A reference is illegal if it is to an M instance defined within the scope of the macro call.
|
||||
* As an approximation, disallow referenced to any local definitions `defs`.
|
||||
* A reference is illegal if it is to an M instance defined within the scope of the macro call. As
|
||||
* an approximation, disallow referenced to any local definitions `defs`.
|
||||
*/
|
||||
def illegalReference(defs: collection.Set[Symbol], sym: Symbol): Boolean =
|
||||
illegalReference(defs, sym, weakTypeOf[Any])
|
||||
|
|
@ -141,7 +141,7 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
|
||||
/**
|
||||
* A function that checks the provided tree for illegal references to M instances defined in the
|
||||
* expression passed to the macro and for illegal dereferencing of M instances.
|
||||
* expression passed to the macro and for illegal dereferencing of M instances.
|
||||
*/
|
||||
def checkReferences(
|
||||
defs: collection.Set[Symbol],
|
||||
|
|
@ -160,12 +160,15 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
@deprecated("Use that variant that specifies the M instance types to exclude", since = "1.3.0")
|
||||
/**
|
||||
* A function that checks the provided tree for illegal references to M instances defined in the
|
||||
* expression passed to the macro and for illegal dereferencing of M instances.
|
||||
* expression passed to the macro and for illegal dereferencing of M instances.
|
||||
*/
|
||||
def checkReferences(defs: collection.Set[Symbol], isWrapper: PropertyChecker): Tree => Unit =
|
||||
checkReferences(defs, isWrapper, weakTypeOf[Any])
|
||||
|
||||
/** Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs. */
|
||||
/**
|
||||
* Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with
|
||||
* an empty rhs.
|
||||
*/
|
||||
def freshMethodParameter(tpe: Type): ValDef =
|
||||
ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree)
|
||||
|
||||
|
|
@ -173,7 +176,7 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
def localValDef(tpt: Tree, rhs: Tree): ValDef =
|
||||
ValDef(localModifiers, freshTermName("q"), tpt, rhs)
|
||||
|
||||
/** Constructs a tuple value of the right TupleN type from the provided inputs.*/
|
||||
/** Constructs a tuple value of the right TupleN type from the provided inputs. */
|
||||
def mkTuple(args: List[Tree]): Tree =
|
||||
global.gen.mkTuple(args.asInstanceOf[List[global.Tree]]).asInstanceOf[ctx.universe.Tree]
|
||||
|
||||
|
|
@ -202,7 +205,10 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
/** A Type that references the given type variable. */
|
||||
def refVar(variable: TypeSymbol): Type = variable.toTypeConstructor
|
||||
|
||||
/** Constructs a new, synthetic type variable that is a type constructor. For example, in type Y[L[x]], L is such a type variable. */
|
||||
/**
|
||||
* Constructs a new, synthetic type variable that is a type constructor. For example, in type
|
||||
* Y[L[x]], L is such a type variable.
|
||||
*/
|
||||
def newTCVariable(owner: Symbol): TypeSymbol = {
|
||||
val tc = newTypeVariable(owner)
|
||||
val arg = newTypeVariable(tc, "x");
|
||||
|
|
@ -226,11 +232,17 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
tpe.asInstanceOf[Type]
|
||||
}
|
||||
|
||||
/** Create a Tree that references the `val` represented by `vd`, copying attributes from `replaced`. */
|
||||
/**
|
||||
* Create a Tree that references the `val` represented by `vd`, copying attributes from
|
||||
* `replaced`.
|
||||
*/
|
||||
def refVal(replaced: Tree, vd: ValDef): Tree =
|
||||
treeCopy.Ident(replaced, vd.name).setSymbol(vd.symbol)
|
||||
|
||||
/** Creates a Function tree using `functionSym` as the Symbol and changing `initialOwner` to `functionSym` in `body`.*/
|
||||
/**
|
||||
* Creates a Function tree using `functionSym` as the Symbol and changing `initialOwner` to
|
||||
* `functionSym` in `body`.
|
||||
*/
|
||||
def createFunction(params: List[ValDef], body: Tree, functionSym: Symbol): Tree = {
|
||||
changeOwner(body, initialOwner, functionSym)
|
||||
val f = Function(params, body)
|
||||
|
|
@ -262,7 +274,7 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
def singleton[T <: AnyRef with Singleton](i: T)(implicit it: ctx.TypeTag[i.type]): Symbol =
|
||||
it.tpe match {
|
||||
case SingleType(_, sym) if !sym.isFreeTerm && sym.isStatic => sym
|
||||
case x => sys.error("Instance must be static (was " + x + ").")
|
||||
case x => sys.error("Instance must be static (was " + x + ").")
|
||||
}
|
||||
|
||||
def select(t: Tree, name: String): Tree = Select(t, TermName(name))
|
||||
|
|
@ -275,12 +287,12 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a Type representing the type constructor tcp.<name>. For example, given
|
||||
* `object Demo { type M[x] = List[x] }`, the call `extractTC(Demo, "M")` will return a type representing
|
||||
* the type constructor `[x] List[x]`.
|
||||
* Returns a Type representing the type constructor tcp.<name>. For example, given `object Demo {
|
||||
* type M[x] = List[x] }`, the call `extractTC(Demo, "M")` will return a type representing the
|
||||
* type constructor `[x] List[x]`.
|
||||
*/
|
||||
def extractTC(tcp: AnyRef with Singleton, name: String)(
|
||||
implicit it: ctx.TypeTag[tcp.type]
|
||||
def extractTC(tcp: AnyRef with Singleton, name: String)(implicit
|
||||
it: ctx.TypeTag[tcp.type]
|
||||
): ctx.Type = {
|
||||
val itTpe = it.tpe.asInstanceOf[global.Type]
|
||||
val m = itTpe.nonPrivateMember(global.newTypeName(name))
|
||||
|
|
@ -290,10 +302,10 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Substitutes wrappers in tree `t` with the result of `subWrapper`.
|
||||
* A wrapper is a Tree of the form `f[T](v)` for which isWrapper(<Tree of f>, <Underlying Type>, <qual>.target) returns true.
|
||||
* Typically, `f` is a `Select` or `Ident`.
|
||||
* The wrapper is replaced with the result of `subWrapper(<Type of T>, <Tree of v>, <wrapper Tree>)`
|
||||
* Substitutes wrappers in tree `t` with the result of `subWrapper`. A wrapper is a Tree of the
|
||||
* form `f[T](v)` for which isWrapper(<Tree of f>, <Underlying Type>, <qual>.target) returns true.
|
||||
* Typically, `f` is a `Select` or `Ident`. The wrapper is replaced with the result of
|
||||
* `subWrapper(<Type of T>, <Tree of v>, <wrapper Tree>)`
|
||||
*/
|
||||
def transformWrappers(
|
||||
t: Tree,
|
||||
|
|
@ -307,7 +319,11 @@ final class ContextUtil[C <: blackbox.Context](val ctx: C) {
|
|||
case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) =>
|
||||
subWrapper(nme.decodedName.toString, targ.tpe, qual, tree) match {
|
||||
case Converted.Success(t, finalTx) =>
|
||||
changeOwner(qual, currentOwner, initialOwner) // Fixes https://github.com/sbt/sbt/issues/1150
|
||||
changeOwner(
|
||||
qual,
|
||||
currentOwner,
|
||||
initialOwner
|
||||
) // Fixes https://github.com/sbt/sbt/issues/1150
|
||||
finalTx(t)
|
||||
case Converted.Failure(p, m) => ctx.abort(p, m)
|
||||
case _: Converted.NotApplicable[_] => super.transform(tree)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import sbt.internal.util.Types.Id
|
|||
/**
|
||||
* The separate hierarchy from Applicative/Monad is for two reasons.
|
||||
*
|
||||
* 1. The type constructor is represented as an abstract type because a TypeTag cannot represent a type constructor directly.
|
||||
* 2. The applicative interface is uncurried.
|
||||
* 1. The type constructor is represented as an abstract type because a TypeTag cannot represent a
|
||||
* type constructor directly. 2. The applicative interface is uncurried.
|
||||
*/
|
||||
trait Instance {
|
||||
type M[x]
|
||||
|
|
@ -51,40 +51,41 @@ object Instance {
|
|||
def idTransform[C <: blackbox.Context with Singleton]: Transform[C, Id] = in => in
|
||||
|
||||
/**
|
||||
* Implementation of a macro that provides a direct syntax for applicative functors and monads.
|
||||
* It is intended to be used in conjunction with another macro that conditions the inputs.
|
||||
* Implementation of a macro that provides a direct syntax for applicative functors and monads. It
|
||||
* is intended to be used in conjunction with another macro that conditions the inputs.
|
||||
*
|
||||
* This method processes the Tree `t` to find inputs of the form `wrap[T]( input )`
|
||||
* This form is typically constructed by another macro that pretends to be able to get a value of type `T`
|
||||
* from a value convertible to `M[T]`. This `wrap(input)` form has two main purposes.
|
||||
* First, it identifies the inputs that should be transformed.
|
||||
* Second, it allows the input trees to be wrapped for later conversion into the appropriate `M[T]` type by `convert`.
|
||||
* This wrapping is necessary because applying the first macro must preserve the original type,
|
||||
* but it is useful to delay conversion until the outer, second macro is called. The `wrap` method accomplishes this by
|
||||
* allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method will remove the call to `wrap`
|
||||
* so that it is not actually called at runtime.
|
||||
* This method processes the Tree `t` to find inputs of the form `wrap[T]( input )` This form is
|
||||
* typically constructed by another macro that pretends to be able to get a value of type `T` from
|
||||
* a value convertible to `M[T]`. This `wrap(input)` form has two main purposes. First, it
|
||||
* identifies the inputs that should be transformed. Second, it allows the input trees to be
|
||||
* wrapped for later conversion into the appropriate `M[T]` type by `convert`. This wrapping is
|
||||
* necessary because applying the first macro must preserve the original type, but it is useful to
|
||||
* delay conversion until the outer, second macro is called. The `wrap` method accomplishes this
|
||||
* by allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method
|
||||
* will remove the call to `wrap` so that it is not actually called at runtime.
|
||||
*
|
||||
* Each `input` in each expression of the form `wrap[T]( input )` is transformed by `convert`.
|
||||
* This transformation converts the input Tree to a Tree of type `M[T]`.
|
||||
* The original wrapped expression `wrap(input)` is replaced by a reference to a new local `val x: T`, where `x` is a fresh name.
|
||||
* These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s.
|
||||
* The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and defines the right hand side of the vals
|
||||
* that unpacks the tuple containing the results of the inputs.
|
||||
* This transformation converts the input Tree to a Tree of type `M[T]`. The original wrapped
|
||||
* expression `wrap(input)` is replaced by a reference to a new local `val x: T`, where `x` is a
|
||||
* fresh name. These converted inputs are passed to `builder` as well as the list of these
|
||||
* synthetic `ValDef`s. The `TupleBuilder` instance constructs a tuple (Tree) from the inputs and
|
||||
* defines the right hand side of the vals that unpacks the tuple containing the results of the
|
||||
* inputs.
|
||||
*
|
||||
* The constructed tuple of inputs and the code that unpacks the results of the inputs are then passed to the `i`,
|
||||
* which is an implementation of `Instance` that is statically accessible.
|
||||
* An Instance defines a applicative functor associated with a specific type constructor and, if it implements MonadInstance as well, a monad.
|
||||
* Typically, it will be either a top-level module or a stable member of a top-level module (such as a val or a nested module).
|
||||
* The `with Singleton` part of the type verifies some cases at macro compilation time,
|
||||
* while the full check for static accessibility is done at macro expansion time.
|
||||
* Note: Ideally, the types would verify that `i: MonadInstance` when `t.isRight`.
|
||||
* With the various dependent types involved, this is not worth it.
|
||||
* The constructed tuple of inputs and the code that unpacks the results of the inputs are then
|
||||
* passed to the `i`, which is an implementation of `Instance` that is statically accessible. An
|
||||
* Instance defines a applicative functor associated with a specific type constructor and, if it
|
||||
* implements MonadInstance as well, a monad. Typically, it will be either a top-level module or a
|
||||
* stable member of a top-level module (such as a val or a nested module). The `with Singleton`
|
||||
* part of the type verifies some cases at macro compilation time, while the full check for static
|
||||
* accessibility is done at macro expansion time. Note: Ideally, the types would verify that `i:
|
||||
* MonadInstance` when `t.isRight`. With the various dependent types involved, this is not worth
|
||||
* it.
|
||||
*
|
||||
* The `t` argument is the argument of the macro that will be transformed as described above.
|
||||
* If the macro that calls this method is for a multi-input map (app followed by map),
|
||||
* `t` should be the argument wrapped in Left.
|
||||
* If this is for multi-input flatMap (app followed by flatMap),
|
||||
* this should be the argument wrapped in Right.
|
||||
* The `t` argument is the argument of the macro that will be transformed as described above. If
|
||||
* the macro that calls this method is for a multi-input map (app followed by map), `t` should be
|
||||
* the argument wrapped in Left. If this is for multi-input flatMap (app followed by flatMap),
|
||||
* this should be the argument wrapped in Right.
|
||||
*/
|
||||
def contImpl[T, N[_]](
|
||||
c: blackbox.Context,
|
||||
|
|
@ -95,8 +96,8 @@ object Instance {
|
|||
)(
|
||||
t: Either[c.Expr[T], c.Expr[i.M[T]]],
|
||||
inner: Transform[c.type, N]
|
||||
)(
|
||||
implicit tt: c.WeakTypeTag[T],
|
||||
)(implicit
|
||||
tt: c.WeakTypeTag[T],
|
||||
nt: c.WeakTypeTag[N[T]],
|
||||
it: c.TypeTag[i.type]
|
||||
): c.Expr[i.M[N[T]]] = {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ package appmacro
|
|||
import scala.reflect._
|
||||
import macros._
|
||||
|
||||
/** A `TupleBuilder` that uses a KList as the tuple representation.*/
|
||||
/** A `TupleBuilder` that uses a KList as the tuple representation. */
|
||||
object KListBuilder extends TupleBuilder {
|
||||
def make(
|
||||
c: blackbox.Context
|
||||
|
|
@ -29,7 +29,7 @@ object KListBuilder extends TupleBuilder {
|
|||
val mTC: Type = mt.asInstanceOf[c.universe.Type]
|
||||
val kconsTC: Type = kconsTpe.typeConstructor
|
||||
|
||||
/** This is the L in the type function [L[x]] ... */
|
||||
/** This is the L in the type function [L[x]] ... */
|
||||
val tcVariable: TypeSymbol = newTCVariable(util.initialOwner)
|
||||
|
||||
/** Instantiates KCons[h, t <: KList[L], L], where L is the type constructor variable */
|
||||
|
|
@ -70,8 +70,8 @@ object KListBuilder extends TupleBuilder {
|
|||
val klist = makeKList(inputs.reverse, knil, knilType)
|
||||
|
||||
/**
|
||||
* The input types combined in a KList type. The main concern is tracking the heterogeneous types.
|
||||
* The type constructor is tcVariable, so that it can be applied to [X] X or M later.
|
||||
* The input types combined in a KList type. The main concern is tracking the heterogeneous
|
||||
* types. The type constructor is tcVariable, so that it can be applied to [X] X or M later.
|
||||
* When applied to `M`, this type gives the type of the `input` KList.
|
||||
*/
|
||||
val klistType: Type = inputs.foldRight(knilType)((in, klist) => kconsType(in.tpe, klist))
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ import scala.reflect._
|
|||
import macros._
|
||||
|
||||
/**
|
||||
* A builder that uses `TupleN` as the representation for small numbers of inputs (up to `TupleNBuilder.MaxInputs`)
|
||||
* and `KList` for larger numbers of inputs. This builder cannot handle fewer than 2 inputs.
|
||||
* A builder that uses `TupleN` as the representation for small numbers of inputs (up to
|
||||
* `TupleNBuilder.MaxInputs`) and `KList` for larger numbers of inputs. This builder cannot handle
|
||||
* fewer than 2 inputs.
|
||||
*/
|
||||
object MixedBuilder extends TupleBuilder {
|
||||
def make(
|
||||
|
|
|
|||
|
|
@ -12,22 +12,25 @@ import scala.reflect._
|
|||
import macros._
|
||||
|
||||
/**
|
||||
* A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or `KList`
|
||||
* and extracting values from it. The `Instance` macro implementation will (roughly) traverse the tree of its argument
|
||||
* and ultimately obtain a list of expressions with type `M[T]` for different types `T`.
|
||||
* The macro constructs an `Input` value for each of these expressions that contains the `Type` for `T`,
|
||||
* the `Tree` for the expression, and a `ValDef` that will hold the value for the input.
|
||||
* A `TupleBuilder` abstracts the work of constructing a tuple data structure such as a `TupleN` or
|
||||
* `KList` and extracting values from it. The `Instance` macro implementation will (roughly)
|
||||
* traverse the tree of its argument and ultimately obtain a list of expressions with type `M[T]`
|
||||
* for different types `T`. The macro constructs an `Input` value for each of these expressions that
|
||||
* contains the `Type` for `T`, the `Tree` for the expression, and a `ValDef` that will hold the
|
||||
* value for the input.
|
||||
*
|
||||
* `TupleBuilder.apply` is provided with the list of `Input`s and is expected to provide three values in the returned BuilderResult.
|
||||
* First, it returns the constructed tuple data structure Tree in `input`.
|
||||
* Next, it provides the type constructor `representationC` that, when applied to M, gives the type of tuple data structure.
|
||||
* For example, a builder that constructs a `Tuple3` for inputs `M[Int]`, `M[Boolean]`, and `M[String]`
|
||||
* would provide a Type representing `[L[x]] (L[Int], L[Boolean], L[String])`. The `input` method
|
||||
* would return a value whose type is that type constructor applied to M, or `(M[Int], M[Boolean], M[String])`.
|
||||
* `TupleBuilder.apply` is provided with the list of `Input`s and is expected to provide three
|
||||
* values in the returned BuilderResult. First, it returns the constructed tuple data structure Tree
|
||||
* in `input`. Next, it provides the type constructor `representationC` that, when applied to M,
|
||||
* gives the type of tuple data structure. For example, a builder that constructs a `Tuple3` for
|
||||
* inputs `M[Int]`, `M[Boolean]`, and `M[String]` would provide a Type representing `[L[x]] (L[Int],
|
||||
* L[Boolean], L[String])`. The `input` method would return a value whose type is that type
|
||||
* constructor applied to M, or `(M[Int], M[Boolean], M[String])`.
|
||||
*
|
||||
* Finally, the `extract` method provides a list of vals that extract information from the applied input.
|
||||
* The type of the applied input is the type constructor applied to `Id` (`[X] X`).
|
||||
* The returned list of ValDefs should be the ValDefs from `inputs`, but with non-empty right-hand sides.
|
||||
* Finally, the `extract` method provides a list of vals that extract information from the applied
|
||||
* input. The type of the applied input is the type constructor applied to `Id` (`[X] X`). The
|
||||
* returned list of ValDefs should be the ValDefs from `inputs`, but with non-empty right-hand
|
||||
* sides.
|
||||
*/
|
||||
trait TupleBuilder {
|
||||
|
||||
|
|
@ -45,20 +48,23 @@ trait BuilderResult[C <: blackbox.Context with Singleton] {
|
|||
import ctx.universe._
|
||||
|
||||
/**
|
||||
* Represents the higher-order type constructor `[L[x]] ...` where `...` is the
|
||||
* type of the data structure containing the added expressions,
|
||||
* except that it is abstracted over the type constructor applied to each heterogeneous part of the type .
|
||||
* Represents the higher-order type constructor `[L[x]] ...` where `...` is the type of the data
|
||||
* structure containing the added expressions, except that it is abstracted over the type
|
||||
* constructor applied to each heterogeneous part of the type .
|
||||
*/
|
||||
def representationC: PolyType
|
||||
|
||||
/** The instance of AList for the input. For a `representationC` of `[L[x]]`, this `Tree` should have a `Type` of `AList[L]`*/
|
||||
/**
|
||||
* The instance of AList for the input. For a `representationC` of `[L[x]]`, this `Tree` should
|
||||
* have a `Type` of `AList[L]`
|
||||
*/
|
||||
def alistInstance: Tree
|
||||
|
||||
/** Returns the completed value containing all expressions added to the builder. */
|
||||
def input: Tree
|
||||
|
||||
/* The list of definitions that extract values from a value of type `$representationC[Id]`.
|
||||
* The returned value should be identical to the `ValDef`s provided to the `TupleBuilder.make` method but with
|
||||
* non-empty right hand sides. Each `ValDef` may refer to `param` and previous `ValDef`s in the list.*/
|
||||
* The returned value should be identical to the `ValDef`s provided to the `TupleBuilder.make` method but with
|
||||
* non-empty right hand sides. Each `ValDef` may refer to `param` and previous `ValDef`s in the list.*/
|
||||
def extract(param: ValDef): List[ValDef]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import scala.reflect._
|
|||
import macros._
|
||||
|
||||
/**
|
||||
* A builder that uses a TupleN as the tuple representation.
|
||||
* It is limited to tuples of size 2 to `MaxInputs`.
|
||||
* A builder that uses a TupleN as the tuple representation. It is limited to tuples of size 2 to
|
||||
* `MaxInputs`.
|
||||
*/
|
||||
object TupleNBuilder extends TupleBuilder {
|
||||
|
||||
|
|
@ -35,8 +35,8 @@ object TupleNBuilder extends TupleBuilder {
|
|||
val ctx: c.type = c
|
||||
val representationC: PolyType = {
|
||||
val tcVariable: Symbol = newTCVariable(util.initialOwner)
|
||||
val tupleTypeArgs = inputs.map(
|
||||
in => internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]
|
||||
val tupleTypeArgs = inputs.map(in =>
|
||||
internal.typeRef(NoPrefix, tcVariable, in.tpe :: Nil).asInstanceOf[global.Type]
|
||||
)
|
||||
val tuple = global.definitions.tupleType(tupleTypeArgs)
|
||||
internal.polyType(tcVariable :: Nil, tuple.asInstanceOf[Type])
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.internal.util
|
||||
|
||||
object Classes {
|
||||
trait Applicative[M[_]] {
|
||||
def apply[S, T](f: M[S => T], v: M[S]): M[T]
|
||||
def pure[S](s: => S): M[S]
|
||||
def map[S, T](f: S => T, v: M[S]): M[T]
|
||||
}
|
||||
|
||||
trait Selective[M[_]] extends Applicative[M] {
|
||||
def select[A, B](fab: M[Either[A, B]])(fn: M[A => B]): M[B]
|
||||
}
|
||||
|
||||
trait Monad[M[_]] extends Applicative[M] {
|
||||
def flatten[T](m: M[M[T]]): M[T]
|
||||
}
|
||||
|
||||
implicit val optionMonad: Monad[Option] = new Monad[Option] {
|
||||
def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match {
|
||||
case (Some(fv), Some(vv)) => Some(fv(vv))
|
||||
case _ => None
|
||||
}
|
||||
|
||||
def pure[S](s: => S) = Some(s)
|
||||
def map[S, T](f: S => T, v: Option[S]) = v map f
|
||||
def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten
|
||||
}
|
||||
|
||||
implicit val listMonad: Monad[List] = new Monad[List] {
|
||||
def apply[S, T](f: List[S => T], v: List[S]) = for (fv <- f; vv <- v) yield fv(vv)
|
||||
def pure[S](s: => S) = s :: Nil
|
||||
def map[S, T](f: S => T, v: List[S]) = v map f
|
||||
def flatten[T](m: List[List[T]]): List[T] = m.flatten
|
||||
}
|
||||
}
|
||||
|
|
@ -36,8 +36,7 @@ object ErrorHandling {
|
|||
if (e.getClass == classOf[RuntimeException]) {
|
||||
val msg = e.getMessage
|
||||
if (msg == null || msg.isEmpty) e.toString else msg
|
||||
} else
|
||||
e.toString
|
||||
} else e.toString
|
||||
}
|
||||
|
||||
sealed class TranslatedException private[sbt] (msg: String, cause: Throwable)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
package sbt.internal.util
|
||||
|
||||
/** Defines a function to call as sbt exits.*/
|
||||
/** Defines a function to call as sbt exits. */
|
||||
trait ExitHook {
|
||||
|
||||
/** Subclasses should implement this method, which is called when this hook is executed. */
|
||||
|
|
@ -21,7 +21,10 @@ object ExitHook {
|
|||
|
||||
object ExitHooks {
|
||||
|
||||
/** Calls each registered exit hook, trapping any exceptions so that each hook is given a chance to run. */
|
||||
/**
|
||||
* Calls each registered exit hook, trapping any exceptions so that each hook is given a chance to
|
||||
* run.
|
||||
*/
|
||||
def runExitHooks(exitHooks: Seq[ExitHook]): Seq[Throwable] =
|
||||
exitHooks.flatMap(hook => ErrorHandling.wideConvert(hook.runBeforeExiting()).left.toOption)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,19 +10,20 @@ package sbt.internal.util
|
|||
final class MessageOnlyException(override val toString: String) extends RuntimeException(toString)
|
||||
|
||||
/**
|
||||
* A dummy exception for the top-level exception handler to know that an exception
|
||||
* has been handled, but is being passed further up to indicate general failure.
|
||||
* A dummy exception for the top-level exception handler to know that an exception has been handled,
|
||||
* but is being passed further up to indicate general failure.
|
||||
*/
|
||||
final class AlreadyHandledException(val underlying: Throwable) extends RuntimeException
|
||||
|
||||
/**
|
||||
* A marker trait for a top-level exception handler to know that this exception
|
||||
* doesn't make sense to display.
|
||||
* A marker trait for a top-level exception handler to know that this exception doesn't make sense
|
||||
* to display.
|
||||
*/
|
||||
trait UnprintableException extends Throwable
|
||||
|
||||
/**
|
||||
* A marker trait that refines UnprintableException to indicate to a top-level exception handler
|
||||
* that the code throwing this exception has already provided feedback to the user about the error condition.
|
||||
* that the code throwing this exception has already provided feedback to the user about the error
|
||||
* condition.
|
||||
*/
|
||||
trait FeedbackProvidedException extends UnprintableException
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ import java.util.concurrent.ConcurrentHashMap
|
|||
import scala.sys.process.Process
|
||||
|
||||
/**
|
||||
* Manages forked processes created by sbt. Any process registered
|
||||
* with RunningProcesses can be killed with the killAll method. In
|
||||
* particular, this can be used in a signal handler to kill these
|
||||
* Manages forked processes created by sbt. Any process registered with RunningProcesses can be
|
||||
* killed with the killAll method. In particular, this can be used in a signal handler to kill these
|
||||
* processes when the user inputs ctrl+c.
|
||||
*/
|
||||
private[sbt] object RunningProcesses {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ sealed abstract class Literal extends Formula {
|
|||
/** The underlying (positive) atom. */
|
||||
def atom: Atom
|
||||
|
||||
/** Negates this literal.*/
|
||||
/** Negates this literal. */
|
||||
def unary_! : Literal
|
||||
|
||||
}
|
||||
|
|
@ -62,24 +62,24 @@ final case class Atom(label: String) extends Literal {
|
|||
}
|
||||
|
||||
/**
|
||||
* A negated atom, in the sense of negation as failure, not logical negation.
|
||||
* That is, it is true if `atom` is not known/defined.
|
||||
* A negated atom, in the sense of negation as failure, not logical negation. That is, it is true if
|
||||
* `atom` is not known/defined.
|
||||
*/
|
||||
final case class Negated(atom: Atom) extends Literal {
|
||||
def unary_! : Atom = atom
|
||||
}
|
||||
|
||||
/**
|
||||
* A formula consists of variables, negation, and conjunction (and).
|
||||
* (Disjunction is not currently included- it is modeled at the level of a sequence of clauses.
|
||||
* This is less convenient when defining clauses, but is not less powerful.)
|
||||
* A formula consists of variables, negation, and conjunction (and). (Disjunction is not currently
|
||||
* included- it is modeled at the level of a sequence of clauses. This is less convenient when
|
||||
* defining clauses, but is not less powerful.)
|
||||
*/
|
||||
sealed abstract class Formula {
|
||||
|
||||
/** Constructs a clause that proves `atoms` when this formula is true. */
|
||||
def proves(atom: Atom, atoms: Atom*): Clause = Clause(this, (atom +: atoms).toSet)
|
||||
|
||||
/** Constructs a formula that is true iff this formula and `f` are both true.*/
|
||||
/** Constructs a formula that is true iff this formula and `f` are both true. */
|
||||
def &&(f: Formula): Formula = (this, f) match {
|
||||
case (True, x) => x
|
||||
case (x, True) => x
|
||||
|
|
@ -111,9 +111,9 @@ object Logic {
|
|||
|
||||
/**
|
||||
* Computes the variables in the unique stable model for the program represented by `clauses` and
|
||||
* `initialFacts`. `clause` may not have any negative feedback (that is, negation is acyclic)
|
||||
* and `initialFacts` cannot be in the head of any clauses in `clause`.
|
||||
* These restrictions ensure that the logic program has a unique minimal model.
|
||||
* `initialFacts`. `clause` may not have any negative feedback (that is, negation is acyclic) and
|
||||
* `initialFacts` cannot be in the head of any clauses in `clause`. These restrictions ensure that
|
||||
* the logic program has a unique minimal model.
|
||||
*/
|
||||
def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = {
|
||||
val (posSeq, negSeq) = separate(initialFacts.toSeq)
|
||||
|
|
@ -130,10 +130,9 @@ object Logic {
|
|||
}
|
||||
|
||||
/**
|
||||
* Verifies `initialFacts` are not in the head of any `clauses`.
|
||||
* This avoids the situation where an atom is proved but no clauses prove it.
|
||||
* This isn't necessarily a problem, but the main sbt use cases expects
|
||||
* a proven atom to have at least one clause satisfied.
|
||||
* Verifies `initialFacts` are not in the head of any `clauses`. This avoids the situation where
|
||||
* an atom is proved but no clauses prove it. This isn't necessarily a problem, but the main sbt
|
||||
* use cases expects a proven atom to have at least one clause satisfied.
|
||||
*/
|
||||
private[this] def checkOverlap(
|
||||
clauses: Clauses,
|
||||
|
|
@ -179,12 +178,11 @@ object Logic {
|
|||
}
|
||||
|
||||
private[this] def dependencyMap(clauses: Clauses): Map[Atom, Set[Literal]] =
|
||||
clauses.clauses.foldLeft(Map.empty[Atom, Set[Literal]]) {
|
||||
case (m, Clause(formula, heads)) =>
|
||||
val deps = literals(formula)
|
||||
heads.foldLeft(m) { (n, head) =>
|
||||
n.updated(head, n.getOrElse(head, Set.empty) ++ deps)
|
||||
}
|
||||
clauses.clauses.foldLeft(Map.empty[Atom, Set[Literal]]) { case (m, Clause(formula, heads)) =>
|
||||
val deps = literals(formula)
|
||||
heads.foldLeft(m) { (n, head) =>
|
||||
n.updated(head, n.getOrElse(head, Set.empty) ++ deps)
|
||||
}
|
||||
}
|
||||
|
||||
sealed abstract class LogicException(override val toString: String)
|
||||
|
|
@ -229,8 +227,8 @@ object Logic {
|
|||
}
|
||||
|
||||
/**
|
||||
* Finds clauses that have no body and thus prove their head.
|
||||
* Returns `(<proven atoms>, <remaining unproven clauses>)`.
|
||||
* Finds clauses that have no body and thus prove their head. Returns `(<proven atoms>, <remaining
|
||||
* unproven clauses>)`.
|
||||
*/
|
||||
private[this] def findProven(c: Clauses): (Set[Atom], List[Clause]) = {
|
||||
val (proven, unproven) = c.clauses.partition(_.body == True)
|
||||
|
|
@ -253,8 +251,7 @@ object Logic {
|
|||
val processedFacts = state add keepPositive(factsToProcess)
|
||||
val newlyProven = proven -- processedFacts.provenSet
|
||||
val newState = processedFacts add newlyProven
|
||||
if (unprovenClauses.isEmpty)
|
||||
newState // no remaining clauses, done.
|
||||
if (unprovenClauses.isEmpty) newState // no remaining clauses, done.
|
||||
else {
|
||||
val unproven = Clauses(unprovenClauses)
|
||||
val nextFacts: Set[Literal] =
|
||||
|
|
@ -265,8 +262,8 @@ object Logic {
|
|||
}
|
||||
|
||||
/**
|
||||
* Finds negated atoms under the negation as failure rule and returns them.
|
||||
* This should be called only after there are no more known atoms to be substituted.
|
||||
* Finds negated atoms under the negation as failure rule and returns them. This should be called
|
||||
* only after there are no more known atoms to be substituted.
|
||||
*/
|
||||
private[this] def inferFailure(clauses: Clauses): Set[Literal] = {
|
||||
/* At this point, there is at least one clause and one of the following is the case as the
|
||||
|
|
@ -281,8 +278,7 @@ object Logic {
|
|||
*/
|
||||
val allAtoms = atoms(clauses)
|
||||
val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse)
|
||||
if (newFacts.nonEmpty)
|
||||
newFacts
|
||||
if (newFacts.nonEmpty) newFacts
|
||||
else {
|
||||
val possiblyTrue = hasNegatedDependency(clauses.clauses, Relation.empty, Relation.empty)
|
||||
val newlyFalse: Set[Literal] = negated(allAtoms.inHead -- possiblyTrue)
|
||||
|
|
@ -296,10 +292,9 @@ object Logic {
|
|||
private[this] def negated(atoms: Set[Atom]): Set[Literal] = atoms.map(a => (Negated(a): Literal))
|
||||
|
||||
/**
|
||||
* Computes the set of atoms in `clauses` that directly or transitively take a negated atom as input.
|
||||
* For example, for the following clauses, this method would return `List(a, d)` :
|
||||
* a :- b, not c
|
||||
* d :- a
|
||||
* Computes the set of atoms in `clauses` that directly or transitively take a negated atom as
|
||||
* input. For example, for the following clauses, this method would return `List(a, d)` : a :- b,
|
||||
* not c d :- a
|
||||
*/
|
||||
@tailrec
|
||||
def hasNegatedDependency(
|
||||
|
|
@ -315,9 +310,8 @@ object Logic {
|
|||
case Clause(formula, head) +: tail =>
|
||||
// collect direct positive and negative literals and track them in separate graphs
|
||||
val (pos, neg) = directDeps(formula)
|
||||
val (newPos, newNeg) = head.foldLeft((posDeps, negDeps)) {
|
||||
case ((pdeps, ndeps), d) =>
|
||||
(pdeps.+(d, pos), ndeps.+(d, neg))
|
||||
val (newPos, newNeg) = head.foldLeft((posDeps, negDeps)) { case ((pdeps, ndeps), d) =>
|
||||
(pdeps.+(d, pos), ndeps.+(d, neg))
|
||||
}
|
||||
hasNegatedDependency(tail, newPos, newNeg)
|
||||
}
|
||||
|
|
@ -346,7 +340,9 @@ object Logic {
|
|||
case True => Set()
|
||||
}
|
||||
|
||||
/** Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses. */
|
||||
/**
|
||||
* Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses.
|
||||
*/
|
||||
final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]) {
|
||||
|
||||
/** Concatenates this with `as`. */
|
||||
|
|
@ -359,16 +355,16 @@ object Logic {
|
|||
|
||||
/**
|
||||
* Applies known facts to `clause`s, deriving a new, possibly empty list of clauses.
|
||||
* 1. If a fact is in the body of a clause, the derived clause has that fact removed from the body.
|
||||
* 2. If the negation of a fact is in a body of a clause, that clause fails and is removed.
|
||||
* 3. If a fact or its negation is in the head of a clause, the derived clause has that fact (or its negation) removed from the head.
|
||||
* 4. If a head is empty, the clause proves nothing and is removed.
|
||||
* 1. If a fact is in the body of a clause, the derived clause has that fact removed from the
|
||||
* body. 2. If the negation of a fact is in a body of a clause, that clause fails and is
|
||||
* removed. 3. If a fact or its negation is in the head of a clause, the derived clause has
|
||||
* that fact (or its negation) removed from the head. 4. If a head is empty, the clause
|
||||
* proves nothing and is removed.
|
||||
*
|
||||
* NOTE: empty bodies do not cause a clause to succeed yet.
|
||||
* All known facts must be applied before this can be done in order to avoid inconsistencies.
|
||||
* Precondition: no contradictions in `facts`
|
||||
* Postcondition: no atom in `facts` is present in the result
|
||||
* Postcondition: No clauses have an empty head
|
||||
* NOTE: empty bodies do not cause a clause to succeed yet. All known facts must be applied before
|
||||
* this can be done in order to avoid inconsistencies. Precondition: no contradictions in `facts`
|
||||
* Postcondition: no atom in `facts` is present in the result Postcondition: No clauses have an
|
||||
* empty head
|
||||
*/
|
||||
def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = {
|
||||
val newClauses =
|
||||
|
|
|
|||
|
|
@ -15,17 +15,21 @@ object Relation {
|
|||
def empty[A, B]: Relation[A, B] = make(Map.empty, Map.empty)
|
||||
|
||||
/**
|
||||
* Constructs a [[Relation]] from underlying `forward` and `reverse` representations, without checking that they are consistent.
|
||||
* This is a low-level constructor and the alternatives [[empty]] and [[reconstruct]] should be preferred.
|
||||
* Constructs a [[Relation]] from underlying `forward` and `reverse` representations, without
|
||||
* checking that they are consistent. This is a low-level constructor and the alternatives
|
||||
* [[empty]] and [[reconstruct]] should be preferred.
|
||||
*/
|
||||
def make[A, B](forward: Map[A, Set[B]], reverse: Map[B, Set[A]]): Relation[A, B] =
|
||||
new MRelation(forward, reverse)
|
||||
|
||||
/** Constructs a relation such that for every entry `_1 -> _2s` in `forward` and every `_2` in `_2s`, `(_1, _2)` is in the relation. */
|
||||
/**
|
||||
* Constructs a relation such that for every entry `_1 -> _2s` in `forward` and every `_2` in
|
||||
* `_2s`, `(_1, _2)` is in the relation.
|
||||
*/
|
||||
def reconstruct[A, B](forward: Map[A, Set[B]]): Relation[A, B] = {
|
||||
val reversePairs = for ((a, bs) <- forward.view; b <- bs.view) yield (b, a)
|
||||
val reverse = reversePairs.foldLeft(Map.empty[B, Set[A]]) {
|
||||
case (m, (b, a)) => add(m, b, a :: Nil)
|
||||
val reverse = reversePairs.foldLeft(Map.empty[B, Set[A]]) { case (m, (b, a)) =>
|
||||
add(m, b, a :: Nil)
|
||||
}
|
||||
make(forward filter { case (a, bs) => bs.nonEmpty }, reverse)
|
||||
}
|
||||
|
|
@ -53,19 +57,17 @@ object Relation {
|
|||
|
||||
/** when both parameters taken by relation are the same type, switch calls a function on them. */
|
||||
private[sbt] def switch[X, Y](relation: Relation[X, X], f: X => Y): Relation[Y, Y] = {
|
||||
val forward = relation.forwardMap.map {
|
||||
case (first, second) =>
|
||||
f(first) -> second.map(f)
|
||||
val forward = relation.forwardMap.map { case (first, second) =>
|
||||
f(first) -> second.map(f)
|
||||
}
|
||||
val reverse = relation.reverseMap.map {
|
||||
case (first, second) =>
|
||||
f(first) -> second.map(f)
|
||||
val reverse = relation.reverseMap.map { case (first, second) =>
|
||||
f(first) -> second.map(f)
|
||||
}
|
||||
make(forward, reverse)
|
||||
}
|
||||
}
|
||||
|
||||
/** Binary relation between A and B. It is a set of pairs (_1, _2) for _1 in A, _2 in B. */
|
||||
/** Binary relation between A and B. It is a set of pairs (_1, _2) for _1 in A, _2 in B. */
|
||||
trait Relation[A, B] {
|
||||
|
||||
/** Returns the set of all `_2`s such that `(_1, _2)` is in this relation. */
|
||||
|
|
@ -113,37 +115,41 @@ trait Relation[A, B] {
|
|||
/** Returns the number of pairs in this relation */
|
||||
def size: Int
|
||||
|
||||
/** Returns true iff `(a,b)` is in this relation*/
|
||||
/** Returns true iff `(a,b)` is in this relation */
|
||||
def contains(a: A, b: B): Boolean
|
||||
|
||||
/** Returns a relation with only pairs `(a,b)` for which `f(a,b)` is true.*/
|
||||
/** Returns a relation with only pairs `(a,b)` for which `f(a,b)` is true. */
|
||||
def filter(f: (A, B) => Boolean): Relation[A, B]
|
||||
|
||||
/**
|
||||
* Returns a pair of relations: the first contains only pairs `(a,b)` for which `f(a,b)` is true and
|
||||
* the other only pairs `(a,b)` for which `f(a,b)` is false.
|
||||
* Returns a pair of relations: the first contains only pairs `(a,b)` for which `f(a,b)` is true
|
||||
* and the other only pairs `(a,b)` for which `f(a,b)` is false.
|
||||
*/
|
||||
def partition(f: (A, B) => Boolean): (Relation[A, B], Relation[A, B])
|
||||
|
||||
/** Partitions this relation into a map of relations according to some discriminator function. */
|
||||
def groupBy[K](discriminator: ((A, B)) => K): Map[K, Relation[A, B]]
|
||||
|
||||
/** Returns all pairs in this relation.*/
|
||||
/** Returns all pairs in this relation. */
|
||||
def all: Traversable[(A, B)]
|
||||
|
||||
/**
|
||||
* Represents this relation as a `Map` from a `_1` to the set of `_2`s such that `(_1, _2)` is in this relation.
|
||||
* Represents this relation as a `Map` from a `_1` to the set of `_2`s such that `(_1, _2)` is in
|
||||
* this relation.
|
||||
*
|
||||
* Specifically, there is one entry for each `_1` such that `(_1, _2)` is in this relation for some `_2`.
|
||||
* The value associated with a given `_1` is the set of all `_2`s such that `(_1, _2)` is in this relation.
|
||||
* Specifically, there is one entry for each `_1` such that `(_1, _2)` is in this relation for
|
||||
* some `_2`. The value associated with a given `_1` is the set of all `_2`s such that `(_1, _2)`
|
||||
* is in this relation.
|
||||
*/
|
||||
def forwardMap: Map[A, Set[B]]
|
||||
|
||||
/**
|
||||
* Represents this relation as a `Map` from a `_2` to the set of `_1`s such that `(_1, _2)` is in this relation.
|
||||
* Represents this relation as a `Map` from a `_2` to the set of `_1`s such that `(_1, _2)` is in
|
||||
* this relation.
|
||||
*
|
||||
* Specifically, there is one entry for each `_2` such that `(_1, _2)` is in this relation for some `_1`.
|
||||
* The value associated with a given `_2` is the set of all `_1`s such that `(_1, _2)` is in this relation.
|
||||
* Specifically, there is one entry for each `_2` such that `(_1, _2)` is in this relation for
|
||||
* some `_1`. The value associated with a given `_2` is the set of all `_1`s such that `(_1, _2)`
|
||||
* is in this relation.
|
||||
*/
|
||||
def reverseMap: Map[B, Set[A]]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,11 @@ object RelationTest extends Properties("Relation") {
|
|||
|
||||
r._1s == _1s && r.forwardMap.keySet == _1s &&
|
||||
r._2s == _2s && r.reverseMap.keySet == _2s &&
|
||||
pairs.forall {
|
||||
case (a, b) =>
|
||||
(r.forward(a) contains b) &&
|
||||
(r.reverse(b) contains a) &&
|
||||
(r.forwardMap(a) contains b) &&
|
||||
(r.reverseMap(b) contains a)
|
||||
pairs.forall { case (a, b) =>
|
||||
(r.forward(a) contains b) &&
|
||||
(r.reverse(b) contains a) &&
|
||||
(r.forwardMap(a) contains b) &&
|
||||
(r.reverseMap(b) contains a)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,12 +45,11 @@ object RelationTest extends Properties("Relation") {
|
|||
("Forward map does not contain removed" |: !r.forwardMap.contains(rem)) &&
|
||||
("Removed is not a value in reverse map" |: !r.reverseMap.values.toSet.contains(rem))
|
||||
} &&
|
||||
all(removeFine) {
|
||||
case (a, b) =>
|
||||
("Forward does not contain removed" |: (!r.forward(a).contains(b))) &&
|
||||
("Reverse does not contain removed" |: (!r.reverse(b).contains(a))) &&
|
||||
("Forward map does not contain removed" |: (notIn(r.forwardMap, a, b))) &&
|
||||
("Reverse map does not contain removed" |: (notIn(r.reverseMap, b, a)))
|
||||
all(removeFine) { case (a, b) =>
|
||||
("Forward does not contain removed" |: (!r.forward(a).contains(b))) &&
|
||||
("Reverse does not contain removed" |: (!r.reverse(b).contains(a))) &&
|
||||
("Forward map does not contain removed" |: (notIn(r.forwardMap, a, b))) &&
|
||||
("Reverse map does not contain removed" |: (notIn(r.reverseMap, b, a)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,8 +57,8 @@ object RelationTest extends Properties("Relation") {
|
|||
val splitInto = math.abs(randomInt) % 10 + 1 // Split into 1-10 groups.
|
||||
val rel = Relation.empty[Int, Double] ++ entries
|
||||
val grouped = rel groupBy (_._1 % splitInto)
|
||||
all(grouped.toSeq) {
|
||||
case (k, rel_k) => rel_k._1s forall { _ % splitInto == k }
|
||||
all(grouped.toSeq) { case (k, rel_k) =>
|
||||
rel_k._1s forall { _ % splitInto == k }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ object Dependencies {
|
|||
// WARNING: Please Scala update versions in PluginCross.scala too
|
||||
val scala212 = "2.12.17"
|
||||
val scala213 = "2.13.8"
|
||||
val scala3 = "3.1.0"
|
||||
val checkPluginCross = settingKey[Unit]("Make sure scalaVersion match up")
|
||||
val baseScalaVersion = scala212
|
||||
val baseScalaVersion = scala3
|
||||
def nightlyVersion: Option[String] =
|
||||
sys.env.get("BUILD_VERSION") orElse sys.props.get("sbt.build.version")
|
||||
|
||||
|
|
@ -79,8 +80,11 @@ object Dependencies {
|
|||
|
||||
val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.13"
|
||||
|
||||
def sjsonNew(n: String) =
|
||||
Def.setting("com.eed3si9n" %% n % "0.9.1") // contrabandSjsonNewVersion.value
|
||||
lazy val sjsonNewVersion = "0.9.1"
|
||||
def sjsonNew(n: String) = Def.setting(
|
||||
if (scalaBinaryVersion.value == "3") "com.eed3si9n" % (n + "_2.13") % sjsonNewVersion
|
||||
else "com.eed3si9n" %% n % "0.9.1"
|
||||
) // contrabandSjsonNewVersion.value
|
||||
val sjsonNewScalaJson = sjsonNew("sjson-new-scalajson")
|
||||
val sjsonNewMurmurhash = sjsonNew("sjson-new-murmurhash")
|
||||
|
||||
|
|
@ -121,7 +125,7 @@ object Dependencies {
|
|||
"org.scala-lang" % "scala-reflect" % scalaVersion.value
|
||||
}
|
||||
)
|
||||
val scalaPar = "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0"
|
||||
val scalaPar = "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.4"
|
||||
|
||||
// specify all of log4j modules to prevent misalignment
|
||||
def log4jModule = (n: String) => "org.apache.logging.log4j" % n % "2.17.1"
|
||||
|
|
|
|||
|
|
@ -24,14 +24,15 @@ object HouseRulesPlugin extends AutoPlugin {
|
|||
.value
|
||||
.toList,
|
||||
scalacOptions ++= "-Ykind-projector".ifScala3.value.toList,
|
||||
scalacOptions ++= "-Ysemanticdb".ifScala3.value.toList,
|
||||
scalacOptions ++= "-Yinline-warnings".ifScala211OrMinus.value.toList,
|
||||
scalacOptions ++= "-Yno-adapted-args".ifScala212OrMinus.value.toList,
|
||||
scalacOptions += "-Ywarn-dead-code",
|
||||
scalacOptions += "-Ywarn-numeric-widen",
|
||||
scalacOptions += "-Ywarn-value-discard",
|
||||
scalacOptions ++= "-Ywarn-unused-import".ifScala(v => 11 <= v && v <= 12).value.toList
|
||||
) ++ Seq(Compile, Test).flatMap(
|
||||
c => (c / console / scalacOptions) --= Seq("-Ywarn-unused-import", "-Xlint")
|
||||
) ++ Seq(Compile, Test).flatMap(c =>
|
||||
(c / console / scalacOptions) --= Seq("-Ywarn-unused-import", "-Xlint")
|
||||
)
|
||||
|
||||
private def scalaPartV = Def setting (CrossVersion partialVersion scalaVersion.value)
|
||||
|
|
|
|||
|
|
@ -10,14 +10,13 @@ package sbt
|
|||
trait CompletionService[A, R] {
|
||||
|
||||
/**
|
||||
* Submits a work node A with work that returns R.
|
||||
* In Execute this is used for tasks returning sbt.Completed.
|
||||
* Submits a work node A with work that returns R. In Execute this is used for tasks returning
|
||||
* sbt.Completed.
|
||||
*/
|
||||
def submit(node: A, work: () => R): Unit
|
||||
|
||||
/**
|
||||
* Retrieves and removes the result from the next completed task,
|
||||
* waiting if none are yet present.
|
||||
* Retrieves and removes the result from the next completed task, waiting if none are yet present.
|
||||
* In Execute this is used for tasks returning sbt.Completed.
|
||||
*/
|
||||
def take(): R
|
||||
|
|
@ -57,20 +56,21 @@ object CompletionService {
|
|||
() => future.get
|
||||
}
|
||||
private[sbt] def submitFuture[A](work: () => A, completion: JCompletionService[A]): JFuture[A] = {
|
||||
val future = try completion.submit {
|
||||
new Callable[A] {
|
||||
def call =
|
||||
try {
|
||||
work()
|
||||
} catch {
|
||||
case _: InterruptedException =>
|
||||
throw Incomplete(None, message = Some("cancelled"))
|
||||
}
|
||||
val future =
|
||||
try completion.submit {
|
||||
new Callable[A] {
|
||||
def call =
|
||||
try {
|
||||
work()
|
||||
} catch {
|
||||
case _: InterruptedException =>
|
||||
throw Incomplete(None, message = Some("cancelled"))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
case _: RejectedExecutionException =>
|
||||
throw Incomplete(None, message = Some("cancelled"))
|
||||
}
|
||||
} catch {
|
||||
case _: RejectedExecutionException =>
|
||||
throw Incomplete(None, message = Some("cancelled"))
|
||||
}
|
||||
future
|
||||
}
|
||||
def manage[A, T](
|
||||
|
|
|
|||
|
|
@ -17,31 +17,30 @@ import scala.collection.mutable
|
|||
/**
|
||||
* Describes restrictions on concurrent execution for a set of tasks.
|
||||
*
|
||||
* @tparam A the type of a task
|
||||
* @tparam A
|
||||
* the type of a task
|
||||
*/
|
||||
trait ConcurrentRestrictions[A] {
|
||||
|
||||
/** Internal state type used to describe a set of tasks. */
|
||||
type G
|
||||
|
||||
/** Representation of zero tasks.*/
|
||||
/** Representation of zero tasks. */
|
||||
def empty: G
|
||||
|
||||
/** Updates the description `g` to include a new task `a`.*/
|
||||
/** Updates the description `g` to include a new task `a`. */
|
||||
def add(g: G, a: A): G
|
||||
|
||||
/** Updates the description `g` to remove a previously added task `a`.*/
|
||||
/** Updates the description `g` to remove a previously added task `a`. */
|
||||
def remove(g: G, a: A): G
|
||||
|
||||
/**
|
||||
* Returns true if the tasks described by `g` are allowed to execute concurrently.
|
||||
* The methods in this class must obey the following laws:
|
||||
* Returns true if the tasks described by `g` are allowed to execute concurrently. The methods in
|
||||
* this class must obey the following laws:
|
||||
*
|
||||
* 1. forall g: G, a: A; valid(g) => valid(remove(g,a))
|
||||
* 2. forall a: A; valid(add(empty, a))
|
||||
* 3. forall g: G, a: A; valid(g) <=> valid(remove(add(g, a), a))
|
||||
* 4. (implied by 1,2,3) valid(empty)
|
||||
* 5. forall g: G, a: A, b: A; !valid(add(g,a)) => !valid(add(add(g,b), a))
|
||||
* 1. forall g: G, a: A; valid(g) => valid(remove(g,a)) 2. forall a: A; valid(add(empty, a)) 3.
|
||||
* forall g: G, a: A; valid(g) <=> valid(remove(add(g, a), a)) 4. (implied by 1,2,3)
|
||||
* valid(empty) 5. forall g: G, a: A, b: A; !valid(add(g,a)) => !valid(add(add(g,b), a))
|
||||
*/
|
||||
def valid(g: G): Boolean
|
||||
}
|
||||
|
|
@ -69,7 +68,8 @@ object ConcurrentRestrictions {
|
|||
|
||||
/**
|
||||
* A ConcurrentRestrictions instance that places no restrictions on concurrently executing tasks.
|
||||
* @param zero the constant placeholder used for t
|
||||
* @param zero
|
||||
* the constant placeholder used for t
|
||||
*/
|
||||
def unrestricted[A]: ConcurrentRestrictions[A] =
|
||||
new ConcurrentRestrictions[A] {
|
||||
|
|
@ -91,13 +91,13 @@ object ConcurrentRestrictions {
|
|||
}
|
||||
}
|
||||
|
||||
/** A key object used for associating information with a task.*/
|
||||
/** A key object used for associating information with a task. */
|
||||
final case class Tag(name: String)
|
||||
|
||||
val tagsKey =
|
||||
AttributeKey[TagMap]("tags", "Attributes restricting concurrent execution of tasks.")
|
||||
|
||||
/** A standard tag describing the number of tasks that do not otherwise have any tags.*/
|
||||
/** A standard tag describing the number of tasks that do not otherwise have any tags. */
|
||||
val Untagged = Tag("untagged")
|
||||
|
||||
/** A standard tag describing the total number of tasks. */
|
||||
|
|
@ -108,9 +108,12 @@ object ConcurrentRestrictions {
|
|||
|
||||
/**
|
||||
* Implements concurrency restrictions on tasks based on Tags.
|
||||
* @tparam A type of a task
|
||||
* @param get extracts tags from a task
|
||||
* @param validF defines whether a set of tasks are allowed to execute concurrently based on their merged tags
|
||||
* @tparam A
|
||||
* type of a task
|
||||
* @param get
|
||||
* extracts tags from a task
|
||||
* @param validF
|
||||
* defines whether a set of tasks are allowed to execute concurrently based on their merged tags
|
||||
*/
|
||||
def tagged[A](get: A => TagMap, validF: TagMap => Boolean): ConcurrentRestrictions[A] =
|
||||
new ConcurrentRestrictions[A] {
|
||||
|
|
@ -142,10 +145,14 @@ object ConcurrentRestrictions {
|
|||
private[this] val poolID = new AtomicInteger(1)
|
||||
|
||||
/**
|
||||
* Constructs a CompletionService suitable for backing task execution based on the provided restrictions on concurrent task execution.
|
||||
* @return a pair, with _1 being the CompletionService and _2 a function to shutdown the service.
|
||||
* @tparam A the task type
|
||||
* @tparam R the type of data that will be computed by the CompletionService.
|
||||
* Constructs a CompletionService suitable for backing task execution based on the provided
|
||||
* restrictions on concurrent task execution.
|
||||
* @return
|
||||
* a pair, with _1 being the CompletionService and _2 a function to shutdown the service.
|
||||
* @tparam A
|
||||
* the task type
|
||||
* @tparam R
|
||||
* the type of data that will be computed by the CompletionService.
|
||||
*/
|
||||
def completionService[A, R](
|
||||
tags: ConcurrentRestrictions[A],
|
||||
|
|
@ -167,10 +174,13 @@ object ConcurrentRestrictions {
|
|||
): (CompletionService[A, R], () => Unit) = {
|
||||
val pool = Executors.newCachedThreadPool()
|
||||
val service = completionService[A, R](pool, tags, warn, isSentinel)
|
||||
(service, () => {
|
||||
pool.shutdownNow()
|
||||
()
|
||||
})
|
||||
(
|
||||
service,
|
||||
() => {
|
||||
pool.shutdownNow()
|
||||
()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def cancellableCompletionService[A, R](
|
||||
|
|
@ -180,11 +190,14 @@ object ConcurrentRestrictions {
|
|||
): (CompletionService[A, R], Boolean => Unit) = {
|
||||
val pool = Executors.newCachedThreadPool()
|
||||
val service = completionService[A, R](pool, tags, warn, isSentinel)
|
||||
(service, force => {
|
||||
if (force) service.close()
|
||||
pool.shutdownNow()
|
||||
()
|
||||
})
|
||||
(
|
||||
service,
|
||||
force => {
|
||||
if (force) service.close()
|
||||
pool.shutdownNow()
|
||||
()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def completionService[A, R](
|
||||
|
|
@ -196,8 +209,9 @@ object ConcurrentRestrictions {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructs a CompletionService suitable for backing task execution based on the provided restrictions on concurrent task execution
|
||||
* and using the provided Executor to manage execution on threads.
|
||||
* Constructs a CompletionService suitable for backing task execution based on the provided
|
||||
* restrictions on concurrent task execution and using the provided Executor to manage execution
|
||||
* on threads.
|
||||
*/
|
||||
def completionService[A, R](
|
||||
backing: Executor,
|
||||
|
|
@ -220,13 +234,16 @@ object ConcurrentRestrictions {
|
|||
/** Backing service used to manage execution on threads once all constraints are satisfied. */
|
||||
private[this] val jservice = new ExecutorCompletionService[R](backing)
|
||||
|
||||
/** The description of the currently running tasks, used by `tags` to manage restrictions.*/
|
||||
/** The description of the currently running tasks, used by `tags` to manage restrictions. */
|
||||
private[this] var tagState = tags.empty
|
||||
|
||||
/** The number of running tasks. */
|
||||
private[this] var running = 0
|
||||
|
||||
/** Tasks that cannot be run yet because they cannot execute concurrently with the currently running tasks.*/
|
||||
/**
|
||||
* Tasks that cannot be run yet because they cannot execute concurrently with the currently
|
||||
* running tasks.
|
||||
*/
|
||||
private[this] val pending = new LinkedList[Enqueue]
|
||||
|
||||
private[this] val sentinels: mutable.ListBuffer[JFuture[_]] = mutable.ListBuffer.empty
|
||||
|
|
|
|||
|
|
@ -65,12 +65,11 @@ private[sbt] final class Execute[F[_] <: AnyRef](
|
|||
private[this] val viewCache = pMap[F, Node[F, *]]
|
||||
private[this] val results = pMap[F, Result]
|
||||
|
||||
private[this] val getResult: F ~> Result = λ[F ~> Result](
|
||||
a =>
|
||||
view.inline(a) match {
|
||||
case Some(v) => Value(v())
|
||||
case None => results(a)
|
||||
}
|
||||
private[this] val getResult: F ~> Result = λ[F ~> Result](a =>
|
||||
view.inline(a) match {
|
||||
case Some(v) => Value(v())
|
||||
case None => results(a)
|
||||
}
|
||||
)
|
||||
private[this] type State = State.Value
|
||||
private[this] object State extends Enumeration {
|
||||
|
|
@ -149,8 +148,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
|
|||
}
|
||||
|
||||
post {
|
||||
if (done(target))
|
||||
assert(done(node))
|
||||
if (done(target)) assert(done(node))
|
||||
else {
|
||||
assert(calling(node))
|
||||
assert(callers(target) contains node)
|
||||
|
|
@ -203,9 +201,9 @@ private[sbt] final class Execute[F[_] <: AnyRef](
|
|||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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[A](node: F[A])(implicit strategy: Strategy): Unit = {
|
||||
if (!added(node)) addNew(node)
|
||||
|
|
@ -214,9 +212,9 @@ private[sbt] final class Execute[F[_] <: AnyRef](
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 is scheduled to run.
|
||||
* The node's dependencies will be added (transitively) if they are not already registered.
|
||||
* 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 is scheduled to run. The node's dependencies will be
|
||||
* added (transitively) if they are not already registered.
|
||||
*/
|
||||
def addNew[A](node: F[A])(implicit strategy: Strategy): Unit = {
|
||||
pre { newPre(node) }
|
||||
|
|
@ -231,8 +229,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
|
|||
/* active is mutable, so take a snapshot */
|
||||
)
|
||||
|
||||
if (active.isEmpty)
|
||||
ready(node)
|
||||
if (active.isEmpty) ready(node)
|
||||
else {
|
||||
forward(node) = active
|
||||
for (a <- active) {
|
||||
|
|
@ -249,7 +246,10 @@ private[sbt] final class Execute[F[_] <: AnyRef](
|
|||
}
|
||||
}
|
||||
|
||||
/** Called when a pending 'node' becomes runnable. All of its dependencies must be done. This schedules the node's computation with 'strategy'.*/
|
||||
/**
|
||||
* Called when a pending 'node' becomes runnable. All of its dependencies must be done. This
|
||||
* schedules the node's computation with 'strategy'.
|
||||
*/
|
||||
def ready[A](node: F[A])(implicit strategy: Strategy): Unit = {
|
||||
pre {
|
||||
assert(pending(node))
|
||||
|
|
@ -283,8 +283,8 @@ private[sbt] final class Execute[F[_] <: AnyRef](
|
|||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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[A](node: F[A], f: => Either[F[A], A])(implicit strategy: Strategy): Completed = {
|
||||
progress.beforeWork(node)
|
||||
|
|
|
|||
|
|
@ -10,48 +10,46 @@ package sbt
|
|||
import sbt.internal.util.RMap
|
||||
|
||||
/**
|
||||
* Processes progress events during task execution.
|
||||
* All methods are called from the same thread except `started` and `finished`,
|
||||
* which is called from the executing task's thread.
|
||||
* All methods should return quickly to avoid task execution overhead.
|
||||
* Processes progress events during task execution. All methods are called from the same thread
|
||||
* except `started` and `finished`, which is called from the executing task's thread. All methods
|
||||
* should return quickly to avoid task execution overhead.
|
||||
*/
|
||||
trait ExecuteProgress[F[_]] {
|
||||
def initial(): Unit
|
||||
|
||||
/**
|
||||
* Notifies that a `task` has been registered in the system for execution.
|
||||
* The dependencies of `task` are `allDeps` and the subset of those dependencies that
|
||||
* have not completed are `pendingDeps`.
|
||||
* Notifies that a `task` has been registered in the system for execution. The dependencies of
|
||||
* `task` are `allDeps` and the subset of those dependencies that have not completed are
|
||||
* `pendingDeps`.
|
||||
*/
|
||||
def afterRegistered(task: F[_], allDeps: Iterable[F[_]], pendingDeps: Iterable[F[_]]): Unit
|
||||
|
||||
/**
|
||||
* Notifies that all of the dependencies of `task` have completed and `task` is therefore
|
||||
* ready to run. The task has not been scheduled on a thread yet.
|
||||
* Notifies that all of the dependencies of `task` have completed and `task` is therefore ready to
|
||||
* run. The task has not been scheduled on a thread yet.
|
||||
*/
|
||||
def afterReady(task: F[_]): Unit
|
||||
|
||||
/**
|
||||
* Notifies that the work for `task` is starting after this call returns.
|
||||
* This is called from the thread the task executes on, unlike most other methods in this callback.
|
||||
* It is called immediately before the task's work starts with minimal intervening executor overhead.
|
||||
* Notifies that the work for `task` is starting after this call returns. This is called from the
|
||||
* thread the task executes on, unlike most other methods in this callback. It is called
|
||||
* immediately before the task's work starts with minimal intervening executor overhead.
|
||||
*/
|
||||
def beforeWork(task: F[_]): Unit
|
||||
|
||||
/**
|
||||
* Notifies that the work for `task` work has finished. The task may have computed the next task to
|
||||
* run, in which case `result` contains that next task wrapped in Left. If the task produced a value
|
||||
* or terminated abnormally, `result` provides that outcome wrapped in Right. The ultimate result of
|
||||
* a task is provided to the `completed` method.
|
||||
* This is called from the thread the task executes on, unlike most other methods in this callback.
|
||||
* It is immediately called after the task's work is complete with minimal intervening executor overhead.
|
||||
* Notifies that the work for `task` work has finished. The task may have computed the next task
|
||||
* to run, in which case `result` contains that next task wrapped in Left. If the task produced a
|
||||
* value or terminated abnormally, `result` provides that outcome wrapped in Right. The ultimate
|
||||
* result of a task is provided to the `completed` method. This is called from the thread the task
|
||||
* executes on, unlike most other methods in this callback. It is immediately called after the
|
||||
* task's work is complete with minimal intervening executor overhead.
|
||||
*/
|
||||
def afterWork[A](task: F[A], result: Either[F[A], Result[A]]): Unit
|
||||
|
||||
/**
|
||||
* Notifies that `task` has completed.
|
||||
* The task's work is done with a final `result`.
|
||||
* Any tasks called by `task` have completed.
|
||||
* Notifies that `task` has completed. The task's work is done with a final `result`. Any tasks
|
||||
* called by `task` have completed.
|
||||
*/
|
||||
def afterCompleted[A](task: F[A], result: Result[A]): Unit
|
||||
|
||||
|
|
@ -62,7 +60,9 @@ trait ExecuteProgress[F[_]] {
|
|||
def stop(): Unit
|
||||
}
|
||||
|
||||
/** This module is experimental and subject to binary and source incompatible changes at any time. */
|
||||
/**
|
||||
* This module is experimental and subject to binary and source incompatible changes at any time.
|
||||
*/
|
||||
object ExecuteProgress {
|
||||
def empty[F[_]]: ExecuteProgress[F] = new ExecuteProgress[F] {
|
||||
override def initial(): Unit = ()
|
||||
|
|
|
|||
|
|
@ -15,11 +15,17 @@ import Incomplete.{ Error, Value => IValue }
|
|||
/**
|
||||
* Describes why a task did not complete.
|
||||
*
|
||||
* @param node the task that did not complete that is described by this Incomplete instance
|
||||
* @param tpe whether the task was incomplete because of an error or because it was skipped. Only Error is actually used and Skipped may be removed in the future.
|
||||
* @param message an optional error message describing this incompletion
|
||||
* @param causes a list of incompletions that prevented `node` from completing
|
||||
* @param directCause the exception that caused `node` to not complete
|
||||
* @param node
|
||||
* the task that did not complete that is described by this Incomplete instance
|
||||
* @param tpe
|
||||
* whether the task was incomplete because of an error or because it was skipped. Only Error is
|
||||
* actually used and Skipped may be removed in the future.
|
||||
* @param message
|
||||
* an optional error message describing this incompletion
|
||||
* @param causes
|
||||
* a list of incompletions that prevented `node` from completing
|
||||
* @param directCause
|
||||
* the exception that caused `node` to not complete
|
||||
*/
|
||||
final case class Incomplete(
|
||||
node: Option[AnyRef],
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ import sbt.internal.util.AList
|
|||
/**
|
||||
* Represents a task node in a format understood by the task evaluation engine Execute.
|
||||
*
|
||||
* @tparam A the task type constructor
|
||||
* @tparam T the type computed by this node
|
||||
* @tparam A
|
||||
* the task type constructor
|
||||
* @tparam T
|
||||
* the type computed by this node
|
||||
*/
|
||||
trait Node[A[_], T] {
|
||||
type K[L[x]]
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ import sbt.internal.util.~>
|
|||
|
||||
// used instead of Either[Incomplete, T] for type inference
|
||||
|
||||
/** Result of completely evaluating a task.*/
|
||||
/** Result of completely evaluating a task. */
|
||||
sealed trait Result[+T] {
|
||||
def toEither: Either[Incomplete, T]
|
||||
}
|
||||
|
||||
/** Indicates the task did not complete normally and so it does not have a value.*/
|
||||
/** Indicates the task did not complete normally and so it does not have a value. */
|
||||
final case class Inc(cause: Incomplete) extends Result[Nothing] {
|
||||
def toEither: Either[Incomplete, Nothing] = Left(cause)
|
||||
}
|
||||
|
||||
/** Indicates the task completed normally and produced the given `value`.*/
|
||||
/** Indicates the task completed normally and produced the given `value`. */
|
||||
final case class Value[+T](value: T) extends Result[T] {
|
||||
def toEither: Either[Incomplete, T] = Right(value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ object EvaluationState extends Enumeration {
|
|||
val New, Blocked, Ready, Calling, Evaluated = Value
|
||||
}
|
||||
|
||||
/*
|
||||
abstract class EvaluateSettings[ScopeType] {
|
||||
protected val init: Init[ScopeType]
|
||||
import init._
|
||||
|
|
@ -228,3 +229,4 @@ abstract class EvaluateSettings[ScopeType] {
|
|||
protected def evaluate0(): Unit = setValue(f(alist.transform(in, getValue)))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -16,8 +16,8 @@ import sbt.util.OptJsonWriter
|
|||
// a single AttributeKey instance cannot conform to AttributeKey[T] for different Ts
|
||||
|
||||
/**
|
||||
* A key in an [[AttributeMap]] that constrains its associated value to be of type `T`.
|
||||
* The key is uniquely defined by its `label` and type `T`, represented at runtime by `manifest`.
|
||||
* A key in an [[AttributeMap]] that constrains its associated value to be of type `T`. The key is
|
||||
* uniquely defined by its `label` and type `T`, represented at runtime by `manifest`.
|
||||
*/
|
||||
sealed trait AttributeKey[T] {
|
||||
|
||||
|
|
@ -32,18 +32,19 @@ sealed trait AttributeKey[T] {
|
|||
|
||||
/**
|
||||
* In environments that support delegation, looking up this key when it has no associated value
|
||||
* will delegate to the values associated with these keys.
|
||||
* The delegation proceeds in order the keys are returned here.
|
||||
* will delegate to the values associated with these keys. The delegation proceeds in order the
|
||||
* keys are returned here.
|
||||
*/
|
||||
def extend: Seq[AttributeKey[_]]
|
||||
|
||||
/**
|
||||
* Specifies whether this key is a local, anonymous key (`true`) or not (`false`).
|
||||
* This is typically only used for programmatic, intermediate keys that should not be referenced outside of a specific scope.
|
||||
* Specifies whether this key is a local, anonymous key (`true`) or not (`false`). This is
|
||||
* typically only used for programmatic, intermediate keys that should not be referenced outside
|
||||
* of a specific scope.
|
||||
*/
|
||||
def isLocal: Boolean
|
||||
|
||||
/** Identifies the relative importance of a key among other keys.*/
|
||||
/** Identifies the relative importance of a key among other keys. */
|
||||
def rank: Int
|
||||
|
||||
def optJsonWriter: OptJsonWriter[T]
|
||||
|
|
@ -133,60 +134,73 @@ object AttributeKey {
|
|||
}
|
||||
|
||||
/**
|
||||
* An immutable map where a key is the tuple `(String,T)` for a fixed type `T` and can only be associated with values of type `T`.
|
||||
* It is therefore possible for this map to contain mappings for keys with the same label but different types.
|
||||
* Excluding this possibility is the responsibility of the client if desired.
|
||||
* An immutable map where a key is the tuple `(String,T)` for a fixed type `T` and can only be
|
||||
* associated with values of type `T`. It is therefore possible for this map to contain mappings for
|
||||
* keys with the same label but different types. Excluding this possibility is the responsibility of
|
||||
* the client if desired.
|
||||
*/
|
||||
trait AttributeMap {
|
||||
|
||||
/**
|
||||
* Gets the value of type `T` associated with the key `k`.
|
||||
* If a key with the same label but different type is defined, this method will fail.
|
||||
* Gets the value of type `T` associated with the key `k`. If a key with the same label but
|
||||
* different type is defined, this method will fail.
|
||||
*/
|
||||
def apply[T](k: AttributeKey[T]): T
|
||||
|
||||
/**
|
||||
* Gets the value of type `T` associated with the key `k` or `None` if no value is associated.
|
||||
* If a key with the same label but a different type is defined, this method will return `None`.
|
||||
* Gets the value of type `T` associated with the key `k` or `None` if no value is associated. If
|
||||
* a key with the same label but a different type is defined, this method will return `None`.
|
||||
*/
|
||||
def get[T](k: AttributeKey[T]): Option[T]
|
||||
|
||||
/**
|
||||
* Returns this map without the mapping for `k`.
|
||||
* This method will not remove a mapping for a key with the same label but a different type.
|
||||
* Returns this map without the mapping for `k`. This method will not remove a mapping for a key
|
||||
* with the same label but a different type.
|
||||
*/
|
||||
def remove[T](k: AttributeKey[T]): AttributeMap
|
||||
|
||||
/**
|
||||
* Returns true if this map contains a mapping for `k`.
|
||||
* If a key with the same label but a different type is defined in this map, this method will return `false`.
|
||||
* Returns true if this map contains a mapping for `k`. If a key with the same label but a
|
||||
* different type is defined in this map, this method will return `false`.
|
||||
*/
|
||||
def contains[T](k: AttributeKey[T]): Boolean
|
||||
|
||||
/**
|
||||
* Adds the mapping `k -> value` to this map, replacing any existing mapping for `k`.
|
||||
* Any mappings for keys with the same label but different types are unaffected.
|
||||
* Adds the mapping `k -> value` to this map, replacing any existing mapping for `k`. Any mappings
|
||||
* for keys with the same label but different types are unaffected.
|
||||
*/
|
||||
def put[T](k: AttributeKey[T], value: T): AttributeMap
|
||||
|
||||
/** All keys with defined mappings. There may be multiple keys with the same `label`, but different types. */
|
||||
/**
|
||||
* All keys with defined mappings. There may be multiple keys with the same `label`, but different
|
||||
* types.
|
||||
*/
|
||||
def keys: Iterable[AttributeKey[_]]
|
||||
|
||||
/** Adds the mappings in `o` to this map, with mappings in `o` taking precedence over existing mappings.*/
|
||||
/**
|
||||
* Adds the mappings in `o` to this map, with mappings in `o` taking precedence over existing
|
||||
* mappings.
|
||||
*/
|
||||
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap
|
||||
|
||||
/** Combines the mappings in `o` with the mappings in this map, with mappings in `o` taking precedence over existing mappings.*/
|
||||
/**
|
||||
* Combines the mappings in `o` with the mappings in this map, with mappings in `o` taking
|
||||
* precedence over existing mappings.
|
||||
*/
|
||||
def ++(o: AttributeMap): AttributeMap
|
||||
|
||||
/** All mappings in this map. The [[AttributeEntry]] type preserves the typesafety of mappings, although the specific types are unknown.*/
|
||||
/**
|
||||
* All mappings in this map. The [[AttributeEntry]] type preserves the typesafety of mappings,
|
||||
* although the specific types are unknown.
|
||||
*/
|
||||
def entries: Iterable[AttributeEntry[_]]
|
||||
|
||||
/** `true` if there are no mappings in this map, `false` if there are. */
|
||||
def isEmpty: Boolean
|
||||
|
||||
/**
|
||||
* Adds the mapping `k -> opt.get` if opt is Some.
|
||||
* Otherwise, it returns this map without the mapping for `k`.
|
||||
* Adds the mapping `k -> opt.get` if opt is Some. Otherwise, it returns this map without the
|
||||
* mapping for `k`.
|
||||
*/
|
||||
private[sbt] def setCond[T](k: AttributeKey[T], opt: Option[T]): AttributeMap
|
||||
}
|
||||
|
|
@ -199,11 +213,11 @@ object AttributeMap {
|
|||
/** Constructs an [[AttributeMap]] containing the given `entries`. */
|
||||
def apply(entries: Iterable[AttributeEntry[_]]): AttributeMap = empty ++ entries
|
||||
|
||||
/** Constructs an [[AttributeMap]] containing the given `entries`.*/
|
||||
/** Constructs an [[AttributeMap]] containing the given `entries`. */
|
||||
def apply(entries: AttributeEntry[_]*): AttributeMap = empty ++ entries
|
||||
|
||||
/** Presents an `AttributeMap` as a natural transformation. */
|
||||
implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = λ[AttributeKey ~> Id](map(_))
|
||||
// implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = λ[AttributeKey ~> Id](map(_))
|
||||
}
|
||||
|
||||
private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any])
|
||||
|
|
@ -230,8 +244,8 @@ private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any])
|
|||
}
|
||||
|
||||
def entries: Iterable[AttributeEntry[_]] =
|
||||
backing.collect {
|
||||
case (k: AttributeKey[kt], v) => AttributeEntry(k, v.asInstanceOf[kt])
|
||||
backing.collect { case (k: AttributeKey[kt], v) =>
|
||||
AttributeEntry(k, v.asInstanceOf[kt])
|
||||
}
|
||||
|
||||
private[sbt] def setCond[T](k: AttributeKey[T], opt: Option[T]): AttributeMap =
|
||||
|
|
@ -269,7 +283,7 @@ object Attributed {
|
|||
/** Extracts the underlying data from the sequence `in`. */
|
||||
def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data)
|
||||
|
||||
/** Associates empty metadata maps with each entry of `in`.*/
|
||||
/** Associates empty metadata maps with each entry of `in`. */
|
||||
def blankSeq[T](in: Seq[T]): Seq[Attributed[T]] = in map blank
|
||||
|
||||
/** Associates an empty metadata map with `data`. */
|
||||
|
|
@ -83,8 +83,8 @@ object Dag {
|
|||
private[sbt] trait DirectedSignedGraph[Node] {
|
||||
|
||||
/**
|
||||
* Directed edge type that tracks the sign and target (head) vertex.
|
||||
* The sign can be obtained via [[isNegative]] and the target vertex via [[head]].
|
||||
* Directed edge type that tracks the sign and target (head) vertex. The sign can be obtained
|
||||
* via [[isNegative]] and the target vertex via [[head]].
|
||||
*/
|
||||
type Arrow
|
||||
|
||||
|
|
@ -103,10 +103,10 @@ object Dag {
|
|||
}
|
||||
|
||||
/**
|
||||
* Traverses a directed graph defined by `graph` looking for a cycle that includes a "negative" edge.
|
||||
* The directed edges are weighted by the caller as "positive" or "negative".
|
||||
* If a cycle containing a "negative" edge is detected, its member edges are returned in order.
|
||||
* Otherwise, the empty list is returned.
|
||||
* Traverses a directed graph defined by `graph` looking for a cycle that includes a "negative"
|
||||
* edge. The directed edges are weighted by the caller as "positive" or "negative". If a cycle
|
||||
* containing a "negative" edge is detected, its member edges are returned in order. Otherwise,
|
||||
* the empty list is returned.
|
||||
*/
|
||||
private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] = {
|
||||
import graph._
|
||||
|
|
@ -132,8 +132,7 @@ object Dag {
|
|||
between
|
||||
else
|
||||
visit(tail, stack)
|
||||
} else
|
||||
visit(tail, stack)
|
||||
} else visit(tail, stack)
|
||||
}
|
||||
|
||||
visit(graph.nodes, Nil)
|
||||
|
|
@ -9,7 +9,7 @@ package sbt.internal.util
|
|||
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
/** A mutable set interface that uses object identity to test for set membership.*/
|
||||
/** 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
|
||||
|
|
@ -32,8 +32,8 @@ object Signals {
|
|||
}
|
||||
|
||||
/**
|
||||
* Register a signal handler that can be removed later.
|
||||
* NOTE: Does not stack with other signal handlers!!!!
|
||||
* Register a signal handler that can be removed later. NOTE: Does not stack with other signal
|
||||
* handlers!!!!
|
||||
*/
|
||||
def register(handler: () => Unit, signal: String = INT): Registration =
|
||||
// TODO - Maybe we can just ignore things if not is-supported.
|
||||
|
|
@ -90,7 +90,8 @@ private final class Signals0 {
|
|||
val oldHandler = Signal.handle(intSignal, newHandler)
|
||||
|
||||
try Right(action())
|
||||
catch { case e: LinkageError => Left(e) } finally {
|
||||
catch { case e: LinkageError => Left(e) }
|
||||
finally {
|
||||
Signal.handle(intSignal, oldHandler); ()
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
package sbt.internal.util
|
||||
|
||||
/*
|
||||
trait TypeFunctions {
|
||||
import TypeFunctions._
|
||||
type Id[X] = X
|
||||
|
|
@ -54,7 +55,9 @@ object TypeFunctions extends TypeFunctions {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
trait ~>[-A[_], +B[_]] { outer =>
|
||||
def apply[T](a: A[T]): B[T]
|
||||
// directly on ~> because of type inference limitations
|
||||
|
|
@ -68,3 +71,4 @@ object ~> {
|
|||
val Id: Id ~> Id = idK[Id]
|
||||
implicit def tcIdEquals: Id ~> Id = Id
|
||||
}
|
||||
*/
|
||||
|
|
@ -9,8 +9,8 @@ package sbt.internal.util
|
|||
|
||||
object Types extends Types
|
||||
|
||||
trait Types extends TypeFunctions {
|
||||
val :^: = KCons
|
||||
type :+:[H, T <: HList] = HCons[H, T]
|
||||
val :+: = HCons
|
||||
trait Types /* extends TypeFunctions */ {
|
||||
// val :^: = KCons
|
||||
// type :+:[H, T <: HList] = HCons[H, T]
|
||||
// val :+: = HCons
|
||||
}
|
||||
|
|
@ -15,8 +15,8 @@ import scala.language.experimental.macros
|
|||
object Util {
|
||||
def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value)
|
||||
|
||||
def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
|
||||
separate(ps)(Types.idFun)
|
||||
// def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
|
||||
// separate(ps)(Types.idFun)
|
||||
|
||||
def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = {
|
||||
val (a, b) = ps.foldLeft((Nil: Seq[A], Nil: Seq[B]))((xs, y) => prependEither(xs, f(y)))
|
||||
|
|
@ -45,7 +45,7 @@ object Util {
|
|||
|
||||
def quoteIfKeyword(s: String): String = if (ScalaKeywords.values(s)) s"`${s}`" else s
|
||||
|
||||
def ignoreResult[T](f: => T): Unit = macro Macro.ignore
|
||||
// def ignoreResult[T](f: => T): Unit = macro Macro.ignore
|
||||
|
||||
lazy val isMac: Boolean =
|
||||
System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("mac")
|
||||
|
|
@ -73,7 +73,7 @@ object Util {
|
|||
implicit class AnyOps[A](private val value: A) extends AnyVal {
|
||||
def some: Option[A] = (Some(value): Option[A])
|
||||
}
|
||||
class Macro(val c: blackbox.Context) {
|
||||
def ignore(f: c.Tree): c.Expr[Unit] = c.universe.reify({ c.Expr[Any](f).splice; () })
|
||||
}
|
||||
// class Macro(val c: blackbox.Context) {
|
||||
// def ignore(f: c.Tree): c.Expr[Unit] = c.universe.reify({ c.Expr[Any](f).splice; () })
|
||||
// }
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.util
|
||||
|
||||
trait Applicative[F[_]] extends Apply[F]:
|
||||
def pure[A](x: A): F[A]
|
||||
|
||||
override def map[A, B](fa: F[A])(f: A => B): F[B] =
|
||||
ap(pure(f))(fa)
|
||||
end Applicative
|
||||
|
||||
object Applicative:
|
||||
given Applicative[Option] = OptionInstances.optionMonad
|
||||
end Applicative
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.util
|
||||
|
||||
trait Apply[F[_]] extends Functor[F]:
|
||||
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
|
||||
|
||||
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] =
|
||||
ap(map(fa)(a => (b: B) => (a, b)))(fb)
|
||||
end Apply
|
||||
|
||||
object Apply:
|
||||
given Apply[Option] = OptionInstances.optionMonad
|
||||
end Apply
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.util
|
||||
|
||||
import scala.annotation.implicitNotFound
|
||||
|
||||
@implicitNotFound("Could not find an instance of FlatMap for ${F}")
|
||||
trait FlatMap[F[_]] extends Apply[F]:
|
||||
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
|
||||
|
||||
def flatten[A](ffa: F[F[A]]): F[A] =
|
||||
flatMap(ffa)(fa => fa)
|
||||
end FlatMap
|
||||
|
||||
object FlatMap:
|
||||
given FlatMap[Option] = OptionInstances.optionMonad
|
||||
end FlatMap
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.util
|
||||
|
||||
trait Functor[F[_]]:
|
||||
def map[A, B](fa: F[A])(f: A => B): F[B]
|
||||
end Functor
|
||||
|
||||
object Functor:
|
||||
given Functor[Option] = OptionInstances.optionMonad
|
||||
end Functor
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.util
|
||||
|
||||
private[sbt] object ListInstances:
|
||||
lazy val listMonad: Monad[List] =
|
||||
new Monad[List]:
|
||||
def pure[A](x: A): List[A] = List(x)
|
||||
def ap[A, B](ff: List[A => B])(fa: List[A]): List[B] =
|
||||
for
|
||||
f <- ff
|
||||
a <- fa
|
||||
yield f(a)
|
||||
def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] = fa.flatMap(f)
|
||||
|
||||
override def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f)
|
||||
override def flatten[A](ffa: List[List[A]]): List[A] = ffa.flatten
|
||||
end ListInstances
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.util
|
||||
|
||||
import scala.annotation.implicitNotFound
|
||||
|
||||
@implicitNotFound("Could not find an instance of Monad for ${F}")
|
||||
trait Monad[F[_]] extends FlatMap[F] with Applicative[F]:
|
||||
//
|
||||
end Monad
|
||||
|
||||
object Monad:
|
||||
given Monad[Option] = OptionInstances.optionMonad
|
||||
end Monad
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.util
|
||||
|
||||
private[sbt] object OptionInstances:
|
||||
lazy val optionMonad: Monad[Option] =
|
||||
new Monad[Option]:
|
||||
def pure[A](x: A): Option[A] = Some(x)
|
||||
def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] =
|
||||
if ff.isDefined && fa.isDefined then Some(ff.get(fa.get))
|
||||
else None
|
||||
def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa.flatMap(f)
|
||||
|
||||
override def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f)
|
||||
override def flatten[A](ffa: Option[Option[A]]): Option[A] = ffa.flatten
|
||||
end OptionInstances
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.util
|
||||
|
||||
trait Selective[F[_]] extends Applicative[F]:
|
||||
def select[A, B](fab: F[Either[A, B]])(fn: F[A => B]): F[B]
|
||||
end Selective
|
||||
|
|
@ -7,11 +7,12 @@
|
|||
|
||||
package sbt.util
|
||||
|
||||
trait Show[A] {
|
||||
trait Show[A]:
|
||||
def show(a: A): String
|
||||
}
|
||||
object Show {
|
||||
end Show
|
||||
|
||||
object Show:
|
||||
def apply[A](f: A => String): Show[A] = a => f(a)
|
||||
|
||||
def fromToString[A]: Show[A] = _.toString
|
||||
}
|
||||
end Show
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import hedgehog.*
|
||||
import hedgehog.runner.*
|
||||
import _root_.sbt.util.Functor
|
||||
|
||||
object FunctorTest extends Properties:
|
||||
val F = summon[Functor[Option]]
|
||||
|
||||
override def tests: List[Test] =
|
||||
List(
|
||||
example("None", testNone),
|
||||
property("identity", identityProperty),
|
||||
property("composition", compositionProperty),
|
||||
property("map", mapProperty),
|
||||
)
|
||||
|
||||
def testNone: Result =
|
||||
Result.assert(F.map(None: Option[Int])(_ + 1) == None)
|
||||
|
||||
def identityProperty: Property =
|
||||
for x <- Gen.int(Range.linear(-100, 100)).forAll
|
||||
yield F.map(Some(x))(identity) ==== Some(x)
|
||||
|
||||
def mapProperty: Property =
|
||||
for
|
||||
x <- Gen.int(Range.linear(-100, 100)).forAll
|
||||
f <- genFun.forAll
|
||||
yield F.map(Some(x))(f) ==== Some(f(x))
|
||||
|
||||
def compositionProperty: Property =
|
||||
for
|
||||
x <- Gen.int(Range.linear(-100, 100)).forAll
|
||||
f <- genFun.forAll
|
||||
g <- genFun.forAll
|
||||
yield F.map(Some(x))(f compose g) ==== F.map(F.map(Some(x))(g))(f)
|
||||
|
||||
def genFun: Gen[Int => Int] =
|
||||
for x <- Gen.int(Range.linear(-100, 100))
|
||||
yield (_: Int) + x
|
||||
|
||||
end FunctorTest
|
||||
Loading…
Reference in New Issue