-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDomain.class.st
More file actions
857 lines (714 loc) · 26.3 KB
/
Copy pathDomain.class.st
File metadata and controls
857 lines (714 loc) · 26.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
"
'Objects' of some category (such as Groups, Rings, Modules, Schemes, etc) connected by the corresponding Morphisms. In many practical situations, domains can be seen as sets of elements, and their morphisms as some kind of Functions.
Domains with an underlying set can have finite or infinite cardinality, and whenever makes sense they implement some *Collection* protocol (such as #do:, #size, #asSet, #atRandom:, etc).
Domains can be derived from other domains, for example by taking products or coproducts (cartesian products, direct products, direct sums), quotients, extensions, localizations, completions, duals, etc. These operations are functors, and are mostly implemented as Smalltalk messages.
Domains can have subobjects: groups have subgroups, vector spaces have subspaces, rings have subrings and ideals, schemes have subschemes, etc. In general, there is an #ambient overobject, and there is a protocol for inclusion testing with #<, #<=, #>=, #> and membership testing witih #includes: and #contains:.
Given two domains A and B in the same category, the set Hom(A,B) of morphisms between them is obtained by sending the message 'A hom: B' (see HomSet). The #hom: message is the Hom(A,-) functor. The identity morphism is returned by the message #id. And a morphism from A to B can be constructed by sending 'A to: B evaluating: [:x| ...]'.
Domains often come equipped with cannonical morphisms. For example, field extensions and localizations have canonical embeddings (monomorphisms), and implement #embed: (the embedding implemented as a message) and #embedding (which returns the embedding morphism as an instance of some subclass of Morphism). Quotient objects have canonical projections (epimorphisms) and implement #project: and #projection. N-ary products and coproducts implement #projection: for the i-th projection map (and coproducts also implement #embedding: for the i-th coprojection).
More generally, canonical morphisms that are uniquely determined by their domain and codomain can be retrieved by sending the message #. This can be used for automatic coercion of elements between domains. For example, for any ring R, 'ZZ R' returns the canonical ring homomorphism that sends an integer 'n' to 'n times the unit of R'. In order for this mechanism to work, subclasses must sometimes reimplement the messages #to: and #from: to answer canonical morphisms when such morphisms are known, and call super otherwise.
There's also a very simple coercion mechanism that allows to convert elements from one domain to another via the message #!. For example, the coercion of an integer 'n' to an element of any ring R can be performed simply as 'R ! n'. Subclasses must sometimes reimplement the message #! to convert the argument when possible, and call super otherwise.
References:
https://en.wikipedia.org/wiki/Algebraic_structure
https://en.wikipedia.org/wiki/Category_theory
https://en.wikipedia.org/wiki/Subobject
https://en.wikipedia.org/wiki/Product_(category_theory)
https://en.wikipedia.org/wiki/Coproduct
"
Class {
#name : #Domain,
#superclass : #Object,
#instVars : [
'properties'
],
#category : #'Mathematics-Kernel'
}
{ #category : #examples }
Domain class >> example1asSet [
"The ring Z/5Z as a Smalltalk Set.
This is possible because the ring is finite."
^ (ZZ / 5) asSet
]
{ #category : #examples }
Domain class >> example1atRandom [
"Taking an element at random from the Z-module of 3x3 matrices.
Use 50 bits to generate this element. Essentially, this can return
at most 2^50 different matrices, and their bitsize doesn't exceed 50
bits (where bitsize is losely defined, in this case it's the sum
of bitsize of the coefficients of the matrix)."
^ (ZZ raisedTo: 3 @ 3) atRandom: (Random seed: 12345) bits: 50
]
{ #category : #examples }
Domain class >> example21VectorSpace [
"The vector space of 3-tuples with rational coefficients."
^ QQ raisedTo: 3
]
{ #category : #examples }
Domain class >> example21VectorSpace2 [
"The vector space of 3x3 matrices with complex coefficients."
^ CC raisedTo: (3@3)
]
{ #category : #examples }
Domain class >> example2ModuleDirectSum [
"The direct sum of the module of 3x3 matrices
with coefficients in Z/6Z plus the rank 1 module
of Z/6Z scalars."
^ 'ZZ/6 ^ (3@3) ê (ZZ/6) asSelfModule'
]
{ #category : #examples }
Domain class >> example3DirectProductOfGroups [
"The direct product of the group of permutations of
the symbols {#a, #b, #c} by the group of alternating
permutations of {1,2,3,4}."
^ '(SymmetricGroup on: #(a b c)) ◊ (AlternatingGroup new: 4)'
]
{ #category : #examples }
Domain class >> example41Polynomials [
"Polynomials in two variables with integer coefficients."
^ ZZ polynomialsIn: #(a b)
]
{ #category : #examples }
Domain class >> example42AffineRing [
"The quotient of a polynomial ring by an ideal.
This is an affine ring, in particular it is the coordinate ring of the twisted cubic."
| R x y z I |
R := CC polynomialsIn: #(x y z).
x := R x: 1.
y := R x: 2.
z := R x: 3.
I := R
*
{((x raisedTo: 2) - y).
((x raisedTo: 3) - z)}.
^ R / I
]
{ #category : #examples }
Domain class >> example43AffineVariety [
"This is the twisted cubic affine variety, parameterized by (x, x^2, x^3) in C^3."
| R x y z I |
R := CC polynomialsIn: #(x y z).
x := R x: 1.
y := R x: 2.
z := R x: 3.
I := R
*
{((x raisedTo: 2) - y).
((x raisedTo: 3) - z)}.
^ (R / I) spec
]
{ #category : #examples }
Domain class >> example5GL [
"The group GL(Q^3) of invertible linear maps
on the vector space of 3-tuples with rational coefficients.
This is the automorphisms group of the vector space, and
its elements are linear maps, not matrices."
^ (QQ raisedTo: 3) automorphisms
]
{ #category : #examples }
Domain class >> example5GLAsMatrixGroup [
"The matrix group GL(3,Q) of 3x3 matrices with rational coefficients."
^ GeneralLinearMatrixGroup new: 3 over: QQ
]
{ #category : #'instance creation' }
Domain class >> newFrom: anObject [
^ self new elements: anObject
]
{ #category : #elements }
Domain >> ! anObject [
"Coerce anObject to an element of the receiver. Subclasses should extend."
(self includes: anObject)
ifTrue: [ ^ anObject ].
(self isQuotient and: [ self base includes: anObject ])
ifTrue: [ ^ self project: anObject ].
self isEmbedded
ifTrue: [ | element |
element := self ambient ! anObject.
(self contains: element)
ifTrue: [ ^ element ] ].
^ DomainError signal: 'coercion failed'
]
{ #category : #operations }
Domain >> + aDomain [
"This is a convenience method for doing coproducts. Subclasses can reimplement it to mean something different from the coproduct in order to match customary notation."
^ self ê aDomain
]
{ #category : #operations }
Domain >> , aDomain [
"Answer the cartesian set product of the receiver with the argument."
^ CartesianProduct
components:
{self.
aDomain}
]
{ #category : #operations }
Domain >> / anEquivalenceRelation [
"Answer the categorical quotient of the receiver by an equivalence relation."
| relation |
relation := anEquivalenceRelation isBlock
ifTrue: [ EquivalenceRelation on: self evaluating: anEquivalenceRelation ]
ifFalse: [ anEquivalenceRelation ].
^ QuotientSet mod: relation
]
{ #category : #comparing }
Domain >> < aDomain [
"Answer true if the receiver is a proper subobject of aDomain (strict inclusion)."
aDomain isFinite ifTrue: [^ self size < aDomain size and: [self elements allSatisfy: [:each| aDomain includes: each]]].
self isFinite ifTrue: [self do: [:each| (aDomain includes: each) ifFalse: [^ false]]. ^ true].
^ aDomain > self
]
{ #category : #comparing }
Domain >> <= aDomain [
"Answer true if the receiver is a subobject of aDomain (inclusion)."
^ self = aDomain or: [self < aDomain]
]
{ #category : #comparing }
Domain >> = aDomain [
self == aDomain ifTrue: [^ true].
self size = aDomain size ifFalse: [^ false].
self isFinite ifTrue: [^ self asSet = aDomain asSet].
self propertyAt: #elements ifPresent: [:aCollectionOrDomain| ^ aDomain = aCollectionOrDomain].
aDomain propertyAt: #elements ifPresent: [:aCollectionOrDomain| ^ self = aCollectionOrDomain].
^ super = aDomain
]
{ #category : #comparing }
Domain >> > aDomain [
"Answer true if aDomain is a proper subobject of the receiver (strict inclusion)."
^ aDomain < self
]
{ #category : #comparing }
Domain >> >= aDomain [
"Answer true if aDomain is a subobject of the receiver (inclusion)."
^ aDomain <= self
]
{ #category : #enumerating }
Domain >> allSatisfy: aBlock [
self do: [:each| (aBlock value: each) ifFalse: [^ false]].
^ true
]
{ #category : #accessing }
Domain >> ambient [
"Domains can be subobjects of other domains, for example subspeces are subobjects of vector spaces.
Answer the ambient domain of which the receiver is a subobject."
^ self propertyAt: #ambient ifAbsent: [self]
]
{ #category : #'accessing-private' }
Domain >> ambient: aDomain [
self propertyAt: #ambient put: aDomain
]
{ #category : #enumerating }
Domain >> anySatisfy: aBlock [
self do: [:each| (aBlock value: each) ifTrue: [^ true]].
^ false
]
{ #category : #morphisms }
Domain >> apply: aMorphism [
"Answer the pushforward of the receiver by aMorphism."
^ self collect: aMorphism
]
{ #category : #converting }
Domain >> asArray [
self propertyAt: #elements ifPresent: [:aCollectionOrDomain| ^ aCollectionOrDomain asArray].
^ (Iterator on: self performing: #do:) asArray
]
{ #category : #converting }
Domain >> asSet [
self propertyAt: #elements ifPresent: [:aCollectionOrDomain| ^ aCollectionOrDomain asSet].
^ Set accumulate: [:aBlock| self do: aBlock]
]
{ #category : #random }
Domain >> atRandom [
"Answer an element in the receiver choosen at random. This will fail if the receiver is not finite."
"^ Random withDefaultDo: [:aRandom| self atRandom: aRandom]" "Cuis specific"
^self atRandom: Random new
]
{ #category : #random }
Domain >> atRandom: aRandom [
"Answer an element in the receiver choosen at random. This will fail if the receiver is not finite."
self propertyAt: #elements ifPresent: [:aCollection| aCollection atRandom: aRandom].
^ self atRandom: aRandom bits: Infinity positive
]
{ #category : #random }
Domain >> atRandom: aRandom bits: bitSize [
"Answer an element of the receiver of at most n bits (as measured by #bitSize). The total number of elements that could be returned shouldn't exceed 2^n."
self propertyAt: #elements ifPresent: [:aCollection| ^ aCollection atRandom: aRandom bits: bitSize].
^ self subclassResponsibility
]
{ #category : #random }
Domain >> atRandomBits: bitSize [
"Answer an element of the receiver of at most n bits (as measured by #bitSize). The total number of elements that could be returned shouldn't exceed 2^n."
"^ Random withDefaultDo: [:aRandom| self atRandom: aRandom bits: bitSize]" "Cuis specific"
^ self atRandom: Random new bits: bitSize
]
{ #category : #morphisms }
Domain >> automorphisms [
self flag: #fix. "this shouldn't be here for all subclasses to inherit, maybe Domain should be abstract, and we could consider Collections as finite sets and implement these things in Collection; same with #to:evaluating:, etc."
^ SymmetricGroup on: self
]
{ #category : #operations }
Domain >> catProduct1: aDomain [
"Answer the categorical product of the receiver with the argument."
"The product in the category of sets is the cartesian product."
^ self, aDomain
]
{ #category : #operations }
Domain >> catProduct: aDomain [
"This is a convenience method for doing categorical products. Subclasses can reimplement it to mean something different from the categorical product in order to match customary notation."
^ 'self ◊ aDomain'
]
{ #category : #operations }
Domain >> categSum: aDomain [
"Answer the categorical sum (or coproduct) of the receiver with the argument."
"The coproduct in the category of sets is the disjoint union."
^ 'self species newFrom: ((self elements collect: [:each| {each. 0}]) ñ (aDomain elements collect: [:each| {each. 1}]))'
]
{ #category : #operations }
Domain >> catprod: aDomain [
"This is a convenience method for doing categorical products. Subclasses can reimplement it to mean something different from the categorical product in order to match customary notation."
^ 'self ◊ aDomain'
]
{ #category : #enumerating }
Domain >> collect: aBlock [
| elements |
elements := Set new.
self do: [:each| elements add: (aBlock value: each)].
^ elements as: Domain
]
{ #category : #testing }
Domain >> contains: anElement [
"Answer true if the receiver contains the given element of its ambient.
This is similar to >>includes: but less checks are needed, because the element is not an arbitrary object."
self isEmbedded ifFalse: [^ true].
^ self elements includes: anElement
]
{ #category : #testing }
Domain >> containsAllOf: aCollection [
"Answer whether all the elements of aCollection are in the receiver."
aCollection do: [:each| (self contains: each) ifFalse: [^ false]].
^ true
]
{ #category : #copying }
Domain >> copyEmpty [
^ self species new
]
{ #category : #enumerating }
Domain >> count: aBlock [
| answer |
answer := 0.
self do: [:each| (aBlock value: each) ifTrue: [answer := answer + 1]].
^ answer
]
{ #category : #enumerating }
Domain >> detect: aBlock [
"Evaluate aBlock with each of the receiver's elements as the argument.
Answer the first element for which aBlock evaluates to true."
^ self detect: aBlock ifNone: [self error: 'Element is not in the domain']
]
{ #category : #enumerating }
Domain >> detect: aBlock ifNone: exceptionBlock [
"Evaluate aBlock with each of the receiver's elements as the argument.
Answer the first element for which aBlock evaluates to true. If none
evaluate to true, then evaluate the argument, exceptionBlock."
self do: [:each | (aBlock value: each) ifTrue: [^each]].
^ exceptionBlock value
]
{ #category : #enumerating }
Domain >> do: aBlock [
(self propertyAt: #elements ifAbsent: [self subclassResponsibility]) do: aBlock
]
{ #category : #enumerating }
Domain >> do: elementBlock separatedBy: separatorBlock [
| beforeFirst |
"Evaluate the elementBlock for all elements in the receiver,
and evaluate the separatorBlock between."
beforeFirst := true.
self do: [ :element |
beforeFirst
ifTrue: [beforeFirst := false]
ifFalse: [separatorBlock value].
elementBlock value: element]
]
{ #category : #accessing }
Domain >> elements [
self propertyAt: #elements ifPresent: [:aCollectionOrDomain| ^ aCollectionOrDomain].
^ Iterator on: self performing: #do:
]
{ #category : #'accessing-private' }
Domain >> elements: aCollection [
self propertyAt: #elements put: aCollection
]
{ #category : #morphisms }
Domain >> endomorphisms [
^ self hom: self
]
{ #category : #morphisms }
Domain >> from: aDomain [
"Answer the canonical morphism from aDomain to the receiver."
self = aDomain ifTrue: [^ self id].
self >= aDomain ifTrue: [^ aDomain id restrictedTo: self].
self isQuotient
ifTrue: ['aDomain self base ifNotNil: [:aMorphism| ^ self projection î aMorphism]'].
self isCoproduct
ifTrue: [(1 to: self arity) do: [:i| 'aDomain (self at: i) ifNotNil: [:aMorphism| ^ (self embedding: i) î aMorphism]']].
^ nil
]
{ #category : #elements }
Domain >> generate: aBlock [
"Answer the result of evaluating aBlock on the generators of the receiver."
^ aBlock valueWithArguments: self generators asArray
]
{ #category : #accessing }
Domain >> generator [
self generators size = 1 ifFalse: [self error: 'more than one generator'].
^ self generators anyOne
]
{ #category : #accessing }
Domain >> generator: anElement [
self generators: {anElement}
]
{ #category : #accessing }
Domain >> generators [
"Subclases can use this or reimplement it when it makes sense."
^ self propertyAt: #generators ifAbsent: []
]
{ #category : #accessing }
Domain >> generators: aCollection [
"Subclases can use this or reimplement it when it makes sense."
self propertyAt: #generators put: aCollection asArray
]
{ #category : #properties }
Domain >> hasProperty: aSymbol [
^ (properties ifNil: [^ false]) includesKey: aSymbol
]
{ #category : #comparing }
Domain >> hash [
^ self isFinite ifTrue: [self size hash] ifFalse: [super hash]
]
{ #category : #morphisms }
Domain >> hom: aCodomain [
"Answer the morphisms that go from the receiver A to the argument B: Hom(A,B)."
^ HomSet from: self to: aCodomain
]
{ #category : #morphisms }
Domain >> id [
"Answer the identity morphism."
| answer |
self propertyAt: #id ifPresent: [:aMorphism| ^ aMorphism].
answer := self to: self evaluating: [:x| x].
answer properties
at: #isIdentity put: true;
at: #leftInverse put: answer;
at: #rightInverse put: answer;
" at: #kernel put: self null;"
at: #image put: self.
^ self propertyAt: #id put: answer
]
{ #category : #testing }
Domain >> includes: anElement [
^ self elements includes: anElement
]
{ #category : #testing }
Domain >> includesAllOf: aCollection [
"Answer whether all the elements of aCollection are in the receiver."
aCollection do: [:each| (self includes: each) ifFalse: [^ false]].
^ true
]
{ #category : #morphisms }
Domain >> inclusion [
"Answer the inclusion monomorphism from the receiver into its ambient object."
^ self ambient id restrictedTo: self
]
{ #category : #operations }
Domain >> intersection: aCollection [
"Answer the intersection of the receiver and the argument."
^ self select: [:each| aCollection includes: each]
]
{ #category : #morphisms }
Domain >> into: aCodomain evaluating: aBlock [
"Answer an embedding (an injective morphism) into the specified codomain (in the same category), evaluating as the given block."
^ self to: aCodomain evaluating: aBlock
]
{ #category : #testing }
Domain >> is: aSymbol [
^ aSymbol == #Set
or: [ aSymbol == #Domain or: [ super is: aSymbol ] ]
]
{ #category : #'testing-category' }
Domain >> isAbelianGroup [
^ false
]
{ #category : #'testing-category' }
Domain >> isAlgebra [
^ false
]
{ #category : #'testing-category' }
Domain >> isCommutativeRing [
^ self isRing and: [ self isCommutative ]
]
{ #category : #'testing-category' }
Domain >> isComplex [
^ false
]
{ #category : #testing }
Domain >> isCoproduct [
"Answer true if the receiver is some kind of categorical coproduct, such as direct sum of vector spaces or of other abelian groups. Coproducts are equipped with canonical embeddings from their components (they must implement >>embedding:)."
^ false
]
{ #category : #testing }
Domain >> isEmbedded [
"Answer true if the receiver is a subobject; for example a submodule of R^n, a subgroup of Sym(n), a subvariety of affine or projective space, etc."
^ self ambient ~~ self
]
{ #category : #testing }
Domain >> isEmpty [
^ self size = 0
]
{ #category : #'testing-category' }
Domain >> isField [
^ false
]
{ #category : #testing }
Domain >> isFinite [
^ self propertyAt: #isFinite ifAbsent: [self size isInfinite not]
]
{ #category : #'testing-category' }
Domain >> isFractionRing [
^ false
]
{ #category : #'testing-category' }
Domain >> isFreeModule [
^ self isVectorSpace
]
{ #category : #'testing-category' }
Domain >> isGroup [
^ false
]
{ #category : #testing }
Domain >> isInfinite [
^ self isFinite not
]
{ #category : #testing }
Domain >> isInitial [
"Answer true if the receiver is an initial object.
The empty set is the initial object in the category of sets. Subclasses can override."
^ self propertyAt: #isInitial ifAbsent: [self isEmpty]
]
{ #category : #'testing-category' }
Domain >> isModule [
^ self isFreeModule
]
{ #category : #'testing-category' }
Domain >> isMonoid [
^ self isGroup
]
{ #category : #testing }
Domain >> isProduct [
"Answer true if the receiver is some kind of categorical product, such as cartesian product of sets, direct product of groups, etc. Products are equipped with canonical projections to their components (they must implement >>projection:)."
^ false
]
{ #category : #testing }
Domain >> isQuotient [
"Answer true if the receiver is some kind of quotient object, such as a quotient of sets, gropus, vector spaces, etc. Quotients are equipped with a canonical embedding from their base object (they must implement >>embedding)."
^ false
]
{ #category : #'testing-category' }
Domain >> isReal [
^ false
]
{ #category : #'testing-category' }
Domain >> isRing [
^ false
]
{ #category : #'testing-category' }
Domain >> isSemigroup [
^ self isMonoid
]
{ #category : #testing }
Domain >> isTerminal [
"Answer true if the receiver is a terminal object.
In the category of sets, the singletons (sets with one element) are the terminal objects. Subclasses can override."
^ self propertyAt: #isTerminal ifAbsent: [self size = 1]
]
{ #category : #'testing-category' }
Domain >> isVectorSpace [
^ false
]
{ #category : #enumerating }
Domain >> max: aBlock [
^ self elements max: aBlock
]
{ #category : #enumerating }
Domain >> min: aBlock [
^ self elements min: aBlock
]
{ #category : #morphisms }
Domain >> morphism: aCodomain [
"Answer the canonical morphism from the receiver to aCodomain.
If the argument is a morphism, answer the composition with the canonical morphism to its domain. See also Morphism>>."
'(aCodomain isKindOf: Morphism)
ifTrue: [^ self aCodomain domain ifNotNil: [:aMorphism| aCodomain î aMorphism]]'.
(self to: aCodomain) ifNotNil: [:aMorphism| ^ aMorphism].
" self propertyAt: #canonicalMaps ifPresent: [:aDictionary| aDictionary at: aCodomain ifPresent: [:aMorphism| ^ aMorphism]]."
^ aCodomain from: self
]
{ #category : #accessing }
Domain >> name [
^ self propertyAt: #name ifAbsent: [self printString]
]
{ #category : #accessing }
Domain >> name: aString [
self propertyAt: #name put: aString
]
{ #category : #elements }
Domain >> newFrom: anObject [
"Coerce anObject to an element of the receiver. Implemented in order to be able to use >>as: consistently. Subclasses should reimplement >>!."
^ self ! anObject
]
{ #category : #enumerating }
Domain >> noneSatisfy: aBlock [
self do: [:each| (aBlock value: each) ifTrue: [^ false]].
^ true
]
{ #category : #accessing }
Domain >> null [
"Answer the null or zero object in the category of the receiver, i.e. an object that is both intial and terminal. For example {id} in the case of groups (the trivial group)."
"The null object in the category of sets is the empty set."
^ self propertyAt: #null ifAbsent: [self class newFrom: (Set new: 0)]
]
{ #category : #morphisms }
Domain >> onto: aCodomain evaluating: aBlock [
"Answer a projection (a surjective morphism) onto the specified codomain (in the same category), evaluating as the given block."
^ self to: aCodomain evaluating: aBlock
]
{ #category : #copying }
Domain >> postCopy [
super postCopy.
properties := properties copy
]
{ #category : #printing }
Domain >> printOn: aStream [
"Subclasses should reimplement defaultPrintOn: instead of this method."
self
propertyAt: #name
ifPresent: [ :aString |
aStream nextPutAll: aString italic.
^ self ].
self
propertyAt: #elements
ifPresent: [ :aCollection |
(aCollection isKindOf: Domain)
ifTrue: [ aStream print: aCollection.
^ self ].
aStream nextPut: ${. "Wildberger notation for unordered sets: elements separated by spaces"
aCollection asSet
do: [ :each | aStream print: each ]
separatedBy: [ aStream nextPutAll: ' ' ].
aStream nextPut: $}.
^ self ].
^ super printOn: aStream
]
{ #category : #enumerating }
Domain >> product: aBlock [
^ self elements product: aBlock
]
{ #category : #properties }
Domain >> properties [
^ properties ifNil: [ properties := IdentityDictionary new ]
]
{ #category : #properties }
Domain >> propertyAt: aSymbol [
^ self propertyAt: aSymbol ifAbsent: [self error: 'no such property']
]
{ #category : #properties }
Domain >> propertyAt: aSymbol ifAbsent: exceptionBlock [
^ (properties ifNil: [^ exceptionBlock value]) at: aSymbol ifAbsent: exceptionBlock
]
{ #category : #properties }
Domain >> propertyAt: aSymbol ifAbsentPut: aBlock [
^ self properties at: aSymbol ifAbsentPut: aBlock
]
{ #category : #properties }
Domain >> propertyAt: aSymbol ifPresent: aBlock [
^ (properties ifNil: [^ nil]) at: aSymbol ifPresent: aBlock
]
{ #category : #properties }
Domain >> propertyAt: aSymbol put: anObject [
^ self properties at: aSymbol put: anObject
]
{ #category : #operations }
Domain >> raisedTo: anInteger [
| components |
components := Array new: anInteger.
components atAllPut: self.
^ CartesianProduct components: components
]
{ #category : #enumerating }
Domain >> select: aBlock [
| elements |
elements := Set new.
self
do: [ :each |
(aBlock value: each)
ifTrue: [ elements add: each ] ].
^ elements as: Domain
]
{ #category : #accessing }
Domain >> size [
^ self propertyAt: #size ifAbsentPut: [ self elements size ]
]
{ #category : #private }
Domain >> species [
"Answer a class appropiate for the subobjects of the receiver."
^ self class
]
{ #category : #enumerating }
Domain >> sum: aBlock [
^ self elements sum: aBlock
]
{ #category : #morphisms }
Domain >> to: aCodomain [
"Answer the canonical morphism from the receiver to aCodomain."
self = aCodomain ifTrue: [^ self id].
self <= aCodomain ifTrue: [^ aCodomain id restrictedTo: self].
self isProduct ifTrue: [(1 to: self arity) do: [:i| '(self at: i) aCodomain ifNotNil: [:aMorphism| ^ aMorphism î (self projection: i)]']].
^ nil
]
{ #category : #morphisms }
Domain >> to: aCodomain evaluating: aBlock [
"Answer a morphism to the specified codomain (in the same category), evaluating as the given block.
Example: ZZ to: ZZ evaluating: [:x| x squared]."
^ Function from: self to: aCodomain evaluating: aBlock
]
{ #category : #morphisms }
Domain >> to: aCodomain evaluatingWithArguments: aBlock [
"When the receiver is N-ary, this method allows to define morphisms by N-ary blocks.
Example: ZZ ◊ ZZ to: ZZ evaluatingWithArguments: [:a :b| a * b]."
^ self to: aCodomain evaluating: [:each| aBlock valueWithArguments: each asArray]
]
{ #category : #enumerating }
Domain >> upTo: maximumNumberOfElements [
| answer |
answer := OrderedCollection new.
self do: [:each|
answer size >= maximumNumberOfElements ifTrue: [^ answer].
answer add: each].
^ answer
]
{ #category : #enumerating }
Domain >> upTo: max do: aBlock [
"Iterate over the elements of the receiver, just like #do:, but stop after max elements."
| k |
k := 1.
self do: [:each|
k > max ifTrue: [^ self].
aBlock value: each.
k := k+1]
]
{ #category : #morphisms }
Domain >> | aMorphism [
"See also Morphism>>|."
^ aMorphism restrictedTo: self
]
{ #category : #comparing }
Domain >> ~ aDomain [
"Answer true if the receiver is isomorphic to the argument."
^ self = aDomain "not much more we can test at this abstract level, subclasses should reimplement"
]