Rename reactor classes: Handler -> RulesList, IdWrapper -> Id

This commit is contained in:
Fedor Isakov 2019-05-09 20:00:06 +02:00
parent eb5b38384f
commit b482de1ce5
10 changed files with 129 additions and 124 deletions

View File

@ -9245,7 +9245,7 @@
<ref role="37wK5l" to="6rp1:~Map.get(java.lang.Object):java.lang.Object" resolve="get" />
<node concept="2ShNRf" id="3wxtToDZuS_" role="37wK5m">
<node concept="1pGfFk" id="3wxtToDZxLF" role="2ShVmc">
<ref role="37wK5l" to="bid0:~IdWrapper.&lt;init&gt;(java.lang.Object)" resolve="IdWrapper" />
<ref role="37wK5l" to="bid0:~Id.&lt;init&gt;(java.lang.Object)" resolve="Id" />
<node concept="37vLTw" id="3wxtToDZyci" role="37wK5m">
<ref role="3cqZAo" node="3wxtToDQuSt" resolve="orig" />
</node>
@ -9414,7 +9414,7 @@
<ref role="37wK5l" to="6rp1:~Map.put(java.lang.Object,java.lang.Object):com.github.andrewoma.dexx.collection.Map" resolve="put" />
<node concept="2ShNRf" id="3wxtToDZiI1" role="37wK5m">
<node concept="1pGfFk" id="3wxtToDZmkj" role="2ShVmc">
<ref role="37wK5l" to="bid0:~IdWrapper.&lt;init&gt;(java.lang.Object)" resolve="IdWrapper" />
<ref role="37wK5l" to="bid0:~Id.&lt;init&gt;(java.lang.Object)" resolve="Id" />
<node concept="37vLTw" id="3wxtToDZnda" role="37wK5m">
<ref role="3cqZAo" node="3wxtToDRnqb" resolve="orig" />
</node>

View File

@ -17,7 +17,7 @@
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.evaluation.RuleMatch
import jetbrains.mps.logic.reactor.util.IdWrapper
import jetbrains.mps.logic.reactor.util.Id
/**
@ -27,8 +27,8 @@ import jetbrains.mps.logic.reactor.util.IdWrapper
interface RuleMatchEx : RuleMatch {
/**
* Returns an array of matched constraint occurrences wrapped in [IdWrapper].
* Returns an array of matched constraint occurrences wrapped in [Id].
*/
fun signature(): ArrayList<IdWrapper<Occurrence>?>
fun signature(): ArrayList<Id<Occurrence>?>
}

View File

