1 prototypes define: #Trait &parents: {Cloneable}
2 &slots: {#users -> IdentitySet new "The Trait or Traits object that use this one."
4 "A Trait object is an individual protocol that exists for compile-time
5 composition into Traits objects. In this way we can divide up protocols as many
6 ways as conceptually appropriate without run-time overhead, and compile-time
7 checks can verify completeness of the compositions.
9 The methods defined for traits are arbitrary multi-methods and can dispatch
10 on anything. However, use of the traits object itself in such a method
11 definition does not add a role to that object but just creates a place in the
12 signature where it is indicated that the future composition result will have
16 [t clone `setting: #{#users} to: {t users new}].
18 t@(Trait traits) noteUseBy: user@(Trait traits)
19 [t users include: user].
21 t@(Trait traits) noteUseEndedBy: user@(Trait traits)
23 t users exclude: user.
24 t users isEmpty ifTrue: [t finalize].
27 t@(Trait traits) notifyUsersOfChange
28 [t users do: #handleChangedComponent `er].
30 t@(Trait traits) handleChangedComponent
33 [| :user | (user is: Trait) ifTrue: [user notifyUsersOfChange]
34 ifFalse: [error: 'Recompilating concrete Trait users is not yet implemented.']]
37 t@(Trait traits) addMutatorForTraitSlot: name
38 "Allow changes for slots whose type will always be Trait to automatically
39 update the usage Set for that Trait and the old Trait, if any."
43 user trait ifNotNilDo: #(noteUseEndedBy: user) `er.
44 user atSlotNamed: name put: trait.
45 trait ifNotNil: [trait noteUseBy: user].
49 x@(Root traits) finalize [].
51 t@(Trait traits) services [overrideThis].
52 "Answer the methods that are held for installation on composed results."
54 t@(Trait traits) flattened
55 "Take the services that this Trait has, and turn them into a single concrete
56 component Trait, thus flattening any compositions."
57 [TraitComponent newFrom: t services].
59 t@(Trait traits) signatures [t services keySet].
60 "The keys of the services are the relevant Signature objects."
61 t@(Trait traits) methods [t services valueSet].
62 "The values of the services are the actual methods to install."
64 t@(Trait traits) hasValidSignatures
65 "Answers whether all of the signatures in the provided services actually
66 dispatch at least in some way on the Trait(?)."
67 "TODO: evaluate/improve the query for the role test"
69 t signatures allSatisfy:
70 [| :sig | sig roles anySatisfy: #(t is: _) `er]
73 t@(Trait traits) hasValidServices
75 t hasValidSignatures /\
76 [| check | check := True.
77 t services keysAndValuesDo:
78 [| :sig :m | check := check /\ [sig arity = m arity]].
82 t@(Trait traits) conflicts [overrideThis].
83 "Answers those service associations which are inconsistent /and/ are not
84 resolved here in an unambiguous way (say, by an override)."
85 t@(Trait traits) hasConflicts [t conficts isEmpty not].
86 "Answer whether there are any service definition conflicts."
88 t@(Trait traits) providedSelectors
89 "Answers a set of all the selectors used in the service signatures."
90 [t signatures collect: #selector `er].
92 t@(Trait traits) requiredSelectors
93 "Answer all the selectors sent which should be defined on users of the Trait,
94 but is not in the services selector set."
96 requirements := Set new.
97 t providedSelectors `cacheAs: #provisions.
100 (0 below: m arity) do:
101 [| :index | requirements addAll:
102 ((m allSelectorsSentToInputAt: index) difference: provisions)]].
106 t@(Trait traits) isConcrete [t requiredSelectors isEmpty].
107 t@(Trait traits) isAbstract [t isConcrete not].
109 t1@(Trait traits) conflictsWith: t2@(Trait traits)
110 "Detects those Signatures which are bound to different Method objects in
111 the two Trait objects."
113 allServices := Dictionary new.
115 [| :base | base services keysAndValuesDo:
116 [| :sig :m | (allServices at: sig ifAbsentPut: [Set new]) include: m]].
117 allServices mapSelect: [| :sig :methods | methods size > 1]
120 t1@(Trait traits) = t2@(Trait traits)
121 "Compare Trait objects by their services Mappings."
122 [t1 services = t2 services].
124 t@(Trait traits) translate: sig for: target
125 "This translates the convention of using the Trait object as place holder for
126 the target installation object into the actual target intended Signature."
127 [sig clone `setting: #{#roles} to: {sig roles copyReplace: t with: target}].
129 t@(Trait traits) installServicesOn: target
131 t services keysAndValuesDo:
132 [| :sig :m | (t translate: sig for: target) defineAs: m]
135 x@(Root traits) speaks: t@(Trait traits)
136 "Answer whether the given object has all of the services defined by the
139 t services keysAndValuesDo:
140 [| :sig :m targetSig | targetSig := (t translate: sig for: target).
141 targetSig isDefined /\ [targetSig nearestMethod = m] ifFalse: [^ False]].
145 t@(Trait traits) hasSubTrait: sub@(Trait traits)
148 "TODO: this extension of is: to consider Trait composition is too conflating.
149 x@(Root traits) is: t@(Trait traits)
150 [x traits hasSubTrait: t].
153 def@(Syntax MethodDefinition traits) asServiceOf: tr
154 "A macro to transform a method definition into a service entry on the Trait
155 object that the second argument should evaluate to (or be, if not a piece
156 of syntax). It expands into a literal of the method object that became a
157 service of the Trait."
159 (tr is: Syntax Node) ifTrue: [tr := tr evaluate].
160 (tr is: Trait) ifFalse: [error: 'The target for installation was not a Trait.'].
161 roles := (def roles collect: #evaluate `er).
162 (roles includes: tr) ifFalse: [error: 'The Trait is not dispatched upon.'].
163 service := (tr services at: (Method Signature newNamed: def selector over: roles)
164 put: (def deepCopy as: Syntax Block) compile) unquoted.
165 tr notifyUsersOfChange.
169 prototypes define: #TraitComponent &parents: {Trait}
170 &slots: {#services -> Dictionary new "Maps Signatures to Method objects."
173 t@(TraitComponent traits) newFrom: map@(Mapping traits)
174 "Create a TraitComponent from valid signature->method associations."
175 "TODO: check roles in the signatures?"
177 map := map mapSelect:
178 [| :key :value | (key is: Method Signature) /\ [value is: Method]].
179 t new `>> [services := map as: t services. ]
182 TraitComponent traits compareAndHashUsingSlots: #{#services}.
184 t@(TraitComponent traits) conflicts
185 "A component cannot conflict with itself because of the Dictionary basis."
186 [Set new &capacity: 0].
188 t@(TraitComponent traits) hasSubTrait: sub@(Trait traits)
191 t@(TraitComponent traits) flattened [t].
193 prototypes define: #TraitSum &parents: {Trait} &slots: {#bases -> Set new}.
194 "A symmetric sum, where no precedence is given to any of the bases."
196 t@(TraitSum traits) newFor: bases
200 bases := bases as: t bases.
201 bases do: #(noteUseBy: t) `er. ]
204 TraitSum traits compareAndHashUsingSlots: #{#bases}.
206 t1@(Trait traits) + t2@(Trait traits)
207 [TraitSum newFor: {t1. t2}].
209 ts@(TraitSum traits) + t@(Trait traits)
210 [ts newFor: (ts bases copyWith: t)].
212 t@(Trait traits) + ts@(TraitSum traits)
213 [ts newFor: (ts bases copyWith: t)].
215 ts@(TraitSum traits) conflicts
216 "Detects those Signatures which are bound to different Method objects in
219 allServices := Dictionary new.
221 [| :base | base services keysAndValuesDo:
222 [| :sig :m | (allServices at: sig ifAbsentPut: [Set new]) include: m]].
223 allServices mapSelect: [| :sig :methods | methods size > 1]
226 ts@(TraitSum traits) services
227 [ts bases reduce: [| :b1 :b2 | b1 services union: b2 services]].
229 ts@(TraitSum traits) hasSubTrait: t@(Trait traits)
230 [(ts bases includes: t) \/ [ts bases anySatisfy: #(hasSubTrait: t) `er]].
232 prototypes define: #TraitModifier &parents: {Trait} &slots: {#base -> Trait}.
233 TraitModifier addMutatorForTraitSlot: #base.
235 tm@(TraitModifier traits) finalize
238 TraitModifier traits compareAndHashUsingSlots: #{#base}.
240 tm@(TraitModifier traits) hasSubTrait: t@(Trait traits)
241 [tm base = t \/ [tm base hasSubTrait: t]].
243 prototypes define: #TraitOverride &parents: {TraitModifier}
244 &slots: {#layer -> Trait}.
245 "A linear overlay composition of other Trait objects."
247 TraitOverride addMutatorForTraitSlot: #overlay.
249 TraitOverride traits compareAndHashUsingSlots: #{#base. #overlay}.
251 to@(TraitOverride traits) newBasedOn: base layering: overlay
252 [to new `>> [base := base. overlay := overlay. ]].
254 t@(Trait traits) as: to@(TraitOverride traits)
255 [to new `>> [overlay := overlay. ]].
257 to@(TraitOverride traits) newFrom: map@(Mapping traits)
258 [(TraitComponent newFrom: map) as: to].
260 base@(Trait traits) ** layer@(Trait traits)
261 [TraitOverride newBasedOn: base layering: layer].
263 to@(TraitOverride traits) finalize
269 to@(TraitOverride traits) services
270 "Apply the overlay to the underlying Trait."
271 [to overlay services over: to base services].
273 to@(TraitOverride traits) conflicts
274 "Count the conflicts remaining after applying the overlay."
275 [(to services as: TraitComponent) conflicts].
277 to@(TraitOverride traits) hasSubTrait: t@(Trait traits)
278 [to overlay = t \/ [to overlay hasSubTrait: t] \/ [resend]].
280 prototypes define: #TraitExclusion &parents: {TraitModifier}
281 &slots: {#exclusion -> Trait}.
282 TraitExclusion addMutatorForTraitSlot: #exclusion.
284 te@(TraitExclusion traits) newBasedOn: base excluding: exclusion
285 [te new `>> [base := base. exclusion := exclusion. ]].
287 TraitExclusion traits compareAndHashUsingSlots: #{#base. #exclusion}.
289 t@(Trait traits) as: te@(TraitExclusion traits)
290 [te new `>> [exclusion := exclusion. ]].
292 te@(TraitExclusion traits) newFrom: map@(Mapping traits)
293 [(TraitComponent newFrom: map) as: te].
295 base@(Trait traits) - exclusion@(Trait traits)
296 [TraitExclusion newBasedOn: base excluding: exclusion].
298 te@(TraitExclusion traits) services
300 te base services mapReject:
301 [| :sig :m | (te exclusion services) includesKey: sig]
304 te@(TraitExclusion traits) finalize
310 te@(TraitExclusion traits) hasSubTrait: t@(Trait traits)
311 [te exclusion = t \/ [te exclusion hasSubTrait: t] \/ [resend]].
313 prototypes define: #TraitWithAlias &parents: {TraitModifier}
314 &slots: {#aliases -> Dictionary new "Map from alias Signatures to original."}.
316 TraitWithAlias traits compareAndHashUsingSlots: #{#base. #aliases}.
318 ta@(TraitWithAlias traits) newBasedOn: base aliasing: map
319 [ta new `>> [base := base. aliases := map as: ta aliases. ]].
321 base@(Trait traits) aliasing: map
322 [TraitWithAlias newBasedOn: base aliasing: map].
324 prototypes define: #TraitWrapper &parents: {Trait}
325 &slots: {#basis. #cachedServices}.
326 "A TraitWrapper allows non-Trait objects to participate in Trait compositions."
328 tw@(TraitWrapper traits) newFor: obj
329 [tw new `>> [basis := obj. cachedServices := Nil. ]].
331 obj as: tw@(TraitWrapper traits)
334 tw@(TraitWrapper traits) services
335 "TODO: speed this up by not looking up signatures for methods stored in
336 other trait objects that the basis object uses."
338 tw cachedServices ifNil:
339 [tw cachedServices := tw basis methods reverseProject: #signature `er ]
342 tw@(TraitWrapper traits) hasSubTrait: t@(Trait traits)