@ -17,7 +17,7 @@
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.core.internal.ProcessingState.*
import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus.*
import jetbrains.mps.logic.reactor.evaluation.*
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.LogicalContext
@ -52,33 +52,33 @@ internal class ControllerImpl (
override fun evaluate(occ: Occurrence): StoreView {
// create the internal occurrence
val active = occ.constraint().occurrence(occ.arguments(), { frameStack.current })
val state = process(active, NORMAL())
if (state is FAILED) {
throw state.failure.failureCause()
val status = process(active, NORMAL())
if (status is FAILED) {
throw status.failure.failureCause()
}
return storeView()
}
fun activate(constraint: Constraint) : ProcessingState {
fun activate(constraint: Constraint) : FeedbackStatus {
// FIXME noLogicalContext
val context = Context(NORMAL(), noLogicalContext)
activateConstraint(constraint, context)
return context.currentState()
return context.currentStatus()
}
override fun reactivate(occ: Occurrence) {
// FIXME propagate the processing state further up the call stack
// TODO: introduce processing state to solver API?
val state = process(occ, NORMAL())
if (state is FAILED) {
throw state.failure.failureCause()
// FIXME propagate the status further up the call stack
// TODO: introduce status to solver API?
val status = process(occ, NORMAL())
if (status is FAILED) {
throw status.failure.failureCause()
}
}
private fun process(active: Occurrence, inState: ProcessingState) : ProcessingState {
private fun process(active: Occurrence, inStatus: FeedbackStatus) : FeedbackStatus {
assert(active.alive)
return profiler.profile<ProcessingState>("process_${active.constraint().symbol()}") {
return profiler.profile<FeedbackStatus>("process_${active.constraint().symbol()}") {
if (!active.stored) {
frameStack.current.store.store(active)
@ -90,13 +90,13 @@ internal class ControllerImpl (
val activatedFront = dispatchFront.expand(active)
this.dispatchFront = activatedFront
val outState = activatedFront.matches().toList().fold(inState) { state, match ->
val outStatus = activatedFront.matches().toList().fold(inStatus) { status, match ->
// TODO: paranoid check. should be isAlive() instead
// FIXME: move this check elsewhere
if (state.operational && active.stored && match.allStored())
processMatch(state, match as RuleMatchImpl)
if (status.operational && active.stored && match.allStored())
processMatch(status, match as RuleMatchImpl)
else
state
status
}
// TODO: should be isAlive()
@ -104,12 +104,12 @@ internal class ControllerImpl (
trace.suspend(active)
}
outState
outStatus
}
}
private fun processMatch(inState: ProcessingState, match: RuleMatchImpl) : ProcessingState {
val context = Context(inState, match.logicalContext())
private fun processMatch(inStatus: FeedbackStatus, match: RuleMatchImpl) : FeedbackStatus {
val context = Context(inStatus, match.logicalContext())
// invoke matched pattern predicates
for (prd in match.patternPredicates()) {
@ -123,19 +123,19 @@ internal class ControllerImpl (
if (!askPredicate(gprd, context)) break
}
context.updateState { state ->
when (state) {
context.updateStatus { status ->
when (status) {
is ABORTED -> { // guard is not satisfied
trace.reject(match)
return state.recover()
return status.recover()
}
is FAILED -> { // guard failed
trace.feedback(state.failure)
return state.recover()
trace.feedback(status.failure)
return status.recover()
}
else -> state
else -> status
}
}
@ -152,13 +152,13 @@ internal class ControllerImpl (
while (altIt.hasNext()) {
val body = altIt.next()
context.updateState { state ->
if (state is FAILED) {
context.updateStatus { status ->
if (status is FAILED) {
trace.retry(match)
state.recover()
status.recover()
} else {
state
status
}
}
@ -173,37 +173,37 @@ internal class ControllerImpl (
}
if (itemOk) {
context.withState { state ->
if (state.feedback?.alreadyHandled() == false) {
state.feedback.handle(match.rule(), supervisor)
context.withStatus { status ->
if (status.feedback?.alreadyHandled() == false) {
status.feedback.handle(match.rule(), supervisor)
}
}
} else {
// state is not operational after constraint/predicate processing
// status is not operational after constraint/predicate processing
break
}
}
val altOk = context.updateState { state ->
if (state is FAILED) {
trace.feedback(state.failure)
val altOk = context.updateStatus { status ->
if (status is FAILED) {
trace.feedback(status.failure)
if (altIt.hasNext()) {
// clear the failure handled status
// the supervisor is NOT notified here
state.failure.handle(match.rule())
state
status.failure.handle(match.rule())
status
} else if (state.feedback?.alreadyHandled() == false && state.failure.handle(match.rule(), supervisor)) {
state.recover()
} else if (status.feedback?.alreadyHandled() == false && status.failure.handle(match.rule(), supervisor)) {
status.recover()
} else {
state
status
}
} else {
state
status
}
}
@ -219,26 +219,26 @@ internal class ControllerImpl (
trace.finish(match)
return context.currentState()
return context.currentStatus()
}
private fun activateConstraint(constraint: Constraint, context: Context) : Boolean {
val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context)
return context.updateState { state ->
return context.updateStatus { status ->
val active = constraint.occurrence(args, { frameStack.current }, context.logicalContext)
process(active, state)
process(active, status)
}
}
private fun askPredicate(predicate: Predicate, context: Context) : Boolean =
profiler.profile<Boolean>("ask_${predicate.symbol()}") {
context.evalSafe { state ->
context.evalSafe { status ->
val args = supervisor.instantiateArguments(predicate.arguments(), context.logicalContext, context)
if (session.ask(predicate.invocation(args, context.logicalContext, context)))
state
status
else
state.abort(DetailedFeedback("predicate not satisfied"))
status.abort(DetailedFeedback("predicate not satisfied"))
}
}
@ -266,52 +266,52 @@ internal class ControllerImpl (
}
private class Context(inState: ProcessingState,
private class Context(inStatus: FeedbackStatus,
val logicalContext: LogicalContext) : InvocationContext
{
private var state = inState
fun currentState(): ProcessingState = state
private var status = inStatus
fun currentStatus(): FeedbackStatus = status
override fun report(feedback: EvaluationFeedback) {
when (feedback) {
is EvaluationFailure -> this.state = state.fail(feedback)
is DetailedFeedback -> this.state = state.report(feedback)
is EvaluationFailure -> this.status = status.fail(feedback)
is DetailedFeedback -> this.status = status.report(feedback)
}
}
inline fun withState(block: (ProcessingState) -> Unit) {
block.invoke(state)
inline fun withStatus(block: (FeedbackStatus) -> Unit) {
block.invoke(status)
}
inline fun updateState(block: (ProcessingState) -> ProcessingState) : Boolean {
this.state = block.invoke(state)
return state.operational
inline fun updateStatus(block: (FeedbackStatus) -> FeedbackStatus) : Boolean {
this.status = block.invoke(status)
return status.operational
}
inline fun evalSafe(block: (ProcessingState) -> ProcessingState) : Boolean {
if (state.operational) {
inline fun evalSafe(block: (FeedbackStatus) -> FeedbackStatus) : Boolean {
if (status.operational) {
try {
this.state = block.invoke(state)
this.status = block.invoke(status)
} catch (ex: EvaluationFailureException) {
this.state = state.fail(EvaluationFailure(ex))
this.status = status.fail(EvaluationFailure(ex))
}
}
return state.operational
return status.operational
}
inline fun runSafe(block: () -> Unit) : Boolean {
if (state.operational) {
if (status.operational) {
try {
block()
} catch (ex: EvaluationFailureException) {
this.state = state.fail(EvaluationFailure(ex))
this.status = status.fail(EvaluationFailure(ex))
}
}
return state.operational
return status.operational
}
}

View File

@ -16,13 +16,11 @@
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.EvaluationFailure
import jetbrains.mps.logic.reactor.core.internal.ProcessingState.FAILED
import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus.FAILED
import jetbrains.mps.logic.reactor.core.EvaluationSessionEx
import jetbrains.mps.logic.reactor.core.Feedback
import jetbrains.mps.logic.reactor.core.RuleIndex
import jetbrains.mps.logic.reactor.evaluation.*
import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.Program
import jetbrains.mps.logic.reactor.util.Profiler
@ -45,7 +43,7 @@ internal class EvaluationSessionImpl private constructor (
override fun controller() = controller
private fun launch(main: Constraint, profiler: Profiler?, storeView: StoreView?) : ProcessingState {
private fun launch(main: Constraint, profiler: Profiler?, storeView: StoreView?) : FeedbackStatus {
this.controller = ControllerImpl(supervisor, RuleIndex(program().handlers()), trace, profiler, storeView)
return controller.activate(main)
}
@ -87,9 +85,9 @@ internal class EvaluationSessionImpl private constructor (
var failure: Feedback? = null
try {
val main = parameters[ParameterKey.of("main", Constraint::class.java)] as Constraint
val state = session.launch(main, profiler, storeView)
if (state is FAILED) {
failure = state.failure
val status = session.launch(main, profiler, storeView)
if (status is FAILED) {
failure = status.failure
}
}
finally {

View File

@ -18,44 +18,51 @@ package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.Feedback
import jetbrains.mps.logic.reactor.core.CompositeFeedback
import jetbrains.mps.logic.reactor.core.EvaluationFailure
import jetbrains.mps.logic.reactor.evaluation.EvaluationFeedback
abstract class ProcessingState(val feedback : Feedback?) {
/**
* Corresponds to the status given by the most recent constraint activation or predicate evaluation.
*
* A disjoint union of types:
* NORMAL - the feedback is neutral or there is no feedback
* FAILED - the feedback is a failure
* ABORTED - the feedback is negative, but not a failure
*
*/
abstract class FeedbackStatus(val feedback : Feedback?) {
abstract val operational : Boolean
// inline fun eval(block: (ProcessingState) -> ProcessingState): ProcessingState =
// inline fun eval(block: (FeedbackStatus) -> FeedbackStatus): FeedbackStatus =
// if (operational) block(this) else this
/** Stop what is being done because a required condition is not satisfied. */
open fun abort(details: Feedback) : ProcessingState = throw IllegalStateException()
open fun abort(details: Feedback) : FeedbackStatus = throw IllegalStateException()
/** Failure occurred during processing. */
open fun fail(failure: Feedback) : ProcessingState = throw IllegalStateException()
open fun fail(failure: Feedback) : FeedbackStatus = throw IllegalStateException()
/** Provide detailed feedback. */
open fun report(details: Feedback) : ProcessingState = throw IllegalStateException()
open fun report(details: Feedback) : FeedbackStatus = throw IllegalStateException()
/** Move back to normal. */
open fun recover() : ProcessingState = throw IllegalStateException()
open fun recover() : FeedbackStatus = throw IllegalStateException()
class NORMAL(feedback: Feedback? = null) : ProcessingState(feedback) {
class NORMAL(feedback: Feedback? = null) : FeedbackStatus(feedback) {
override val operational = true
override fun abort(details: Feedback): ProcessingState = ABORTED(this, details)
override fun fail(failure: Feedback): ProcessingState = FAILED(this, failure)
override fun report(details: Feedback): ProcessingState = NORMAL(compose(this.feedback, details))
override fun abort(details: Feedback): FeedbackStatus = ABORTED(this, details)
override fun fail(failure: Feedback): FeedbackStatus = FAILED(this, failure)
override fun report(details: Feedback): FeedbackStatus = NORMAL(compose(this.feedback, details))
}
class FAILED(state: ProcessingState, val failure: Feedback) : ProcessingState(compose(state.feedback, failure)) {
class FAILED(status: FeedbackStatus, val failure: Feedback) : FeedbackStatus(compose(status.feedback, failure)) {
override val operational = false
/** Recover after a failure or a cancellation, bring the state back to operational. */
override fun recover(): ProcessingState = NORMAL(feedback)
/** Recover after a failure or a cancellation, bring the status back to operational. */
override fun recover(): FeedbackStatus = NORMAL(feedback)
}
class ABORTED(state: ProcessingState, val reason: Feedback) : ProcessingState(compose(state.feedback, reason)) {
class ABORTED(status: FeedbackStatus, val reason: Feedback) : FeedbackStatus(compose(status.feedback, reason)) {
override val operational = false
override fun recover(): ProcessingState = NORMAL(feedback)
override fun recover(): FeedbackStatus = NORMAL(feedback)
}
}

View File

@ -24,7 +24,7 @@ import jetbrains.mps.logic.reactor.core.LogicalObserver
import jetbrains.mps.logic.reactor.core.addObserver
import jetbrains.mps.logic.reactor.evaluation.StoreView
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.util.IdWrapper
import jetbrains.mps.logic.reactor.util.Id
import jetbrains.mps.logic.reactor.util.cons
import jetbrains.mps.logic.reactor.util.remove
import java.util.*
@ -35,7 +35,7 @@ internal class Frame: LogicalObserver, FrameObservable {
val store: Store
private var observers: Map<IdWrapper<Logical<*>>, ConsList<(FrameObservable) -> LogicalObserver>>
private var observers: Map<Id<Logical<*>>, ConsList<(FrameObservable) -> LogicalObserver>>
constructor(stack: FrameStack) {
this.stack = stack
@ -58,7 +58,7 @@ internal class Frame: LogicalObserver, FrameObservable {
override fun storeObserver() = store
override fun addObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
val logicalId = IdWrapper(logical)
val logicalId = Id(logical)
if (!observers.containsKey(logicalId)) {
stack.addObserver(logical)
}
@ -67,7 +67,7 @@ internal class Frame: LogicalObserver, FrameObservable {
}
override fun removeObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
val logicalId = IdWrapper(logical)
val logicalId = Id(logical)
observers[logicalId].remove(obs)?.let { newList ->
this.observers = observers.put(logicalId, newList)
if (newList.isEmpty) {
@ -77,7 +77,7 @@ internal class Frame: LogicalObserver, FrameObservable {
}
override fun valueUpdated(logical: Logical<*>) {
observers[IdWrapper(logical)]?.let { list ->
observers[Id(logical)]?.let { list ->
for (obs in list) {
obs(this).valueUpdated(logical)
}
@ -85,7 +85,7 @@ internal class Frame: LogicalObserver, FrameObservable {
}
override fun parentUpdated(logical: Logical<*>) {
observers[IdWrapper(logical)]?.let { list ->
observers[Id(logical)]?.let { list ->
for (obs in list) {
obs(this).parentUpdated(logical)
}
@ -98,7 +98,7 @@ internal class FrameStack(storeView: StoreView?) : LogicalObserver {
var current: Frame
val observing = HashSet<IdWrapper<Logical<*>>>()
val observing = HashSet<Id<Logical<*>>>()
init {
this.current = if (storeView != null) Frame(this, storeView) else Frame(this)
@ -115,7 +115,7 @@ internal class FrameStack(storeView: StoreView?) : LogicalObserver {
}
fun addObserver(logical: Logical<*>) {
val token = IdWrapper(logical)
val token = Id(logical)
if (!observing.contains(token)) {
logical.addObserver(this)
observing.add(token)

View File

@ -25,7 +25,7 @@ import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.logical.LogicalOwner
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.logic.reactor.util.IdWrapper
import jetbrains.mps.logic.reactor.util.Id
internal class RuleMatchImpl(private val rule: Rule,
private val subst: Subst,
@ -51,8 +51,8 @@ internal class RuleMatchImpl(private val rule: Rule,
inline fun forEachReplaced(action: (Occurrence) -> Unit) =
headReplaced.forEach(action)
override fun signature(): ArrayList<IdWrapper<Occurrence>?> =
ArrayList((headKept + headReplaced).map { IdWrapper(it) })
override fun signature(): ArrayList<Id<Occurrence>?> =
ArrayList((headKept + headReplaced).map { Id(it) })
override fun rule(): Rule = rule

View File

@ -49,9 +49,9 @@ internal class RuleMatcherImpl(private val ruleLookup: RuleLookup,
0)
inner class RuleMatchFront(private val nodes: List<MatchNode>,
private val seenOccurrences: PersSet<IdWrapper<Occurrence>>,
private val consumedSignatures: PersSet<ArrayList<IdWrapper<Occurrence>?>>,
private val genId: Int) : RuleMatchingProbe
private val seenOccurrences: PersSet<Id<Occurrence>>,
private val consumedSignatures: PersSet<ArrayList<Id<Occurrence>?>>,
private val genId: Int) : RuleMatchingProbe
{
override fun rule(): Rule = lookupRule()
@ -74,8 +74,8 @@ internal class RuleMatcherImpl(private val ruleLookup: RuleLookup,
* Mask specifies possible slots for the occurrence.
*/
override fun expand(occ: Occurrence, mask: BitSet): RuleMatchFront {
val reactivated = seenOccurrences.contains(IdWrapper(occ))
val newSeen = if (reactivated) seenOccurrences else seenOccurrences.add(IdWrapper(occ))
val reactivated = seenOccurrences.contains(Id(occ))
val newSeen = if (reactivated) seenOccurrences else seenOccurrences.add(Id(occ))
val newNodes = ArrayList<MatchNode>(nodes)
val allSignatures = newNodes.map { it.signature }.toHashSet()
@ -99,7 +99,7 @@ internal class RuleMatcherImpl(private val ruleLookup: RuleLookup,
open inner class MatchNode(val subst: Subst, val vacant: BitSet = bitSetOfOnes(head.size)) {
// a signature is a (partial) set of constraint occurrences that belong to this node
open val signature: ArrayList<IdWrapper<Occurrence>?> = arrayListOf(* arrayOfNulls(head.size))
open val signature: ArrayList<Id<Occurrence>?> = arrayListOf(* arrayOfNulls(head.size))
/**
* Returns the additional nodes built from this node on adding the occurrence.
@ -162,8 +162,8 @@ internal class RuleMatcherImpl(private val ruleLookup: RuleLookup,
MatchNode(subst, parent.vacant.clearBit(headIndex)) {
val complete = vacant.cardinality() == 0
override val signature: ArrayList<IdWrapper<Occurrence>?> =
ArrayList(parent.signature).also { it[headIndex] = IdWrapper(occurrence) }
override val signature: ArrayList<Id<Occurrence>?> =
ArrayList(parent.signature).also { it[headIndex] = Id(occurrence) }
fun constraint(): Constraint = head[headIndex]

View File

@ -46,7 +46,7 @@ internal class Store : LogicalObserver {
var symbol2occurrences: PersMap<ConstraintSymbol, IdHashSet<Occurrence>>
var logical2occurrences: PersMap<IdWrapper<Logical<*>>, IdHashSet<Occurrence>>
var logical2occurrences: PersMap<Id<Logical<*>>, IdHashSet<Occurrence>>
constructor(copyFrom: Store, currentFrame: () -> FrameObservable) {
this.currentFrame = currentFrame
@ -62,7 +62,7 @@ internal class Store : LogicalObserver {
constructor(copyFrom: StoreView, currentFrame: () -> FrameObservable) {
this.currentFrame = currentFrame
var log2occs = Maps.of<IdWrapper<Logical<*>>, IdHashSet<Occurrence>>()
var log2occs = Maps.of<Id<Logical<*>>, IdHashSet<Occurrence>>()
val orig2occ = IdentityHashMap<ConstraintOccurrence, Occurrence>()
// process the original data and make copies as needed
@ -72,7 +72,7 @@ internal class Store : LogicalObserver {
for (arg in orig.arguments()) {
when (arg) {
is Logical<*> -> {
val key = IdWrapper(arg.findRoot())
val key = Id(arg.findRoot())
log2occs = log2occs.put(key,
log2occs[key]?.add(occ) ?: singletonIdSet(occ))
currentFrame().addObserver(arg) { frame -> frame.storeObserver() }
@ -101,9 +101,9 @@ internal class Store : LogicalObserver {
override fun parentUpdated(logical: Logical<*>) {
// TODO: should we care about the order in which occurrences are stored?
val logicalId = IdWrapper(logical)
val logicalId = Id(logical)
logical2occurrences[logicalId]?.let { toMerge ->
val rootId = IdWrapper(logical.findRoot())
val rootId = Id(logical.findRoot())
var newSet = logical2occurrences[rootId] ?: emptyIdSet()
for (log in toMerge) {
newSet = newSet.add(log)
@ -126,7 +126,7 @@ internal class Store : LogicalObserver {
when (value) {
is Logical<*> -> {
// free logical
val argId = IdWrapper(value.findRoot())
val argId = Id(value.findRoot())
this.logical2occurrences = logical2occurrences.put(argId,
logical2occurrences[argId]?.add(occ) ?: singletonIdSet(occ))
currentFrame().addObserver(value) { frame -> frame.storeObserver() }
@ -147,7 +147,7 @@ internal class Store : LogicalObserver {
for (arg in occ.arguments()) {
when (arg) {
is Logical<*> -> {
val argId = IdWrapper(arg.findRoot())
val argId = Id(arg.findRoot())
logical2occurrences[argId]?.remove(occ)?.let { newList ->
this.logical2occurrences = if (newList.isEmpty) {
logical2occurrences.remove(argId)

View File

@ -16,14 +16,14 @@
package jetbrains.mps.logic.reactor.util
class IdWrapper<T>(val wrapped: T) {
class Id<T>(val wrapped: T) {
val idHash = System.identityHashCode(wrapped)
override fun hashCode(): Int = idHash
override fun equals(other: Any?): Boolean {
if (other is IdWrapper<*>)
if (other is Id<*>)
return this.wrapped === other.wrapped // referential equality!
return false
}