We denote x.p(i) the i-th application of .p to x (we put x.p(0) = x). A subset C ⊆ X is called a
A structure is said to be locally finite if its every finitely generated substructure is finite. For a (partial) monounary algebra (X, .p) it means that for every x ∈ X, the set {x, x.p(1), x.p(2), …, } is finite.
Proposition: Let (X, .p) be a connected (total) monounary algebra.
The following picture shows a pseudotree with a 4-element pseudoroot C. An arrow x → y means x.p = y.
The brown arrow indicates that after choosing an r ∈ C and a redefinition of .p to .pr byNotes:
reflexive transitive closure ofwhen referring to algebraic forests of form (2) or (3).
algebraic forestis equivalent to that of a rooted forest [].
Proposition:
A structure (X, .p, r) is an algebraic tree iff (X, .p) is a pseudotree with the pseudoroot being the singleton set {r}.
Proposition:
Note: We always aim at C being the closest approximation of D as possible so that the data structure is as little determined by transitions as possible.
| primordiality → | Primary objects |
Secondary objects
|
|
| terminality ↓ | |||
Classive objects
alias Classives |
Classes |
Eigenclasses |
|
Terminative objects
alias Terminatives |
Terminals |
||
Class has no instances.
(This is in contradiction with most of present Ruby literature.)
(O, .terminative?, .primary?)
where
O is a set of (all) objects,
.terminative? and .primary? are boolean attributes of objects
indicating whether an object is terminative resp. primary.
(O, .ec, .pr, .sc, r, c)
where
O is a set of (all) objects.
.ec is a total function between objects,
x.ec is called the eigenclass of x.
.pr is a total function between objects,
x.pr is called the primary object of x.
.sc is a partial function between objects,
x.sc (if defined) is called the superclass of x.
r is an object, called the inheritance root.
c is an object, called the instance root
or metaclass root
(by (S1~7), c is a shorthand for r.ec.sc).
x is terminative if
x.pr is not r and x.pr.sc is not defined.
.terminal?, .class? and .eigenclass?,
respectively).
x.sc(i) (resp. x.ec(i))
denotes the ith application of .sc (resp. .ec)
to x
(we allow 0th application
whenever the 1st application is defined).
.sc
(with the reflexive closure only applied to non-terminals)
is denoted ℍ
and called the (.sc-) inheritance.
.ec
is denoted ℙ and called the primorder.
(O, .ec, .pr) is a
primorder algebra.
.ce and .eci.
ℍ
is an algebraic tree on non-terminals, its root is r.
More specifically, for every object x,
x.sc is defined iff x is
neither the inheritance root r nor a terminal,
x.sc is defined, then
x.sc(n) equals r for some, necessarily unique, n.
.sc and .ec are commutative in the following sense:
if(x.scis defined, thenx.ec.scis defined and is equal tox.sc.ec.
.sc-links per .ec-chains are parallel.)
.sc preserves primary objects on non-terminals, i.e.
x.sc is a class for every non-root class x.
.ec.sc preserves primary objects on terminals, i.e.
x.ec.sc is primary for every terminal x.
.sc maps to classives, i.e.
for every object x,
if x.sc is defined then x.sc.pr is a class.
c equals
r.ec.sc and is different from r
(this prevents the degenerate case of r being the only class).
r.ec is the only object x satisfying
x.sc == c.
Proposition:
c is a class.
r is a class.
x, x.sc is a class.
x, y are classes such that
x.sc(i) == y for some i > 0
then
we say that x is a subclass-of y.
By a previously mentioned proposition,
the reflexive closure of the subclass-of relation is an algebraic tree on classes.
x,
the eigenclass index of x,
denoted x.eci,
is the unique i such that x == x.pr.ec(i).
Proposition:
The superclass operator .sc decrements the eigenclass index on
(all) eigenclasses of terminals and on (all) eigenclasses of the inheritance root
r.
On other objects, the index is preserved.
I.e. for every object y for which y.sc is defined,
y.sc.eci == y.eci - 1
if y.pr equals either r or some terminal,
y.sc.eci == y.eci otherwise.
.sc-links are skewin case (a) and
uprightin case (b).
x we denote by x.hancs
the list of inheritance ancestors of x.
More specifically,
.hancs is a partial function from the set
O of objects to the set of lists of objects defined by
x.hancs is defined iff x is non-terminal,
x.hancs[i] == y iff x.sc(i) == y.
x by
x.hancestors,
i.e. x.hancestors equals x.hancs without eigenclasses.
c,
i.e.
a metaclass is a non-terminal object x such that x.hancs
contains c.
A metaclass is said to be
Note: The explicit/implicit terminology is redundant. We could use the primary/secondary terminology established in the 2x2 nomenclature.
Observations:
c is the only explicit metaclass.
r.ec.
.ec_sc_pr the restriction of .ec.sc.pr
to the set O.pr ∖ {r} of all primary objects except the
inheritance root.
Obviously, .ec_sc_pr is an algebraic tree.
For every primary x,
x.ec_sc_pr == x.sc if x is a class,
x.ec_sc_pr == x.class == x.ec.sc if x is a terminal (*).
.ec_sc_pr the primary inheritance.
Note:
(*)
The .class map is defined later.
.ec.sc.pr to just the set O.pr of
all primary objects we obtain the Ruby's front pseudotree.
Its pseudoroot is the set of helix classes introduced in the next section.
The primary inheritance can be then called the front tree.
The picture from the
Pseudotree introductory section
provides a visualization of these 2 structures.
(O₀₁, .sc, .ec)
of an S1 structure such that
O₀₁ is the set of objects with eigenclass index equal
to 0 or 1,
.sc is the restriction of the superclass partial map to
O₀₁,
.ec is the restriction of the eigenclass map to primary objects
–
so that .ec is only partial in S1₀₁
(and is a bijection between primary objects and first eigenclasses).
Proposition: Up to isomorphism, an S1 structure is uniquely determined by its S1₀₁ substructure.
S
is generated from an empty set using S's constants and functions.
Applied to an S1 structure,
the least substructure is generated from the inheritance root r
using .sc, .ec and .pr.
We call the minimum S1 substructure Ruby helix. The name comes from the fact that the substructure resembles a helical threading of a right-infinite screw. The inheritance corresponds to the helix curve, the primorder corresponds to imaginary lines parallel to the screw axis. This is stated more precisely as follows.
Proposition:
c.hancs and
their .ec chains.
In particular, Ruby helix contains no terminals.
.sc is injective.
n be the number of helix classes,
i.e. n = c.hancs.length.
Then x.ce == x.sc(n) for every helix eigenclass x.
Class < Module < Object < BasicObject.
In particular,
BasicObject is the inheritance root r.
The following table shows basic notation and terminology for helix classes.
| Document notation | Description | Ruby name |
r |
the inheritance root | BasicObject |
¤ |
the conventional inheritance-root | Object |
m |
the metamodule root | Module |
c |
the instance root / metaclass root | Class |
.sc function is indicated by
arrows ↖ and
↑,
the .ec function by the
→ arrows.
Objects A, B and b
are assumed to be created by the code
class A; end; class B < A; end; b = B.new
(cf.[])
| Primary objects (alias classes and terminals) |
Secondary objects (alias eigenclasses) |
|||||||||
| Eigenclass index → | 0 | 1 | 2 | … | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| (Duplicately presented row) | •Class | → | • | → | • | → | … | |||
| 4-row cylinder skew seam → | ↖ | ↖ | ↖ | |||||||
| Classives | •BasicObject | → | • | → | • | → | … | |||
| ↑ | ↑ | ↑ | ||||||||
| •Object | → | • | → | • | → | … | ||||
| ↑ ↑ ↑ ↑ ↑ ↑ |
↑ | ↑ ↑ ↑ ↑ ↑ ↑ |
↑ | ↑ ↑ ↑ ↑ ↑ ↑ |
↑ | |||||
| •Module | → | • | → | • | → | … | ||||
| ↑ | ↑ | ↑ | ||||||||
| •Class | → | • | → | • | → | … | ||||
| •A | → | • | → | • | → | … | ||||
| ↑ | ↑ | ↑ | ||||||||
| •B | → | • | → | • | → | … | ||||
| ↖ | ↖ | ↖ | ||||||||
| Terminatives | •b | → | • | → | • | → | … | |||
.class function alias
direct-instance-of relation
.class function maps objects to classes.
For every object x,
x.class == x.ec.sc if x is terminal,
x.class == r.ec.sc (== c) otherwise.
Proposition:
For every object x,
x.class equals the entry-object
of the class subtree when traversed from the eigenclass of x,
i.e.
x.class == x.ec.hancestors[0].
For objects x, y,
if x.class == y then we say
that y is the class of x
and that x is a direct instance of y.
The reflexive transitive closure of .class
is an algebraic tree with a 2-3 level structure
shown in the following table
(we use the name Class for the instance root c).
| level depth | level members | |
| 0 (top level) | Class |
|
| 1 | Classes except Class |
Eigenclasses |
| 2 | Terminals | |
Class has no (direct) terminal instances.
.class ◦ ℍ.
Equivalently,
x is an instance-of y
iff
x.class.sc(i) == y for some i.
Proposition:
x is an instance-of y
iff
x.ec.hancestors contains y.
S0, S1, …,
of abstract states or just states.
The sequence itself is called an (abstract) run.
S0 is the initial state.
For each state Si, we denote Si.S1
its corresponding S1 structure.
In addition, we use the following notational conventions:
By saying that S → S' is a state transition we
mean S and S' are abstract states
with S appearing before S' in the run.
We use state indices or apostrophes to distinguish between structures for
particular states,
e.g. Oi denotes
the set of objects of Si.S1.
We drop underscores whenever no confusion is likely to arise,
so that Oi means Oi.
The following rules apply:
Si, Sj,
the restriction of Si.S1 and Sj.S1
to their common object set
Oi ∩ Oj is equal.
More specifically,
for every object x from
Oi ∩ Oj,
x is terminative in Si
iff
x is terminative in Sj.
ri equals rj.
x.sci, x.eci and
x.pri
are equal to
x.scj, x.ecj and
x.prj, respectively.
Si, Si+1,
either
Oi ⊆ Oi+1 or
Oi ⊇ Oi+1.
O0 ⊆ Oi for every
i, i.e.
the initial objects are never removed.
x
and their .ec chains to the S1 structure.
The following must be specified, explicitly or implicitly, for each such x:
x.ec.sc.pr
(i.e. the parent in the primary inheritance .ec_sc_pr
– necessarily a class).
x
- i.e. whether x should be (a) a terminal or (b) a class.
X, different from Class,
and then creating a new object x such that
x.ec.sc.pr == X either by (a) instantiation of X
or (b) subclassing from X, as in the following code:
x = X.new.
This creates a new terminal x.
x = Class.new(X).
This creates a new class x.
y is instantiable
if it is allowed to have instances, i.e.
in some reachable state,
x
such that x is instance-of y.
y
is final
if
y has no subclasses, and
Note:
The Class class is final (due (S1~8)).
y
is quasifinal
if
y does not have instantiable subclasses, and
Proposition:
Class,
quasifinal classes are those that cannot have indirect instances.
Note:
The Symbol class is quasifinal (due (S3~4)).
(m, .incs) where
m is a class, called the root metamodule,
.incs is a partial function from the set
O of objects to the set of (finite) lists of objects.
.incs are called (own) inclusion lists.
Descendant classes of m different from Class
are called metamodules.
Terminals that are instances of m are modules.
An S2 structure has the following (additional) axioms:
m equals
the already presented helix class Module.
x.incs is defined iff x is a module or a non-terminal.
.incs maps includers to lists of includers.
Note that this terminology admits includers with no own includees.
Proposition:
An object is an includer iff it is an instance of Module.
x, y,
if y occurs in x.incs
then y is called an own includee of x
and x is called an own includer of y.
We also say that y is directly included in x.
The reflexive closure, restricted to includers,
of the own-includer-of relation is denoted by
Μ.
x
is a pure instance if it is an end userof the type system:
x is neither a class nor an eigenclass nor a module.
≤ between includers
as (ℍ ◦ Μ) ∪ Μ.
Recall that
ℍ is the .sc-inheritance, and
Μ is the reflexive closure of the own-includer-of relation.
x ≤ y iff x and y are includers
such that at least one of the following is satisfied:
x == y,
x.sc(i) == y for some i,
x.incs[i] == y for some i,
x.sc(i).incs[j] == y for some i, j.
< for the strict version of ≤
(and symbols ≥ and > for inverses of
≤ and <, respectively).
Proposition:
.sc-inheritance.
≤ symbol,
HM descendancy is not a partial order in general.
Neither transitivity nor acyclicity (antisymmetry of the transitive closure)
is guaranteed.
(See Inclusion anomalies for examples.)
x, y such that x ≤ y,
x not being an eigenclass implies
y not being an eigenclass,
x being a module implies
y being a module.
< to modules, i.e.
x is an includer of y
iff x < y and y is a module.
y is an includee of x
or that
y is included in x.
Proposition: The includer-of relation is an extension of the own-includer-of relation.
.ec ◦ ≤.
Equivalently,
x is kind of y
iff x.ec ≤ y.
Proposition:
If X is an includer
then Xs means the set of objects that
are kind of X.
This convention is usually applied to named classes or modules.
Notes:
kindin the phrase
(l) is kind of (r)construes with the right side of the phrase, so that we cannot express
Xs as kindsof
X.
X is a class then Xs means instances of X.
BasicObject
Object
Module
Class
Module subtable provides the following nomenclature of includers:
Classes
⊂
Modules
⊃
Modules
Classes − Eigenclasses
Non-terminals
Includers
Modules − Classes
Module.
(Classes ⊂ Modules.)
Class.
(Modules ∩ Classes == ∅.)
Note: Another diagram of the nomenclature is provided as a side view of an example of the S2 structure. This diagram is a refinement of the 2x2 nomenclature table.
Objects
Object are considered
blank slate objects.
BasicObjects
== Objects ⊎ (blank slate objects).
Note: (∗) Each of the 5 division lines partitions the set of objects into two complementary subsets, specified as <set> / <complemetary set>. The structure can be created as follows.
class S < String; end; class A; end; class B < A; end; class X < Module; end module R; end; module M; end; N = X.new s = S.new; i = 20; j = 30; k = 2**70; b = B.new class BasicObject; include ::R end class B; include M end class << B; include M end class X; include M end class << X; include M end module N; include M, Comparable end class << s; include N endThe code first builds classes (
S, A, B, X),
then modules (R, M, N),
then non-includers
(pure instances
s, i, j, k, b).
Finally, inclusion lists are created.
The diagram shows that inclusion lists refine the sc-inheritance.
This refined structure is refered to as
MRO in the next section.
Ancestor lists in the structure
can be reported (without eigenclasses) by the following code
().
class Object; def ec; singleton_class rescue self.class end end
%w{s i j k b }.each { |x| puts "%s.ec: %s" % [x, eval(x).ec.ancestors] }
%w{B.ec X.ec N X}.each { |x| puts "%s: %s" % [x, eval(x).ancestors] }
Notes about [埋込みオブジェクト:../doc/_svg/s2-front/legend-iclass-r.svg] :
R into BasicObject.
R (more precisely, the pair (BasicObject, R))
is the root in the example's MRO structure.
BasicObject are untypical.
Our inclusion has been made to show that
BasicObject is not necessarily the MRO root.
R can be superceded by a new root after e.g.
module R; include (R = Module.new) end class BasicObject; include ::R end p Object.ancestors #--> [Object, Kernel, BasicObject, R, R::R]
Μ.
The domain Μ
consists of the following two disjoint sets of elements:
(x,x) with x being an includer
(includer elements).
(x,y) with y being a member of x.incs
(iclass elements,
y is necessarily a module).
inclusion class.
.super
.super
defined on the MRO domain Μ as follows:
(x,x),
(x,x).super == (x.incs[0], x) if x.incs is nonempty, else
(x,x).super == (x.sc, x.sc) if x.sc is defined, else
(x,x).super is undefined.
(x,y) with x different from y,
(x,y).super == (x, x.incs[i+1]) if
y equals x.incs[i] for some
i < x.incs.length-1, else
(x,y).super == (x.sc, x.sc) if x.sc is defined, else
(x,y).super is undefined.
(x,y).super(i) the ith application of
.super to (x,y).
(Μ, ≤)
is defined by
(x,y) ≤ (a,b) iff
x < a and a is not a module
(equivalently,
x.sc(i) equals a for some i > 0 ),
or
x == a and y appears before b in
x.incs, or
x == a == y.
Proposition:
(x,y) ≤ (a,b) iff
(x,y).super(i) equals (a,b) for some i,
i.e.
(Μ, ≤)
is a reflexive transitive closure of
(Μ, .super).
(r,r),
called the MRO tree.
Its root equals either (r,r) or
(r, r.incs.last).
.ancs the function from the set
of includers to the set of lists of includers defined by
x.ancs[i] == y iff (x,x).super(i) == (w,y) for some
w.
x.ancs[i] is defined then it is said to be the ith
(MRO) ancestor of x.
The Ruby .ancestors built-in method filters out eigenclasses:
For each class or module x,
x.ancestors equals x.ancs without eigenclasses.
Proposition:
Let x, y be includers.
x ≤ y iff x.ancs contains y.
x.ancs == [x] + x.incs + x.sc.ancs
if x.sc is defined,
x.ancs == [x] + x.incs otherwise
(i.e. if x is a module or equals r).
(x,y) is in Μ
iff
y is in x.ancs
and only modules appear before the first occurrence of
y in x.ancs.
(x,y) is in ℍ
iff
y is in x.ancs
and is not a module.
x, the inheritance ancestor list x.hancs
is obtained from x.ancs by removing modules.
x is not an eigenclass then x.ancs does not
contain an eigenclass (i.e. x.ancs == x.ancestors).
(.sc, .incs) replaced by (.ancs, .module?)
where .module?
is a boolean attribute indicating whether an object is a module.
(Μ, ≤)
reflects the fact that the relation is used in Ruby's method resolution.
However, only the MRO tree is used in this respect.
On the other hand, qualified constant resolution uses all components
of (Μ, ≤) so that
the term qualified constant resolution order / QCROwould be more appropriate. The reason we have chosen MRO is because this term has already been established in the Python programming language [] [].
The following rules apply:
S → S' are substructural in the S2 structure.
m is fixed.
x,
x.incs is a (possibly non-contiguous) sublist of x.incs'[i].
Proposition:
x ≤ y in S then
x ≤ y in S'.
S
are not necessarily restrictions of their counterparts in S'.
In particular, for an includer x,
(x,x).super can differ between S and S'.
(Note that it is in contrast to the superclass (partial) function .sc.)
S → S'
with two parameters:
p.
q.
acceptediff the following holds:
q is a module
– otherwise an error wrong argument type is raised.
p does not occur in [q] + q.incs
(== q.ancs)
– otherwise an error cyclic include detected is raised.
S' equals S (possibly) except for the
inclusion list p.incs' which is defined as follows.
Denote A = p.incs & ([q] + q.incs)
so that A = [a1, …, an] is the (possibly empty) list of
common elements of p.incs and [q] + q.incs, ordered
according to p.incs.
Then the p.incs' list is defined as
the concatenation of lists l0, …, ln where
l0 = (s0 - p.ancs) + t0
where s0 and t0
are maximum prefixes (initial sublists)
of [q] + q.incs and p.incs, respectively,
that are disjoint from A.
li = [ai] + (si - p.ancs) + ti,
i = 1, …, n,
where [ai] + si and [ai] + ti
are maximum slices (interval sublists)
of [q] + q.incs and p.incs, respectively,
that are disjoint from A - [ai].
si,
without any modules that appear along the p.ancs
ancestor chain
(note that this can only happen if
p is a class or eigenclass,
for modules p the subtractionis already made by excluding elements from
A)
are
prepended before the start for i == 0 and
inserted after ai for 1 ≤ i ≤ n.
In most cases, A is empty, so that
p.incs' == [q] + (q.incs - p.ancs) + p.incs,
q's inclusion list,
without the modules already appearing in p.ancestors,
is prepended to p's inclusion list.
Subsequently, the single element list [q] is prepended.
T0, T1, T2, S0, S1, S2, A1, A2 = Array.new(8).map { Module.new }
module P; include T0, A1, T1, A2, T2 end
module Q; include S0, A2, S2, A1, S1 end
module P; include Q end
p P.included_modules #--> [Q, S0, T0, A1, S1, T1, A2, S2, T2]
include method of Module,
e.g.
class X; include M end.
The extend method of Kernel
provides a shorthand for inclusion into eigenclasses:
x.extend(M) is roughly equivalent to
class << x; include M end.
Each of include and extend might be considered
outer
methods of the
form
<outer-method> ==
<inner-method> + <hook-method>
hooks:
| eigenclass shift → | 0 |
1 |
|
| outer/inner ↓ | |||
| Outer method | include |
extend |
|
| Inner method | append_features |
extend_object |
|
| Hook | included |
extended |
|
module M; end
class << M
def append_features(x); puts "inner: #{self} as includee of #{x}"; super end
def included(x); puts "hook: #{self} as includee of #{x}" end
def extend_object(x); puts "inner: #{self} as extender of #{x}"; super end
def extended(x); puts "hook: #{self} as extender of #{x}" end
end
class X; end
x = X.new
class << x; include M end # inner: M as includee
# hook: M as includee
x.extend(M) # inner: M as extender
# hook: M as extender
Notes:
coincideswith the inclusion list order [].
sc-inheritance ancestor lists).
Examples:
module A; end; module B; end; module C; end
module A; include B end module B; include C end
module B; include C end module A; include B end
p A.included_modules
#=> [B]
#=> [B,C]
(A,B,C) is created.
(The red-framed case of the previous example.)
module A; end; module B; end; module C; end module A; include B end module B; include C end puts "%p, %p, %p" % [A < B, B < C, A < C] #=> true, true, nil
module L; end
module M; include L end
module A; end
module L; include A end # M does not know about this
module A; include M end # inclusion cycle A < M < L < A created
puts "#{A.include? L}, #{L.include? A}" #=> true, true
puts "#{A < L}, #{L < A}" #=> true, true
module M; end class X; end class Y < X; include M end class X; include M end p Y.ancestors # [Y, M, X, M, Object, Kernel, BasicObject]
(O, Φ, ΦA,
FALSE, TRUE, NULL, UNDEF, ℤ, ℱ, ℬ, .φvalue)
where
O is the already introduced set of objects.
Φ is a value domain,
disjoint from the set O of objects.
ΦA is a subset of Φ,
elements of ΦA are called atomic.
FALSE and TRUE are distinct elements of ΦA,
having the semantics of boolean values.
NULL and UNDEF are distinct elements of ΦA,
indicating undefinedness.
ℤ is a subset of ΦA
representing the set of integer numbers.
ℱ is a subset of ΦA
(disjoint from ℤ)
representing the set of floating point numbers.
ℬ is a subset of ΦA.
It is a set of bytestrings
which are finite sequences of bytes.
A byte can be considered a copy of an integer within the 8-bit range 0, …, 255.
Bytes corresponding to the 7-bit range of 0, …, 127 are called ascii.
(ℬ, +, '') where
+ is bytestring concatenation,
'' is the empty bytestring.
.φvalue is a partial function from O
to Φ assigning objects their
semantic value.
x for which x.φvalue is defined
are called φvalued.
Note:
Gray color indicates that merging V1 and S2 structures yields
a single meaning for the O symbol.
The following condition is required:
ΦA × ΦA × ⋯ × ΦA
are subsets of Φ.
Note:
φvalued:
Symbols and Strings are
φvalued by pairs from ℬ × ℬ
(via estrings).
Rationals are
φvalued by pairs from ℤ × ℤ.
(FalseClass, TrueClass, NilClass, false, true, nil) where
Object.
(Fixnum, Integer, Numeric) where
Fixnum, Integer and Numeric
are classes such that
Fixnum < Integer < Numeric < Object
is a direct inheritance chain.
Fixnum are caled fixnums.
(Symbol, Υ) where
Symbol is a class, a direct descendant of Object.
Υ denotes the set of Symbol
instances (Symbols).
Υ are called symbols.
false, true, and nil
together with fixnums and symbols are called immediate values.
The structure is subject to the following axioms:
| (S3~1) |
Immediate values are φvalued according to the following table:
| ||||||||||||
| (S3~2) |
The restriction of
.φvalue to the set of all immediate values is injective.
| ||||||||||||
| (S3~3) |
The terminals false, true, and nil
are the only instances of their respective classes.
| ||||||||||||
| (S3~4) | Classes of immediate values are quasifinal. |
Notes:
φvalues.
.φvalue is preserved on immediate values.
(.actual?) where
.actual? is a boolean attribute of objects.
.actual? set to true are actual(s),
otherwise are non-actual(s).
We also provide a set-alternative for .actual?
by denoting Oa the set of all actual objects.
The structure is subject to the following axioms:
.ce preserves actuals.
For every eigenclass x,
if x is actual then x.ce is actual.
.sc preserves actuals.
For every eigenclass x,
if x is actual then x.sc is actual.
r.ec(i) is actual then so is r.class.ec(i).
r.class.ec,
the Class's eigenclass, is actual.
Oa ⊆ O₀₁, i.e. if
only primary objects and (some) first eigenclasses are actual
(equivalently, eigenclasses of eigenclasses are not actual),
Oa equals the set
(primary objects) ⊎ ((first) eigenclasses of classes).
Note that this a special case of conventional actuality.
Oa
== (primary objects) ⊎ ((first) helix eigenclasses).
x, we denote x.actuals
the list corresponding to the finite .ec-subchain of actual objects
starting at x.
Note that under conventional actuality, x.actuals.length ≤ 2
for every primary object x.
Proposition:
r.class.actuals.last
is the least actual helix object in the inheritance.
r.actuals.length == 2).
.aclass function maps objects to actual non-terminals
(classes or actual eigenclasses).
For every object x,
it is defined recursively by
x.aclass == x.ec if x.ec is actual, else
x.aclass == x.ec.sc (== x.class)
if x is terminal, else
x.aclass == x.sc.aclass.
x.aclass the actualclass of x.
Proposition:
x,
x.aclass equals the first actual member of x.ec.hancs,
x.class ==)
x.ec.hancestors[0] == x.aclass.hancestors[0].
.aclass map forms
an algebraic tree – the actualclass tree.
r.class.actuals.last.
Class.ec.
r.actuals.length + 1
(3 under conventional actuality).
Example:
Assume conventional actuality and let
A be a direct subclass of Object and
a a direct instance of A.
Then there are four possible .aclass
chains from a to Class.ec
according to whether a.ec and A.ec are actual or not.
a
→
A
→
Object.ec
→
Class.ec
a
→
A
→
A.ec
→
Class.ec
a
→
a.ec
→
A.ec
→
Class.ec
a
→
a.ec
→
Object.ec
→
Class.ec
.ec ≤ .aclass ≤ .class refinement
.aclass, can be considered a refinement
of the class map, .class.
Further observation shows that .aclass
can be considered a coarsement of the eigenclass map, .ec,
so that we have the following refinement chain:
.ec ≤ .aclass ≤ .class.
This can be stated precisely as follows.
(Recall that the HM descendancy ≤ restricted to non-terminals
equals the inheritance ℍ which is an algebraic tree.)
Proposition:
x,
x.ec ≤ x.aclass ≤ x.class.
.ec, .aclass and .class
form a chain when ordered pointwise by the HM descendancy.)
x,
x.ec
is the first member of x.ec.hancs,
x.aclass
is the first member of x.ec.hancs
that is actual,
x.class
is the first member of x.ec.hancs
that is a class.
Note:
Gray color indicates that statements are valid for both
.hancs and .ancs.
.f
of the .ec, .aclass or .class maps
is monotone with respect to the inheritance ℍ, i.e.
for every non-terminals x, y,
x ≤ y implies
x.f ≤ y.f.
x and every i > 0,
x.ec(i) ≤ x.aclass(i) ≤ x.class(i).
.aclass map is related
to MRI 1.9 implementation,
we introduce
a function .saec,
meaning semiactual eigenclass,
partially defined for primary objects as follows:
x.saec is defined iff x is
x.actuals.length > 2.
x.saec is defined then it equals x.actuals.last.ec.
.saec are called semiactual(s).
Thus, semiactuals are (right) covers of actual lists
except for 1-2 sized actual lists starting with terminals
– such lists are uncovered.
We also introduce a 3-valued attribute of .actuality
which is defined on all objects according to the table below.
| Object set | x.actuality |
|
| non-actuals | 0 |
|
| semiactuals | 1 |
|
| actuals | 2 |
|
x,
if x is actual or semiactual then
x.sc.ec is actual or semiactual.
.klass map
.klass map provides a
virtual connectionto object's eigenclass. We consider two versions:
.klass is a partial map between actual objects.
For every actual object x,
x.klass == x.ec if x.ec is actual, else
x.klass == x.ec.sc (== x.class)
if x is terminal, else
x.klass is undefined.
.klass
is a map between actual or semiactual objects
defined according to the MRI 1.9 implementation.
For every actual or semiactual object x,
x.klass == x.ec if x.ec is actual or semiactual, else
x.klass == x.ec.sc (== x.class)
if x is terminal, else
x.klass == x.sc.ec if x.pr is terminal
and x is actual or semiactual, else
x.klass == c.ec(x.eci)
(if x.pr is a class and x semiactual
– the value is probably unimportant).
Proposition:
.klass map
is a restriction of both
the .aclass and the full version of .klass.
.klass is a generator of .aclass
in the following sense: For every actual x,
x.aclass == x.klass
if x.ec is actual or x terminal,
x.aclass == x.sc.aclass otherwise.
S → S'
are incremental in the S4 structure in the following sense.
For every object x from
O ∩ O',
x is actual in S then it is so in S'
S → S' with a single parameter
x - an actual object whose eigenclass x.ec
is requested to be made actual.
S' equals S (possibly) except for the
set of actual objects.
The difference between sets of actual objects, denoted x.acdelta,
is defined as follows
(all functions are taken in S).
x.acdelta == [] if x.ec is actual, else
x.acdelta == [x.ec] if x is terminal, else
x.acdelta == [x.ec] + x.sc.acdelta if x.pr is a non-helix
class, else
x.acdelta == r.class.hancs.map{|c| c.ec(x.eci+1)}
if x.pr is a helix class, else
x.acdelta == [x.ec] + x.sc.ec.acdelta if
(x.pr is terminal and) x.eci != 1, else
x.acdelta == [x.ec] + x.sc.acdelta + x.sc.ec.acdelta
(if x is the eigenclass of a terminal).
Note:
The recursive definition in (5) and (6) requires that
x.acdelta is defined also for non-actuals.
x.acdelta == [x.ec] + x.sc.acdelta (if x.pr is terminal)
– the same prescription as in (3) applies.
Proposition:
In case (B), x.acdelta, as a set,
equals the union of the following (possibly empty) sets Δ1 and Δ2
(all functions are taken in S):
x.ec.hancs - x.aclass.hancs.
r.class.actuals.last.ec.hancs - y.hancs
if y is the .sc-least helix object of Δ1, or
x.aclass
and then equalize helix actual lists.
The following examples of Ruby code show several ways of x's eigenclass
actualization.
x.ec.
class << x; end
x.singleton_class
x.ec's inclusion list (or attempting extension).
x.extend(Module.new)
x.extend(Kernel) (including an already included module)
x.ec's own method
(aka singleton method of x)
def x.dummy; end
x.instance_eval { def dummy; end }
x.define_singleton_method("", lambda{})
x.module_function(:m)
(if x is a module having own method m)
Notes:
can't define singleton is raised).
Numerics
that are not Fixnums
(an error can't define singleton method is raised).
T_CLASS counter
ObjectSpace.count_objects[:T_CLASS]
counts the following objects in total:
module Kernel
def nnt_delta # number of non-terminals delta
@nnt ||= 0
nnt = ObjectSpace.count_objects[:T_CLASS]
delta = nnt - @nnt
@nnt = nnt
delta
end
def nnt_delta_report; puts "nnt_delta: #{nnt_delta}" end
end
nnt_delta_report # nnt_delta: 387 class X; end nnt_delta_report # nnt_delta: 2
Proposition:
Assuming (S4X~1), for every actual x which is not an
immediate value, x.acdelta.length equals
nnt_delta - 1 if x.eci == 1,
x.pr is terminal and x.pr.actuals.length == 2,
nnt_delta otherwise.
(.cparent, .cname) where
.cparent is a partial function between includers,
.cname is a partial function from includers to symbols.
.cparent is called
includer containment or just containment
and denoted Ϲ.
x.cparent, if defined, is called
the containment parent of x.
x.cname, if defined, is called
the (containment) name of x.
x.cparent(i) denotes the i-th application
of .cparent to x.
Ϲ is an algebraic forest
(with .cparent undefined on roots).
x such that x.cname
is defined, namely the Object class.
x.cparent is defined then
x.cname is defined.
(Non-roots are named.)
x, x.cname is undefined.
(Eigenclasses are anonymous, therefore roots.)
constant nameis defined.)
x.cparent is Object then
x.cname differs from Object.cname.
Proposition: Components of the containment forest are trees of the following 3 types:
maintree rooted at
Object.
Note:
offshoots.
offshootsare chains corresponding to module-in-module inclusion lists.
offshoots, if any, are usually of minor importance.
x.cancs
the list of containment ancestors of x,
i.e.
x.cancs is defined iff x is an includer,
x.cancs[i] == y iff x.cparent(i) == y.
x.croot
the containment root of x,
x.croot == x.cancs.last,
x.cpathname the (proper) containment path-name of x
defined by
x.cpathname == (x.cancs - [x.croot]).reverse.map{|y| y.cname}.join("::"),
::".
Note that for every containment root x
(in particular, Object),
x.cpathname is an empty string.
S → S'
preserve x.cparent
and x.cname
whenever x.cparent is defined.
S → S'
with three parameters:
p which is an actual includer.
n.
q which is an anonymous class or module
to be named by n.
requestsfor containment binding.
p being a named class or module.
p being an anonymous class or module.
p being an eigenclass.
| Group | Container p |
Name n |
Ruby code | Containee q (after transition) |
||
q.croot |
q.cpathname |
|||||
| A | Object |
"Q" |
class Q; end |
Object |
Q |
|
X |
class X; class Q; end end |
Object |
X::Q |
|||
X |
X::Q = Class.new |
Object |
X::Q |
|||
| C1 | X.ec |
class << X; class Q; end end |
X.ec |
Q |
||
q = Class.new |
||||||
| C2 | X.ec |
X.singleton_class::Q = q |
unchanged |
|||
| B | x |
x::Q = q |
||||
x |
x.class_eval { self::Q = q } |
|||||
| Notation / Expression | Terminology / Description | Domain | Generating map | Relation characteristics |
ℍ
== (Classes, ≤) |
inheritance | non-terminals | .sc |
algebraic tree |
ℙ |
primorder | all objects | .ec |
component-wise isomorphic to the linear order of natural numbers |
ℍ ↾ (classes) |
self-or-subclass-of | classes | .sc ↾ (classes) |
finite algebraic tree |
Μ |
self-or-own-includer-of | includers | — | reflexive and antisymmetric |
≤
== (ℍ ◦ Μ) ∪ Μ |
HM descendancy | includers | — | reflexive |
(Μ, ≤) |
MRO | Μ |
.super |
algebraic forest |
| instance tree | all objects | .class (aka direct-instance-of) |
algebraic tree of depth 2 | |
.class ◦ ℍ |
instance-of | all objects | — | complete (any-to-any) on helix classes, irreflexive and antisymmetric otherwise |
.ec ◦ ≤ |
kind-of | all objects | — | |
| actualclass tree | all objects | .aclass |
algebraic tree (of depth 3 under conventional actuality) | |
Ϲ |
includer containment | includers | .cparent |
algebraic forest |
(.frozen?, .tainted?, .trusted?) where
.frozen?, .tainted? and .trusted?
are boolean attributes of objects.
x is frozen then x.ec is frozen.
x is tainted then x.pr is tainted.
(.ec-chains are tainted as a whole.)
x is trusted then x.pr is trusted.
(.ec-chains are trusted as a whole.)
S → S'
preserve being frozen, i.e. if x is frozen in S
then it is so in S'.
.φvalue of frozen objects.
freeze and taint/untaint
methods, respectively.
(nestlists, selfs) where
nestlists is a non-empty finite list of (possibly empty) finite lists
of actual includers,
selfs is a non-empty finite list of actual objects.
nestlists.last.reverse is denoted nesting
and called the current nesting.
selfs.last is denoted self
and called the current object.
selfs[0] is denoted main
and called the main context.
nesting are named.
main is a direct instance of Object.
nesting[0] exists and is a class or module,
then
nesting[0].cancs equals nesting.
pseudorulebecause it only reflects a common pattern of a nested class/module definition, e.g.
class A class B class C # current nesting corresponds to A::B::C end end endWriting
class ::C instead of class C would break
the pseudorule.
main
remains unchanged across transitions.
nesting gets changed:
explicit and implicit.
An explicit change is performed via class/module/eigenclass (re)definition.
In this case,
nesting changes correspond to
prepending/removing one object to/from the front of nesting,
i.e. they are equivalent to
nesting.unshift(y) and nesting.shift,
respectively.
The unshift operation is includer opening
and can have one of the following forms:
class X for a class X,
module M for a module M,
class << x for an eigenclass x.ec.
end.
nesting change is obtained via method invocation
as demonstrated by the following code:
class X class Y; def self.nest1; Module.nesting end end # def-nesting is [X::Y, X] def Y .nest2; Module.nesting end # def-nesting is [X] end class A p Module.nesting # [A] p X::Y.nest1 # [X::Y, X] p X::Y.nest2 # [X] endThe example shows that each method has its own nesting which becomes the current nesting after method invocation. The method's nesting equals the current nesting at the time of method definition.
(Ω, ω(), OID, .oid, IVN, .invals) where
Ω is a finite set of object data-representatives,
which we also call ω-objects.
Ω is disjoint from O.
ω() is a partial injective map
from O × O onto Ω.
OID is an object-identifier domain, a subset of integers.
.oid is an injective map from Ω to
OID.
IVN is a finite set of internal value names,
a subset of the value domain Φ.
.invals is a function from non-includers to
the set
IVN ↷ Φ
of partial maps from internal value names to the value domain Φ.
ω(a,b) is defined iff the following are satisfied:
a and b are actual.
a is an includer then (a,b) belongs to the MRO domain.
a is a non-includer (pure instance) then it equals b.
ω(a,b)
with a different from b
iclasses (inclusion-classes).
Condition (S8~1) says that
Ω is a copy of
all actual MRO domain pairs extended by all pairs (x,x) with
x being a pure instance (non-includer).
Equivalently,
Ω
is a copy of the set of all actual objects extended by the set of iclasses.
This is illustrated by the following diagram.
Extended actual O |
Data representatives | Extended actual Μ |
||
iclasses |
actual MRO pairs | |||
| actual objects | actual includer representatives |
|||
non-includer representatives |
||||
Ω:
ω(x,y).module ≝ ω(x,x)
whenever ω(x,y) is an iclass.
ω(x,y).super ≝ ω((x,y).super)
whenever (x,y).super (the MRO parent of (x,y)) is defined.
ω(x,x).func ≝ ω(x.func, x.func)
whenever .func is a (partial) function on O
and x.func is defined.
ω(x,x).func ≝ x.func
whenever .func is a (partial) function from O
to Φ
and x.func is defined.
ω-objects are identifiable with
OIDs
we can regard functions defined on Ω as data fields.
The following table presents a distinguished data-field subset.
| Field Name |
Domain (Type) |
Applicability | Description | Relevant field(s) in Ruby implementation |
||
| non-includer | includer | iclass | ||||
oid |
OID | ● | ● | ● | ω-object id |
|
super |
OID | ● | ● | MRO parent | super |
|
ce |
OID | ● | eigenclass predecessor | __attached__ |
||
klass |
OID | ● | ● | actualclass generator | klass |
|
module |
OID | ● | iclass originator | |||
cparent |
OID | ● | containment parent | __classid__ and/or__classpath__ |
||
cname |
string |
● | containment name | |||
frozen? |
boolean | ● | ● | frozenness | part of flags |
|
tainted? |
boolean | ● | ● | taintedness | ||
trusted? |
boolean | ● | ● | trust | ||
type |
ENUM_T | ● | ● | ● | basic type | |
invals |
IVN ↷ Φ |
● | internal value(s) | |||
Notes:
oid
indicates a primary key.
cparent in Ruby's implementation.
stringindicate that the domain of of
cnames should be more precisely described
as φvalues of Symbols.
The type field indicates the basic type with the following enumeration
domain
(we assume that ENUM_T is a subset of Φ):
| Value | Meaning of ω(x,y) |
T_ICLASS |
iclass |
T_CLASS |
class or eigenclass |
T_MODULE |
module |
T_NONINC |
non-includer (pure instance) |
ωobjects data table
ωobjects data table, with one-to-one correspondence between
rows and object data-representatives.
oid |
super |
ce |
klass |
module |
cparent |
cname |
frozen? |
tainted? |
trusted? |
type |
invals |
||
| … | |||||||||||||
Proposition:
The ωobjects data table
uniquely determines the S6 structure,
up to isomorphism and except for the .φvalue function.
| Prefix | Notation | Terminology | Description |
| class | A primary non-terminal object. | ||
| e | eigenclass | A secondary object. | |
| metaclass | A (non-strict) inheritance descendant of the Class class. |
||
| explicit metaclass | A metaclass that is a class.
In Ruby, the Class class is the only explicit
metaclass.
The set of explicit metaclasses equals the set of classes of classes.
|
||
| implicit metaclass |
A metaclasss that is an eigenclass.
The set of implicit metaclasses equals the set of eigenclasses of Classes.
|
||
e |
.ec |
the eigenclass map | A map from objects to eigenclasses. |
s |
.sc |
the superclass map | A partial map between non-terminal objects. |
.class |
the class map | A map from objects to classes.
The second application, .class(2), is constant. |
|
| direct-subclass-of | A relation which equals the restriction of .sc to classes. |
||
| subclass-of | The transitive closure of direct-subclass-of. | ||
| direct-superclass-of, superclass-of | Inverses of direct-subclass-of and subclass-of, respectively. Not used in this document to avoid terminological conflicts with the superclass map. | ||
| direct-instance-of | Equivalent to .class
(i.e. x direct-instance-of y
iff x.class == y). |
||
| instance-of | Composition of direct-instance-of and self-or-subclass-of. | ||
| class-of | Inverse of direct-instance-of. | ||
Class |
(A) the Class class |
The instance tree root, and, simultaneously, the metaclass tree root.
Denoted c, equal to r.class. |
|
(B) a Class |
A class or an eigenclass.
An instance of the Class class.
An object that is kind-of Class.
|
||
Classes |
Class instances |
Classes and eigenclasses. | |
| helix classes | Classes that belong to the Ruby helix.
Members of
c.hancs, i.e.
Class, Module, Object, and BasicObject.
|
||
a |
.aclass |
the actualclass map | A map from objects to actual eigenclasses or classes.
The n-th application, .aclass(n), is constant,
where n equals r.actuals.length + 1
(n == 3 under conventional actuality).
|
| i | iclass | An inclusion class, abstraction of an includer-includee pair. |
|
.klass |
the actualclass generator |
|
|
| singleton class | Equivalent to eigenclass.
Not used in this document to avoid a conflict with the term class.
Note:
The word |
(Encoding, ℇ, ℇ¢, ℇ$, .name, e8b, e7b,
⅀, ⅀⋄, ≘) where
Encoding is a class, a direct descendant of Object.
ℇ denotes the set of Encodings, called just encodings.
ℇ¢ is a subset of ℇ,
containing encodings with proper character handling support.
ℇ$ is a subset of ℇ¢,
containing ascii-compatible encodings.
.name is an injective function from Encodings to
ascii bytestrings.
(Even .name.upcase is injective.)
e8b is a distinguished ascii-compatible encoding,
called binary and named 'ASCII-8BIT'.
e7b is a distinguished ascii-compatible encoding,
called ascii and named 'US-ASCII'.
⅀ denotes the set ℬ × ℇ
– the set of pairs (s,e) where
s is a bytestring and
e an encoding.
Elemens of ⅀ are called estrings,
meaning encoded strings.
⅀⋄ is a subset of ⅀, containing
valid estrings.
≘ is an equivalence relation on the set ⅀⋄
of valid estrings.
{e8b, e7b} ⊆ ℇ$ ⊆ ℇ¢ ⊆ ℇ.
The structure is subject to the following axioms:
('',e) is a valid estring for every encoding e.
(s,e8b) is a valid estring for every bytestring s.
(s,e7b) is a valid estring iff
s is an ascii bytestring.
(s,e7b) ≘ (s,e8b) whenever
(s,e7b) is a valid estring.
(s,e) ≘ (t,e) implies s == t,
i.e. valid ≘-equivalent estrings with the same encoding are equal.
e and
every valid estring (s,e7b),
(s,e7b) ≘ (t,e)
(t,e).
e from ℇ¢ and every
bytestrings s, t such that (s,e) is valid,
(s + t, e) is valid iff (t,e) is valid.
a, b are leading characters
∗
of x, y, then
x ≘ y implies a ≘ b.
(∗)
For a valid estring x = (s,e) with non-empty bytestring s
and encoding e from ℇ¢,
we denote x.chr the leading character of x
defined as the unique valid atomic prefix of x, i.e. it is a
valid estring (u,e) such that
u is non-empty,
u + v = s for some bytestring v,
u is the smallest bytestring satisfying the previous condition.
| Set membership | Ruby boolean-attribute reflection | |
| Expression | Terminology / description | |
x ∈ ℇ¢ |
encoding x supports proper character handling |
!x.dummy? |
x ∈ ℇ$ |
encoding x is ascii-compatible |
x.ascii_compatible? |
x ∈ ⅀⋄ |
estring x is valid |
x.valid_encoding? |
estring x is
ascii-only
|
x.ascii_only? |
|
⅀¢ the set of all valid estrings with encoding from
ℇ¢. We call such estrings char-decomposable.
⅀e
the set of all valid estrings with encoding e.
.encode() map
.encode() function maps valid estrings to their
≘-equivalents in given encodings,
i.e. it is a partial function from ⅀⋄ × ℇ to
⅀⋄ such that
(s,e).encode(f) == (t,f) iff
there is a bytestring t such that (s,e) ≘ (t,f).
Proposition:
(s,e) and every encoding f,
(s,e).encode(f).encode(e) == (s,e)
(s,e).encode(f) is defined.
x are called
x.encode(e7b) is defined,
e7b encoding,
equivalenty x == x.encode(e7b).
x and an ascii bytestring s, we might write
x == s for x == (s,e7b).
(This is later applied, for instance, for x == 'method_missing'.)
∔ is a partial binary operator
on the set ⅀¢ of char-decomposable estrings defined by
(s,e) ∔ (t,f) == (s + t, e)
if e == f,
(s,e) ∔ (t,f) is undefined, otherwise.
.chars
is a function from ⅀¢
to finite lists over ⅀¢ defined recursively by
(s,e).chars == [] if s is empty, else
(s,e).chars == [(u,e)] + (v,e).chars where
u, v are the unique bytestrings such that u + v == s
and u is the smallest non-empty such that (u,e) is valid
((u,e) is the leading character of (s,e)).
s from ⅀¢,
s.length is the length of s.chars,
s[i] denotes the i-th member of s.chars.
Proposition:
(⅀e, ∔, ('',e)) is
a free monoid for every encoding e from ℇ¢.
s == s[0] ∔ s[1] ∔ ⋯ ∔ s[n-1]
for every char-decomposable estring s of length n.
e, f from ℇ¢,
.encode(f) is an isomorphism between
(⅀e, ∔, ('',e)) and
(⅀f, ∔, ('',f)).
.encode(f) and .chars commute,
.encode(f) preserves .length.
+ on valid estrings
satisfying the following:
x + y == x ∔ y whenever x ∔ y is defined,
('',e) + x ≘ x ≘ x + ('',e),
(s,e) + x ≘ (s,e) ∔ x.encode(e)
whenever x is ascii-only, similarly
x + (s,e) ≘ x.encode(e) ∔ (s,e)
whenever x is ascii-only.
x can startwith an ascii estring even if
x is not ascii.
S → S',
all of the following are fixed for encodings and estrings
existing both in S and S':
the ℇ¢- and ℇ$- set memberships,
validity of estrings, the ≘-equivalence.
names are preserved across transitions.
(String, .estr) where
String is a class, a direct descendant of Object.
.estr is a function from
Strings and Symbols to the set ⅀
of estrings.
Strings and Symbols are φvalued by
pairs of bytestrings as follows:
x.φvalue == (s, e.name)
(s,e) == x.estr.
x,
the estring x.estr is valid.
x,
if x.estr ≘ (s,e7b) for some s then
x.estr == (s,e7b).
Notes:
Strings.
Proposition: The following are consequences of the already introduced conditions:
.estr is preserved on symbols.
.estr is preserved on frozen strings.
(Array, ._list) where
Array is a class, a direct descendant of Object.
._list is a function
from the set of all Arrays to finite lists of objects.
Arrays are called arrays.
Notes:
0, …, n-1 for some natural n.
a be an array and i an integer. Then
a.length denotes the cardinality of (the domain of) a._list.
a[i] is defined as follows:
a[i] == a._list(i)
if 0 ≤ i < a.length,
a[i] == a._list(a.length - i)
if -a.length ≤ i < 0,
a[i] == nil
otherwise.
S → S'
preserve ._list on frozen arrays,
i.e. if a is a frozen array,
then a[i] equals a[i]'
and a.length equals a.length'.
(Hash, .hcodes, .keys, .values,
.compare_by_identity?, .dflt, .dflt_call?, Proc)
where
Hash and Proc are classes,
direct descendants of Object.
Hashes are called hashes.
The remaining members are functions on the set of all hashes.
For every hash x,
x.hcodes is a finite list of integers called hash codes.
x.keys is a finite list of objects called keys.
x.values is a finite list of objects called values.
x.compare_by_identity? is a boolean attribute.
x.dflt is an object determining the default value.
x.dflt_call? is a boolean attribute determining
whether x.dflt is interpreted indirectly
– as the default's value evaluator,
or if it is interpreted directly as the default value itself.
x is subject to the following conditions:
x.hcodes.zip(x.keys)
(x.hcodes paired with x.keys) is non-repetitive.
x.compare_by_identity? is true then
even the list x.keys is non-repetitive.
x.dflt_call? is true then
x.dflt is an instance of Proc.
s is a direct instance of String
contained in x.keys
and x.compare_by_identity? is false
then
s is frozen.
x,
the triple (x.hcodes, x.keys, x.values)
has the following equivalent forms:
single-listform.
x.compare_by_identity? is true then even keys are unique.
slotform.
slots) to lists of triples of the form (idx, key, value) such that
slot-lists but within the whole hash,
slot-lists respect the idx order (so that ordering in individual
slotsis
induced),
slot-lists and if
x.compare_by_identity? is true then even within the whole hash.
Example:
The following diagrams show a hash x in the two above described forms.
Note that due to multiple occurrence of the 'a' key,
x.compare_by_identity? is necessarily false.
|
Ⅰ
Ⅱ
Ⅲ
Ⅳ
3 c 1 a 0 m 5 b
8 u 2 r 4 a 6 q
9 a 7 d 11 g
10 b
|
S → S'
preserve the 6 member maps
.hcodes, …, .dflt_call? on frozen hashes.
.compare_by_identity? are preserved.
(Once being set to true, this boolean attribute cannot be set to false.)
(.hash, .eql?(), .call()) where
.hash is a function from objects to integers
(x.hash is called the hash code of x).
.eql?() is a boolean-valued function on
O × O encoding equality between objects.
.call() is an object-valued function on
Procs × Hashes × O.
x and an object k we say
that i is a
resolution key-index of k in x
if either of the following is satisfied:
x.compare_by_identity? is false and
i is the smallest index such that
k.hash == x.hcodes[i] and
either k == x.keys[i] or k.eql?(x.keys[i]),
x.compare_by_identity? is true and
i is the unique index such that
k == x.keys[i].
[]
is an object-valued function on
Hashes × O
assigning each hash x and each object k
a value x[k] as follows:
x[k] == x.values[i] if
i is the resolution key-index of k in x,
else, if there is no such i,
x[k] == x.dflt if x.dflt_call? is false, else
x[k] == x.dflt.call(x,k).
(Bignum, Float, Rational, Complex, .real, .imag) where
Float, Rational and
Complex are classes, direct descendants of Numeric.
Bignum
is a direct descendant of Integer.
Bignums are called bignums.
.real and .imag
are functions from Complexes to Numerics
that are not Complexes.
Note:
The Fixnum < Integer < Numeric class chain has
already been introduced, see
Immediate values.
The structure is subject to the following axioms:
Integers are either Fixnums or Bignums.
Integers are φvalued by the integers ℤ.
Bignums and Fixnums have disjoint .φvalue images.
Floats are φvalued by the floating point numbers ℱ.
Rationals are φvalued
by pairs (n,d) from ℤ × ℤ such that
d is positive, and
n and d are relatively prime.
.real and .imag maps
are preserved on all Complexes.
(Range, .start, .end, .exclude_end?) where
Range is a direct descendant of Object.
Ranges are called ranges.
.start and .end
are functions from Ranges to objects.
.exclude_end? is a boolean attribute of Ranges.
.start, .end and .exclude_end? maps
are preserved on all Ranges.
invals data (sub)fields in the ωobjects
data table as follows.
| Applied to | Field Name |
Domain (Type) |
Description |
false |
value |
{FALSE} |
|
true |
value |
{TRUE} |
|
nil |
value |
{NULL} |
|
Encodings |
name |
ℬ |
encoding name |
dummy? |
boolean | indicates statefulencoding without proper character handling |
|
ascii_compatible? |
boolean | indicates ascii-compatible encoding | |
Symbols and Strings |
bytes |
ℬ |
byte-sequence together with encoding |
encoding |
ENUM |
||
valid_encoding? |
boolean | indicates whether bytes is a valid sequence w.r.t
encoding |
|
Arrays |
length |
ℤ |
array length |
Hashes |
length |
ℤ |
hash length |
compare_by_identity? |
boolean | indicates hash resolution mode | |
dflt |
OID | the hash's default object or evaluator | |
dflt_call? |
boolean | indicates dflt interpretation mode |
|
Fixnums |
value |
ℤ |
the φvalue of a fixnum |
Bignums |
value |
ℤ |
the φvalue of a bignum |
Floats |
value |
ℱ |
the φvalue of a float |
Rationals |
numerator |
ℤ |
the first component of the φvalue |
denominator |
ℤ |
the second component of the φvalue, needs to be positive |
|
Complexes |
real |
OID | the real-part object |
imag |
OID | the imaginary-part object | |
Ranges |
start |
OID | range start |
end |
OID | range end | |
exclude_end? |
boolean | range end exclusion indicator |
Notes:
invals data.
(.idcat) where
.idcat is a partial function on estrings.
.idcat are called
(lexical) identifiers,
x.idcat is x's identifier category.
| (A0~1) |
The .idcat partial function categorizes estrings according
to the following table:
| |||||||||||||||||
| (A0~2) | Identifiers are subject to additional restrictions which are not specified in this document. |
.idcat.
We will also use the word name
for non-method identifiers
and apply the categorization directly to symbols,
so that e.g. for a symbol s,
s.estr.idcat == 'constant-identifier'
means
.
s is a constant name
(Π, .met(), μ) where
Π is a set of anonymous methods,
disjoint from the set O of objects,
π is a distinguished element of Π,
called the/a whiteout method, or simply whiteout
(the term adopted from []),
.met() is
is a partial function from O × Υ to
Π, called own method map,
μ is a symbol
such that μ.estr == "method_missing".
.met() can be viewed as a subset of
O × Υ × Π.
If x.met(s) is defined then we say that
x has own method s
or that
x is a method-owner of s.
If, in addition,
x.met(s) is a whiteout,
then we say that
x has own wo-method s
or that
x is a wo-method-owner of s,
x.met(s) is not a whiteout,
then we say that
x has own nwo-method s
or that
x is an nwo-method-owner of s.
An A1 structure has the following axioms:
If in initial state, an A1 structure satisfies the following:
r
is an nwo-method owner of μ
(the method_missing method).
Notes:
s
to become a method name.
In particular,
if x.met(s) is defined then it is not required that
s.estr.idcat == 'method-identifier', see notes to
Transitions .
x.ec of Numerics x
cannot have own methods.
.mowner()
from O × Υ × {true, false}
to O,
called method owner map,
by
x.mowner(s, wo) == y
iff
x is an includer,
y equals the least-indexed member
of x.ancs that is a method owner of s,
wo is false then
y is also an nwo-method owner of s.
false to be the default value of wo,
so that x.mowner(s) means x.mowner(s, false).
Propositions:
x.mowner(s) == x iff
x is an nwo-method-owner of s.
x.mowner(s,wo).mowner(s,wo) == x.mowner(s,wo)
whenever x.mowner(s,wo) is defined.
x is a method-inheritor of s
if x.mowner(s, true) is defined,
i.e.
if some of x.ancs has own method s.
We say that x is an nwo-method-inheritor of s
if x.mowner(s) is defined.
By an inherited method map we mean
the partial function .met_h()
from O × Υ × {true, false}
to Π
defined by
x.met_h(s,wo) == x.mowner(s,wo).met(s)
x.mowner(s,wo) is defined.
Again, the default value for wo is false.
smr()
from O × Υ
to O × Υ defined as follows:
smr(x,s) == (x.ec.mowner(s), s) if
x.ec.mowner(s) is defined, else
smr(x,s) == (x.ec.mowner(μ), μ) if
x.ec.mowner(μ) is defined, else
smr(x,s) is undefined.
Notes:
x.ec.mowner(s) expresses the Ruby
method call mantra[]:
One to the right, then up.This is applied in both
search phases, (1) and (2).
μ,
so that (3) never occurs.
.ec and .aclass
Proposition:
For every object x and every symbol s,
x.ec.mowner(s) == x.aclass.mowner(s)
(either both sides are undefined or both defined and equal).
x
can actually start from the actualclass of x.
S → S'
preserve .met() on frozen objects,
i.e. if x is a frozen includer,
then x.met(s) equals x.met'(s).
.map()
can be arbitrarily (re)defined on non-frozen objects.
Modifications of .map() are accomplished via
transitions S → S' of types (A)–(E) according to
the following table.
| Transition parameters | Requested output condition | Ruby's correspondent(s) | |
| (A) New method definition | |||
| (1) |
|
x.met'(s) == m |
|
| (2) |
|
x.ec.met'(s) == m |
|
| (B) Aliasing an inherited method | |||
|
x.met'(a) == x.met_h(s) |
|
|
(C) Whiteoutingan inherited method |
|||
|
x.met'(s) == π |
|
|
| (D) Removing an own non-whiteout method | |||
|
x.met'(s) is undefined |
|
|
| (E) Changing method visibility – see Method visibility transitions | |||
Notes:
x is not specified in the rightmost column,
then it is assumed that x equals self unless
self == main – in this case x == Object.
s.estr.idcat == 'method-identifier' applies
to the method name s (or to the alias a).
In (b) cases, no such restriction applies, as shown in the following example.
class X
define_method ("") { self }
define_method ("1 + 1") { 3 }
alias_method "@a", ""
end
p X.new.send("").send("@a").send("1 + 1") #-> 3
p X.instance_methods(false) #-> [:"", :"1 + 1", :@a]
class X
undef_method "1 + 1"
remove_method "", "@a"
end
p X.instance_methods (false) #-> []
(.pty(), κ) where
.pty()
is a partial function from O × Υ to
O, called property map,
κ is a symbol
such that κ.estr == "const_missing".
.pty() can be viewed as a subset of
O × Υ × O.
We call elements of the set Υ × O properties.
We say that an object x has own property (s,y)
if x.pty(s) == y.
We might just say that x has own property s.
We categorize properties according to .idcat:
Symbol s |
Property (s,y) category |
s.estr starts with an uppercase letter |
constant |
s.estr starts with "@"
but not with "@@" |
instance variable |
s.estr starts with "@@" |
class variable |
s.estr starts with "$" |
global variable |
s.estr starts with a lowercase letter or with _ |
local variable |
Notes:
Ruby object model.
An A2 structure has the following axioms:
Kernel module can have global variables.
Note:
Global variable ownership is a rather artificial concept.
We have chosen the Kernel as the owner, because it owns
the global_variables method for global variable enumeration.
If in initial state, an A2 structure satisfies the following:
m is an nwo-method owner of
κ (the const_missing method).
weak formof the following condition applies:
x, y,
and every class variable symbol s
such that both x.pty(s) and y.pty(s) are defined,
each of the following conditions in its own right is sufficient
to imply x.pty(s) == y.pty(s):
x ≤ y or y ≤ x.
x.pr == y.pr.
weak formlooks like is not specified in this document. Neither are specified transition rules or resolution rules which seem to be even more complicated.
Note: Class variables are considered a controversial Ruby feature.
.cowner()
from O × Υ to O
defined by
x.cowner(s) == y iff
x is an includer,
y equals the least-indexed member
of x.ancs that has own constant s.
Propositions:
x.cowner(s) == x iff
x is a constant-owner of s.
x.cowner(s).cowner(s) == x.cowner(s)
whenever x.cowner(s) is defined.
x is a constant-inheritor of s
if x.cowner(s) is defined,
i.e.
if some of x.ancs has own constant s.
By an inherited constant map we mean
the partial function .cst_h()
from O × Υ to O
defined by
x.cst_h(s) == x.cowner(s).pty(s)
x is a constant-inheritor of s.
.powner(), the property owner map,
and .pty_h(), the inherited property map,
as extensions of
.cowner() and .cst_h(), respectively.
.powner() is a partial map
from O × Υ to O
defined by
x.powner(s) == x.cowner(s)
if s is a constant name
and x.cowner(s) is defined,
x.powner(s) == x
if s is an instance variable name
and x.pty(s) is defined,
value not specified) if
s is a class variable name
and (condition not specified),
x.powner(s) == Kernel
if s is a global variable name
and Kernel.pty(s) is defined,
x.powner(s) is undefined otherwise.
.pty_h() is a partial map
from O × Υ to O
defined by
x.pty_h(s) == x.powner(s).pty(s)
whenever x.powner(s) is defined.
Notes:
.pty_h() is only related to inheritance in cases (1) and (3).
.pty_h() factors through
.powner() even for class variables.
qcr()
from O × Υ
to O × Υ defined as follows:
qcr(x,s) is undefined if x is not an includer
or s is not a constant name, else
qcr(x,s) == (x.cowner(s), s) if
x.cowner(s) is defined, else
qcr(x,s) == (x.ec.mowner(κ), κ)
if x.ec.mowner(κ) is defined, else
qcr(x,s) is undefined.
qcr(x,s) == (y,t) then we say that (y,t) is
a qualified constant resolution of (x,s).
Notes:
qcr(x,s) == (y,t) is defined in phase (1), then
y.pty(t) equals the evaluation x::<s.estr>
(for example, if s.estr == "A", then
y.pty(t) equals x::A).
qcr(x,s) == (y,κ) is defined in (2), then
the method y.met(κ)
gets called with x as the receiver and s as the
argument.
κ, so that phase (3) never occurs.
Object class are considered
toplevel constantsnot designed for referencing by descendants. If
qcr(x,s) == (Object,s)
and x != Object then (in non-nil $VERBOSE mode)
a warning is issued.
s
the expression ::<s.estr>
is equivalent to x::<s.estr> with
x being equal to Object.
The qcr map can be expressed as a method of Module
as follows:
class String
def constant_name?; !!match(/^[A-Z]\w*$/) end
end
class Module
def cowner(s)
ancs.find{|x| x.const_defined?(s, false)}
end
def qcr(s)
!s.to_s.constant_name? ? nil :
(o = cowner(s)) ? [o, s.to_sym] :
respond_to?(t = :const_missing, true) ? [method(t).owner, t] : nil
end
end
uqcr()
from Υ
to O × Υ.
The start point x is obtained by
x == nesting[0] if the nesting cursor nesting is nonempty, else
x == main.ec otherwise, i.e. if the current nesting is empty.
q is defined by
q == nesting + x.ancs + Object.ancs if x is a module,
q == nesting + x.ancs otherwise, i.e. if the start point is a class or
an eigenclass.
uqcr map is then defined as follows:
uqcr(s) is undefined if s is not a constant name, else
uqcr(s) == (w, s) if
w is the least-indexed member of q
such that w.pty(s) is defined, else, if no such w exists,
uqcr(s) == (x.ec.mowner(κ), κ)
if x.ec.mowner(κ) is defined, else
uqcr(s) is undefined.
uqcr(s) == (y,t) then we say that (y,t) is
an unqualified constant resolution of s.
Notes:
uqcr(s) corresponds to the evaluation of <s.estr>
– i.e. there is no double-colon (::) before <s.estr>.
uniq operation (removing duplicate members) to be applied
to the lookup sequence q.
nesting == [r] then constants
owned by the Object class are not visible via
the unqualified constant resolution
– they must be referenced using the :: prefix.
Example (using another class that is not a descendant of Object):
class A < BasicObject def self.const_missing(s); :missing end p [Module, ::Module] # [:missing, Module] end
The uqcr map can be expressed as a method
of Array
as follows:
$main = self
class Array
def uqcr(s)
return nil if !s.to_s.constant_name?
x = first || $main.ec
q = self + x.ancs + (::Class === x ? [] : ::Object.ancs)
o = q.find{|y| y.const_defined?(s, false)}
o ? [o, s.to_sym] :
x.respond_to?(t = :const_missing, true) ? [x.method(t).owner, t] : nil
end
end
Note:
The method must be called with the current nesting as the receiver,
i.e. Module.nesting.uqcr(s).
state, in particular, they can
Statefulnessof
Fixnums is demonstrated in the following code.
class Fixnum
def false(i = 1)
@n_falsified ||= 0
@n_falsified += i
self
end
def report
"#{self} falsified #{@n_falsified || 0} times"
end
end
puts 5.report # 5 falsified 0 times
puts 6.report # 6 falsified 0 times
puts 5.false.report # 5 falsified 1 times
puts 5.false.report # 5 falsified 2 times
puts 6.false.report # 6 falsified 1 times
S → S'
preserve .pty(s) on frozen objects,
except for global variables s,
i.e. if x is a frozen object
and s is a symbol that is not a global variable name,
then x.pty(s) equals x.pty'(s).
.pty()
can be arbitrarily (re)defined on non-frozen objects.
Modifications of .pty() are accomplished via
transitions S → S' according to the following table:
| Transition parameters | Requested output condition | Ruby's correspondent(s) |
| (A) Property assignment | ||
|
x.pty'(s) == y |
|
| (B) Property removal | ||
|
x.pty'(s) is undefined |
|
Notes:
x is not specified in the rightmost column, then
x equals the artificial owner Kernel
if s is a global variable name, else
x equals Object if self == main, else
x equals self.
.cparent function is not directly supported,
and, in general, is not even obtainable.
The built-in Module's method x.name
provides a global containment pathof
x in
one of the following forms ∗:
A::B::C, if x.croot equals Object,
#<Class:0xaafc40>::A::B::C, otherwise
(i.e. if x.croot is an eigenclass).
x.cparent means resolving the path
without the last segment.
Such a resolution might not yield consistent results,
as discussed in the following subsections.
Note:
(∗)
x.name may be even undefined, as shown in the following section.
x.cancs.map{ |a| a.cname.estr }
are (loosely) concatenable
for every includer x.
X = Object.const_set("X\u00e1".encode('utf-8'), Class.new)
class X
Y = const_set("Y\u00e1".encode('iso-8859-2'), Class.new)
end
p X::Y.name # raises Encoding::CompatibilityError
.cname is injective on each sibling set, i.e.
x, y with a containment parent,
x.cparent == y.cparent and
x.cname == y.cname implies
x == y.
class X
class Y; end
Z = Y
class_eval { remove_const :Y }
class Y; end
end
x = X
y = X::Y
z = X::Z
puts "#{y.object_id}-->#{y}"
puts "#{z.object_id}-->#{z}"
The code produces three different classes,
x, y and z,
such that
y.cparent == z.cparent == x, and
y.cname == z.cname == "Y".
Module's method
remove_const which would prevent, at least to some extent,
sibling inconsistencies.
class Module
alias toS to_s
alias __remove_const remove_const; private :__remove_const
def remove_const(sym)
c = const_defined?(sym, false) ? const_get(sym,false) : nil
if c.kind_of?(Module) && c.toS.match(Regexp.new("((\\:\\:)|(^))#{sym}$"))
raise NameError.new(
"Cannot remove module/class :#{sym} from #{toS}", sym)
end
__remove_const(sym)
end
end
x.cparent.pty(x.cname) == x
for every includer x with a containment parent.
remove_const modification,
(b) seems to be preventable only by convention.
(When in $VERBOSE mode,
Ruby issues a warning about an already initialized constant.)
x ≤ y and y == b.cparent
implies
x.cst_h(b.cname) ≤ b.
class X def a; @a end def initialize; @a = self.class::A.new end class A def report; self.to_s end end end class Y < X def initialize; super end class A < A; end end puts X.new.a.report #--> #<X::A:0xab80d0> puts Y.new.a.report #--> #<Y::A:0xab8028>
(.mvisibility()) where
.mvisibility() is
a partial function from O × Υ to
the set
{:private, :protected, :public}.
The following axioms are required to hold:
x.mvisibility(s) is defined iff
x.met(s) is defined and is not a whiteout.
x.mvisibility(s) == v we say
that the own visibility of s in x is v.
We define inherited visibility .mvisibility_h()
by
x.mvisibility_h(x) == x.mowner(s).mvisibility(s)
x.mowner(s) is defined.
uqmr()
from Υ to O × Υ
defined as the partial application smr(self, _),
i.e.
uqmr(s) == smr(self, s) whenever the right side is defined.
uqmr(s) == (y,t) then we say that (y,t)
is an unqualified method resolution of s.
The uqmr map can be expressed as a method of
Object as follows:
class Object def uqmr(s) t = :method_missing respond_to?(s, true) ? [method(s).owner, s] : respond_to?(t, true) ? [method(t).owner, t] : nil end end
qmr()
from
O × Υ to
O × Υ
defined as follows.
qmr(x,s) == (x.ec.mowner(s), s) if
x.ec.mvisibility_h(s) is :public, or
x.ec.mvisibility_h(s) is :protected
and self.ec ≤ x.ec.mowner(s), else
qmr(x,s) == (x.ec.mowner(μ), μ) if
x.ec.mowner(μ) is defined, else
qmr(x,s) is undefined.
qmr(x,s) == (y,t) then we say that (y,t)
is a qualified method resolution of (x,s).
Notes:
x.ec.mvisibility_h(s) is :private or
x.ec.mvisibility_h(s) is undefined, i.e.
x.ec.mowner(s) is undefined.
x.ec.mvisibility_h(μ) is irrelevant.
The qmr map can be expressed as a method of Object
as follows:
class Object def qmr(x,s) if x.respond_to?(s, false) # public or protected o = x.method(s).owner if o.protected_instance_methods(false).include?(s) && !kind_of?(o) o = nil end if o then return [o, s] end end t = :method_missing x.respond_to?(t, true) ? [x.method(t).owner, t] : nil end end
.met() and .pty() partial maps,
.mvisibility() is not guaranteed to be preserved on frozen objects:
class X; def m; end end p X.private_instance_methods(false) # [] X.freeze class X; private :m end p X.private_instance_methods(false) # [:m]Modifications of
.mvisibility() are accomplished
according to the following table.
| Transition parameters | Requested output condition | Ruby correspondents (visibility specifiers) |
|
| Method visibility setting | |||
| (1) |
|
x.mvisibility'(s) == v |
|
| (2) |
|
x.ec.mvisibility'(s) == v |
|
Notes:
x is not specified in the rightmost column,
then x equals self unless
self == main – in this case x == Object.
v is implied by the method (visibility specifier) used.
private_class_method or public_class_method
visibility specifiers:
class X; end class << X private; def m; end end ec = (x = X.new).singleton_class p ec.respond_to?(:m) # false ec.public_class_method(:m) p ec.respond_to?(:m) # true p ec.method(:m).owner # X.ec (#<Class:X>) p ec.singleton_methods(false) # [:m] p ec.singleton_class.public_instance_methods(false) # [] p ec.singleton_methods(false) # []The visibility of
:m in x.ec.ec
has been changed from private to public
but the owner of :m remains X.ec
(according to ec.method(:m).owner).
In addition, the last three line show inconsistency in :m's ownership.
(Α,
ϙ(), ϻ(), ι(), ϰ(),
α(), β(), αɦ(), βɦ())
where
Α is a set of
arrow data-representatives or just arrows,
disjoint from hitherto introduced sets,
ϙ(), …, βɦ() are injective maps
to arrows with mutually disjoint ranges denoted
Αϙ, …, Αβɦ.
ϙ() is a partial map
from O × ℬ to Α,
ϙ(x,s) is defined according to the table in
Internal property arrows.
ϻ() is a partial map
from O × ℤ to Α,
ϻ(x,i) is defined iff x.incs[i] is defined.
ι() is a partial map
from Arrays × ℤ to Α,
ι(x,i) is defined iff x._list[i] is defined.
ϰ() is a partial map
from Hashes × ℤ to Α,
ϰ(x,i) is defined iff x.keys[i] is defined.
α() is a partial map
from O × Υ to Α,
α(x,s) is defined iff x.met(s) is defined.
β() is a partial map
from O × Υ to Α,
β(x,s) is defined iff x.pty(s) is defined.
αɦ() is a partial map
from O × Υ to Α,
αɦ(x,s) is defined iff
x is actual and x.ec.mowner(s) is defined.
βɦ() is a partial map
from O × Υ to Α,
βɦ(x,s) is defined iff
x is actual and x.powner(s) is defined.
The following table provides a summary of arrow nomenclature together
with applicability of arrow constituents.
Symbols |
arrow constituents → | Source | Name (Key) | Owner | Attributes | Target | |||
Terminology for the set Αs |
source |
idx |
hcode |
key / name |
owner |
mvisibility |
target |
||
ϙ |
internal-property-arrows | internal arrows |
● | ● | ● | ||||
ϻ |
inclusion-list-arrows | ● | ● | ● | |||||
ι |
array-arrows | (external) own-arrows |
● | ● | ● | ||||
ϰ |
hash-arrows | ● | ● | ● | ● | ● | |||
α |
own-method-arrows | ● | ● | ● | ● | ||||
β |
own-property-arrows | ● | ● | ● | |||||
αɦ |
preview-method-arrows | preview-arrows | ● | ● | ● | ● | ● | ||
βɦ |
preview-property-arrows | ● | ● | ● | ● | ||||
ω-objects for our arrow formalization.
Instead, we use the set O of objects directly, so that the
ωobjects table can be considered split again into
(A) objects and (B) a table of module inclusion lists.
This yields
| Terminology | sourceDomain |
name |
targetDomain |
Description |
| self-arrows | objects | self (*) |
objects | identity arrow |
| type-system-arrows | objects | terminative? |
boolean | basic type |
primary? |
boolean | |||
| non-terminals | sc |
non-terminals | superclass | |
| eigenclasses | ce |
objects | eigenclass predecessor | |
| objects | ec |
eigenclasses | eigenclass successor | |
| includers | cparent |
includers | containment parent | |
cname |
symbols (Υ) |
containment name | ||
| common-value-arrows | objects | frozen? |
boolean | frozenness |
tainted? |
taintedness | |||
trusted? |
trust | |||
| special-value-arrows | some non-includers |
value,bytes,dflt, … |
Object value fields, see Object internal value data. | |
Note:
(*) This corresponds to the identity function .self on O.
Because of the dot-notation, there is no naming conflict with the already
introduced self cursor.
ϙ-arrows and ϻ-arrows:ϙ-arrows and ϻ-arrows:
ϙ(x,s).source ≝ x
ϙ(x,s).name ≝ s
ϻ(x,i).source ≝ x
ϻ(x,i).idx ≝ i
ϙ(x,s).target ≝ x.s
ϻ(x,i).target ≝ x.incs[i]
ι-arrows and ϰ-arrows:ι-arrows and ϰ-arrows:
ι(x,i).source ≝ x
ι(x,i).idx ≝ i
ϰ(x,i).source ≝ x
ϰ(x,i).idx ≝ i
ϰ(x,i).hcode ≝ x.hcodes[i]
ϰ(x,i).key ≝ x.keys[i]
ι(x,i).target ≝ x._list[i]
ϰ(x,i).target ≝ x.values[i]
α-arrows and β-arrows:α-arrows and β-arrows:
α(x,s).source ≝ x
α(x,s).name ≝ s
β(x,s).source ≝ x
β(x,s).name ≝ s
α(x,s).target ≝ x.met(s)
β(x,s).target ≝ x.pty(s)
α(x,s).mvisibility ≝ x.mvisibility(s)
| Applies to | Field Name |
Domain (Type) |
Description | Relevant field(s) in MRI |
ϙ-arrows |
source
|
OID | the object | |
name |
ℬ |
internal property name | ||
target
| mixed |
internal property value | ||
ϻ-arrows |
source
|
OID | the includer | |
idx |
ℤ |
member index | ||
target
| OID | ith-includee |
||
ι-arrows |
source
(owner)
|
OID | the Array instance (owner) |
|
idx |
ℤ |
member index | ||
target
(value) |
OID | the target object (value) | ||
ϰ-arrows |
source
(owner)
|
OID | the Hash instance (owner) |
|
idx |
ℤ |
member index | ||
hcode |
ℤ |
hash code | ||
key |
OID | key | ||
target
(value) |
OID | the target object (value) | ||
α -arrows |
source
(owner)
|
OID | the source object (owner) | |
name |
string | method name | ||
mvisibility |
ENUM_V | method visibility | part of flag (in rb_method_entry_t) |
|
target
(value) |
Π |
the target method (value) | def (in rb_method_entry_t) |
|
β-arrows |
source
(owner)
|
OID | the source object (owner) | |
name |
string | property name | ||
target
(value) |
OID | the target object (value) |
αɦ(x,s).owner ≝ x.ec.mowner(s)
βɦ(x,s).owner ≝ x.powner(s)
αɦ(x,s).target ≝ x.ec.met_h(s)
βɦ(x,s).target ≝ x.pty_h(s)
αɦ(x,s).mvisibility ≝ x.ec.mvisibility_h(s)
αɦarrows
and βɦarrows data tables
which are sort of built-in database view.
Gray color indicates that except for the owner column,
αarrows (resp. βarrows)
and αɦarrows (βɦarrows)
have the same field set.
αɦarrows |
βɦarrows |
|||||||||
|
|
a emanating from a given object x
(i.e. such that a.source == x)
we obtain a picture
about object data stratification.
| Arrow set | Subset | Description |
ϙ-arrows |
identity arrow | Object identity |
| type-system | Internal properties | |
| common values | ||
| special values | ||
ϻ-arrows |
Inclusion list | |
ι-arrows and ϰ-arrows |
Array/hash members | |
α-arrows |
Own methods | |
β-arrows |
Own properties | |
αɦ-arrows |
Respondent methods | |
βɦ-arrows |
Inherited properties | |
(.dyn_class?)
where
.dyn_class? is a boolean attribute of classes indicating whether
the class is dynamic.
Proc is dynamic.
.dyn_class? attribute, we
define the following attributes of objects:
.clontype is an attribute of objects with possible values
'clone' or 'none'.
.dumptype is an attribute of objects with possible values
'clone', 'ref' or 'none'.
objects x |
x.clontype |
x.dumptype |
| eigenclasses | 'none' |
'none' |
classes and modules such that x.croot != Object(in particular, anonymous classes and modules) |
immediate values
(false, true, nil, Fixnums, Symbols) |
'ref' |
|
Encodings |
||
the inheritance root r
(i.e. the BasicObject class) |
||
Numerics except Fixnums |
'clone' |
|
includers such that x.croot == Object except r(i.e. full-namedclasses and modules except BasicObject) |
'clone' |
'ref' |
instances of dynamic classes
(Procs, Methods, UnboundMethods, …) |
'none' |
|
other objects
(objects that are not immediate values and not
instances of Module, Class, Encoding,
Numeric and of dynamic classes) |
'clone' |
clone and dup methods of Kernel
create siblings in the primary inheritance.
Given a primary object x (a class or, more typically, a terminal),
y = x.clone (or y = x.dup)
creates a primary object y such that
.terminative? and .ec_sc_pr
coincide on x and y.
The following table shows a correspondence between
clone/dup and new.
The new method |
The clone method |
The dup method |
||
| Application | x is a class |
Class.new(x.sc) |
x.clone |
x.dup |
x is a terminal |
x.class.new |
x.clone |
x.dup |
|
| Hooks | initialize |
initialize_clone |
initialize_dup |
|
initialize_copy |
||||
| Procedure without hooks |
|
|||
|
||||
|
||||
x.arrows is defined as follows:
clone |
dup |
|
|
a.source.pr == x |
a.source == x |
except self-arrows and except the following:
|
|
|
|
Notes:
x with
x.clontype == 'none' is not supported.
r.clontype == 'none'.
clone versus dup
support for instances of Method and UnboundMethod.
This difference is not expressed by the above description.
ϰ-arrows.
a from x.arrows that is an own method arrow
(an α-arrow) the method a.target is copied too.
This allows for proper copy of method de-aliasing.
To describe this would require introducing
arrows for .orig_owner and .orig_name.
Marshal.dump logic
by specifying sets x.objects and x.arrows
of stored objects and arrows, respectively, for each dumpableobject
x.
We proceed inductively
by defining sets x.objects(0), …, x.objects(n)
and x.arrows(0), …, x.arrows(n)
so that
x.objects(n)
(resp. x.arrows(n)), if defined, is some subset of objects (resp. arrows)
reachable from x by at most n arrows.
The sets
x.objects and x.arrows are then either undefined
(in the case of a non-dumpable
object x)
or they are defined by
x.objects == x.objects(n) and x.arrows == x.arrows(n)
n is such that x.objects(n) == x.objects(n+1).
x.objects(0) is undefined if x.dumptype == 'none', else
x.objects(0) is the empty set if x.dumptype == 'ref', else
x.objects(0) is undefined if x.ec has an own method or property
(i.e. an α- or β-arrow a
such that a.source == x.ec), else
x.objects(0) is the single-element set {x}.
x.arrows(0) is undefined if x.objects(0) is undefined, else
x.arrows(0) is the the single-element set {ϙ(x,'self')}
if x.objects(0) is the empty set, else
x.arrows(0) is the set of all arrows a such that either (A) or (B) is
satisfied:
a.source == x and a is of one of the following types:
ϙ-arrow,
ι-arrow (array member),
ϰ-arrow (hash member),
β-arrow (own property, necessarily instance variable,
because x is a pure instance (non-includer)),
a.source.ce == x and a is one of the following types:
ϙ-arrow named sc
(so that a corresponds to a .class-link
–
recall that x.ec.sc equals x.class for terminal x),
ϻ-arrow,
(so that a corresponds to an .ec.incs[i]-link
between x and a module included in the eigenclass x.ec).
n > 0.
x.objects(n) is undefined if
x.objects(n-1) is undefined, else if
y.objects(0) is undefined for some object y that is the target
of an arrow from x.arrows(n-1),
x.objects(n) is the set of all objects y such that
y ∈ x.objects(n-1) or
y.dumptype == 'clone' and
y is the target of an arrow from x.arrows(n-1).
x.arrows(n) is undefined if x.objects(n) is undefined, else
x.arrows(n) is the set of all arrows a such that
a ∈ x.arrows(n-1), or
a ∈ y.arrows(0) for some y ∈ x.objects(n-1).
Comments:
.ec-arrow
are considered as a single short-cutarrow (case (B)).
x.dumptype == 'ref'
then x.objects is defined as empty set but
x.arrows is defined as a singleton set containing just the identity
arrow of x. This means that just a referenceto
x is dumped.
x.objects can only contain pure instances (non-includers).
Includers are dumped by reference.
x is unsupported whenever there is an arrow path
ending in an object y with y.objects(0) undefined,
in particular, if y.dumptype == 'none'.
(.orig_owner, .orig_name)
where
.orig_owner is a function from the set
Π ∖ {π} to includers
assigning each non-whiteout method its original owner.
.orig_name is a function from the set
Π ∖ {π} to Υ
assigning each non-whiteout method its original name.
α,
x.met(s) == α implies
x ≤ α.orig_owner.
Notes:
(x,s) == (α.orig_owner, α.orig_name)
then any of the following cases may occur:
x.met(s) == α (the most typical case),
x.met(s) is undefined or equals π,
x.met(s) is an nwo-method different from α.
.orig_owner and .orig_name are preserved.
overwritingthe original method owner is shown in the following code.
def def_report
class_eval { def report; super end }
end
class A; def report; '-A-' end end
class B; def report; '-B-' end end
class X < A; def_report; end
class Y < B; def_report; end
p Y.new.report #-> -B-
p X.new.report #-> NotImplementedError
Note:
The example demonstrates that the problem is not restricted to eigenclasses,
as suggested by the error message
(super from singleton method that is defined to multiple classes is not supported
).
super)
pmr() from
O × O × Υ
to
O × O × Υ.
The assignment (x,o,s) ↦ (x,p,t)
has the following semantics:
x is the receiver,
o is the current method owner,
s is the current method name,
x is the preserved receiver,
p is the parent method owner,
t is the parent method name.
(x,o,s),
the triple (x,p,t) == pmr(x,o,s) is
defined as follows:
pmr(x,o,s) is undefined if
either
o.met(s) is undefined or a whiteout
or
o does not occur in x.ec.ancs.
α = o.met(s).
i be the smallest such that
x.ec.ancs[i] == α.orig_owner.
If no such i exists then apply (C).
j be the smallest such that i < j and
x.ec.ancs[j] is a method owner of α.orig_name.
If no such j exists then apply (C).
x.ec.ancs[j].met(α.orig_name) is a whiteout then apply (C).
pmr(x,o,s) = (x, x.ec.ancs[j], α.orig_name).
pmr(x,o,s) == (x, x.ec.mowner(μ), μ) if
x.ec.mowner(μ) is defined, else
pmr(x,o,s) is undefined.
Notes:
pmr map.
Examples:
C#report results in skippingof
B#report.
class O; def report; "O" end end class A < O; def report; "A" + super end end class B < A; end class C < B; alias report report end class B; def report; "B" end end class A; remove_method :report end puts C.new.report #--> AO
M in B's ancestor list causes
a cycle of supers.
module M
def report(i=0); "-M(#{i})" + (i < 4 ? super(i+1) : " ...") end
end
class A; end
class B < A; include M end
class A; include M end
p B.ancestors #--> [B, M, A, M, Object, Kernel, BasicObject]
p B.new.report #--> "-M(0)-M(1)-M(2)-M(3)-M(4) ..."
A inherits the report method from N.
The original method owner of N#report is M.
This module does NOT appear among A's ancestors
until the re-inclusion of N into A.
module M; def report; super end end module N; end class O; def report; '-O-' end end class A < O; include N end module N; include M end module N; alias report report end A.new.report rescue puts $!.inspect #--> super: no superclass method p A.ancestors.take(4) #--> [A, N, O, Object] class A; include N end p A.ancestors.take(5) #--> [A, N, M, O, Object] puts A.new.report #--> -O-
blanknessconditions imposed by our axioms. In particular, it is possible to have semiactual objects
x
such that
x has own includees,
x has own methods,
x has own properties (constants or instance variables).
.ec_ref hack
ec_ref
which allows to reference an eigenclass x.ec
of a class x
without x.ec's actualization.
The method makes a guessof
x.ec's object identifier
and uses ObjectSpace._id2ref,
see
The inverse to .object_id.
class Class EC_ID_DELTA = (x = Class.new).singleton_class.object_id - x.object_id def ec_ref ObjectSpace._id2ref(object_id + EC_ID_DELTA) end endThough being platform/environment dependent by nature, the code probably works in most cases.
_class_method hack
private_class_method and public_class_method
visibility specifiers:
class X; end; class Y < X; end def X.m; end nnt_delta_report Y.private_class_method(:m) nnt_delta_report # 0 ec = Y.singleton_class nnt_delta_report # 1 p ec.private_instance_methods(false) # [:m]
!, ==, != or equal?,
Ruby does not provide reflection for
blank slate objects.
Most methods that are stated to be provided for objects
are actually provided for Objects.
A test whether an object x is not a blank slate object
is performed by
Object === x
(it evaluates to true if x is an Object,
i.e. x is not a blank slate object).
.object_id
object_id of Object
provides an injective map from actual objects to integers represented by
Fixnum or Bignum instances.
Immediate values except Symbols have a fixed value-to-id prescription.
Object x |
Class of x |
x.object_id |
Class of x.object_id |
false |
FalseClass |
0 |
Fixnum |
true |
TrueClass |
2 |
|
nil |
NilClass |
4 |
|
x |
Fixnum |
2*x + 1 |
Fixnum
if 2*x + 1 is in Fixnum's range,
Bignum
otherwise
|
| All other objects | No fixed prescription | Fixnum |
|
.object_id
ObjectSpace._id2ref(x)
provides an inverse to y.object_id:
ObjectSpace._id2ref(x) == y if
y.object_id == x for some, necessarily unique,
object y.
y, then an exception is raised.
Note:
The method also works for semiactual objects, see
The .ec_ref hack.
.toX map
.toX function
is an injective map from objects to hexadecimally encoded integers.
It is of the form
x.toX ≝ "%#08x" % [x.oid2]
.oid2 is a variant of .object_id.
The string "0x2fe00e" is an example of a .toX value.
Note:
The 8 digit which is used in the string format
determines string length and is platform dependent.
The conversion between .object_id and .oid2
is partially described in the following table.
x.oid2 |
Applicability condition |
x.object_id |
x is
false, true, nil or a non-negative
Fixnum
|
| ? | x is a negative Fixnum
|
| ? | x is a symbol
|
2 * x.object_id |
x is NOT an immediate value
|
.toS
to_s of Object
and Module provide a map from objects to strings represented by
String instances.
If (CNC~1) is satisfied then the map is injective.
In contrast to object_id, the
to_s method is – by convention –
subject to override.
We therefore refer to an alias method .toS which can be
established by
class Object; alias toS to_s end class Module; alias toS to_s endThe
.toS function is defined according to the following rules:
x.toS |
Applicability condition | |
"Object" |
x == Object |
|
x.cpathname |
x.croot == Object and x != Object |
|
"#<Class:%s>:%s" %
|
x is a named class or module and x.croot an eigenclass
|
|
"#<%s:%s>" % [x.class.toS, x.toX] |
x is a non-includer (pure instance) |
|
x is an anonymous module | ||
"#<Class:%s>" % [x.toX] |
x is an anonymous class |
|
"#<Class:%s>" % [x.ce.toS] |
x is an eigenclass |
|
.toS.
Each row contains a representantive x
of a partition of primary objects
according to the following criteria:
x a named module?
x or of x.class
if x is a non-includer.
x terminal?
X::Y and X.ec::A are classes
and X::M and X.ec::N are modules.
| Containment type | x.ec(i).toS
(string representation of i-th eigenclass
of a primary object x)
|
|||
| ↓ | x↓ |
i →
|
0 |
1 |
| A | X::Y |
X::Y |
#<Class:X::Y> |
|
X::Y.new |
#<X::Y:0xab4590> |
#<Class:#<X::Y:0xab4590>> |
||
| B | Class.new |
#<Class:0xab43f8> |
#<Class:#<Class:0xab43f8>> |
|
↳ .new |
#<#<Class:0xab43f8>:0xab4200> |
#<Class:#<#<Class:0xab43f8>:0xab4200>> |
||
| C | X.ec::A |
#<Class:0xad3d00>::A |
#<Class:#<Class:0xad3d00>::A> |
|
X.ec::A.new |
#<#<Class:0xad3d00>::A:0xaaf9a0> |
#<Class:#<#<Class:0xad3d00>::A:0xaaf9a0>> |
||
named modules |
||||
| A | X::M |
X::M |
#<Class:X::M> |
|
| C | X.ec::N |
#<Class:0xaae968>::N |
#<Class:#<Class:0xaae968>::N> |
|
Observations:
Denote y = x.ec(i).
n the number of trailing '>'
characters of y.toS.
Then for the eigenclass index i the following holds:
i == n - 1
if y.toS contains the string ":0x"
not followed by a string containing "::",
i == n otherwise.
i > 0,
the substring of y.toS
delimited by the last occurrence of a single ':'
and the first occurrence of trailing '>' equals
x.toS if x is a named class or module
with x.croot equal to Object,
x.croot.toX + ">::" + x.cpathname
if x is a named class or module
with x.croot an eigenclass,
x.toX otherwise.
y is
terminative cannot be detected from
y.toS alone
(X.ec::A.toS and
X.ec::N.toS have same format).
y,
to obtain the eigenclass index y.eci and
the primary object y.pr from the
string representation y.toS.
This can be realized by the following code:
class Object
def eci
(s = toS).length - s.index(/[>]*$/) - (s.match(/:0x(?!.*\:\:)/)? 1:0)
end
def pr_chunk
(m = toS.match(/[^:]\:(?!\:)(?!.*[^:]\:[^:])(.*[^>])[>]+$/)) ? m[1] : toS
end
def pr
if eci == 0 then return self end
c = pr_chunk
if c.include?(">") # the complicated case: pr.croot is an eigenclass
r = (m = c.match(/^(0x.*)[>]::(.*)$/)) ? m[1] : raise("???")
r = ObjectSpace._id2ref(eval(r)/2)
return r.class_eval("self::" + m[2])
end
(p = ::Object.class_eval(c)).instance_of?(Fixnum) ?
ObjectSpace._id2ref(p/2) : p
end
end
Unfortunately,
the .pr implementation requires (CNC~2).
class Module
def cname
(m = toS.match(/(^|(::))([^:>]+)$/)) ? m[3] : nil
end
def cpathname
(m = toS.match(/(^Object|^|(::))([^<>]*)$/)) ? m[3] : ""
end
def croot_toS
((s = toS).match(/^[^>]*$/)) ? "Object" :
(m = s.match(/(.*?)::.*[^>]$/)) ? m[1] : toS
end
def croot
if eci > 0 then return self end
n = croot_toS
m = n.match(/\:(0x[^>]*)/)
m ? ObjectSpace._id2ref(eval(m[1])/2) : eval("::" + n)
end
def cancs
a = [croot]
cpathname.split("::").each { |x| a << a.last.const_get(x, false) }
a.reverse!
end
def cparent(i = 1); cancs[i] end
end
O
are described in the following table.
Subset of O |
Way of enumeration | |
| Immediate values |
false, true, and nil |
Literalenumeration |
Fixnums |
Not supported (?)∗ | |
Symbols |
Using Symbol.all_symbols |
|
| Classes and terminals that are not immediate values | Using ObjectSpace.each_object |
|
| Actual eigenclasses | Not supported (?) | |
Note:
(∗)
Fixnums can be enumerated by domain enumeration
.
The not supported status means that there is no efficient built-in way
(known to the author) of enumerating just the fixnums with non-default state
(see Immediate values revisited).
| Relations, (partial) functions and constants defined in this document |
Ruby 1.9 built-in or semi-built-in correspondents | |
| Notation | Description | |
x.sc |
the superclass of a non-terminal x |
x.superclass |
x.ec |
the eigenclass of x(the successor of x in the eigenclass chain) |
x.singleton_class
with the following limitations:
|
x.ec(i) |
i-th eigenclass successor of x |
i > 0 ? x.singleton_class.ec(i-1) : x |
x.ce |
the predecessor of x in the eigenclass chain |
Obtainable from x.toS, see x.ce(i).
Note:
Internally, |
x.ce(i) |
i-th eigenclass predecessor of x |
(j = x.eci - i) > 0 ? x.pr.ec(j) : nil
|
x.pr |
the primary object of x |
Obtainable from x.toS using
ObjectSpace._id2ref. |
x.eci |
the eigenclass index of x |
Obtainable from x.toS. |
r |
the inheritance root | BasicObject |
c
(equal to r.class)
|
the instance root, the metaclass root |
Class |
m |
the metamodule root | Module |
¤
(equal to r.croot)
|
|
Object |
¤.incs[0]
if having its initial value |
the conventional MRO root | Kernel
Note:
More precisely, the conventional MRO root equals the pair |
r.ec |
the implicit-metaclass root | BasicObject.singleton_class |
c.ec |
the usual actualclass root | Class.singleton_class |
x.class |
the class of x |
x.class |
x direct-instance-of y |
x.class equal to y |
x.instance_of? y |
x instance-of y |
x.class is a subclass of or equal to y |
x.class ≤ y && y.class == Class
Note:
|
x kind-of y |
x.ec ≤ y |
x.kind_of? y |
x.incs |
own inclusion list of an includer x
|
Obtainable via included_modules. Equals
|
x own-includer-of y |
includer x
is an own includer of a module y |
x.include?(y) && !x.superclass.include?(y)
is not reliablewith respect to repetitive ancestor lists. |
x includer-of y |
x.include? y (method of Module) |
|
x ≤ y |
HM descendancy | x <= y (method of Module)
Similarly, methods <, >=, > are defined with
obvious meaning.
|
|
inheritance ancestors
of a non-terminal x,
starting with x itself |
Obtainable using x.superclass. Equals
|
x.hancestors |
without eigenclasses |
x.ancestors - x.included_modules |
x.ancs |
MRO ancestors of an includer x,
starting with x itself |
Obtainable using x.superclass and
x.incs. Equals
|
x.ancestors |
without eigenclasses |
x.ancestors (method of Module) |
x.terminal? |
is an object x terminal? |
x.class != Class
|
x.class? |
is an object x a class? |
x.class == Class && x == x.ancestors[0]
|
x.eigenclass? |
is an object x an eigenclass? |
x.class == Class && x != x.ancestors[0]
|
x.terminative? |
is an object x terminative? |
|
x.metaclass? |
is an object x a metaclass? |
Class == x.class && !!(Class >= x)
|
x.module? |
is an object x a module? |
x.kind_of?(Module) && !x.kind_of?(Class)
|
x.metamodule? |
is an object x a metamodule? |
x <= Module && x != Class && x.class?
|
x.blank_slate? |
is an object x a blank slate object? |
!(Object === x)
|
x.frozen? |
is an object x frozen? |
x.frozen? |
x.tainted? |
is an object x tainted? |
x.tainted? |
x.trusted? |
is an object x trusted? |
!x.untrusted? |
x.klass |
the virtual connection to x's eigenclass |
Not supported (?)
Note:
Internally, at the |
x.aclass |
the actualclass of x |
Not supported (?) |
x.actuals |
actual eigenclasses-or-self of a primary object x |
Not supported (?) |
x.cparent |
the containment parent of an includer x |
See Containment reflection |
x.cname |
the containment name of an includer x |
|
x.cpathname |
the containment path-name of an includer x |
|
x.croot |
the containment root of an includer x |
|
x.cancs |
containment ancestors of an includer x |
|
nesting |
the current nesting | Module.nesting |
self |
the current object | self |
main |
the main context | Not directly supported (?) but recordableby $main = self
at the start of a Ruby program |
x nwo-method-owner-of s |
includer x has own method s
which is not a whiteout
|
methods.include?(s)
where methods equals
x.private_instance_methods(false) +
x.protected_instance_methods(false) +
x.public_instance_methods(false)
(methods of Module).
|
x nwo-method-inheritor-of s |
includer x
owns or inherits a non-whiteout method s
|
Same as with nwo-method-owner-of but with
(false) omitted (or replaced by (true))
|
x wo-method-owner-of s
(x.met(s) == π) |
includer x
has own whiteout method s
|
Not supported (?) |
x wo-method-inheritor-of s
(x.met_h(s) == π) |
includer x
inherits a whiteout method s
|
Not supported (?) |
x.mowner(s) |
owner of a non-whiteout method s
inherited by an includer x |
x.instance_method(s).owner
|
x.ec.mowner(s) |
owner of a non-whiteout method s
inherited by an eigenclass x.ec |
x.method(s).owner
(methods of Object and Method, respectively)
|
x constant-owner-of s |
includer x has own constant s |
x.const_defined?(s, false)
|
x constant-inheritor-of s |
includer x owns or inherits constant s |
x.const_defined?(s, true)
|
x.cowner(s) |
owner of a constant s
inherited by an includer x |
x.ancs.find{ |y| y.const_defined?(s, false)}
|
x.mvisibility(s) |
own method visibility of s in an includer x
|
[:private,:protected,:public].find{|v| x.send(
"#{v}_instance_methods",false).include?(s)
}
|
x.mvisibility_h(s) |
inherited method visibility of s
in an includer x
|
Same as with .mvisibility
but with false replaced by true.
|
uqcr(s) |
unqualified constant resolution of s |
See Unqualified constant resolution |
qcr(x,s) |
qualified constant resolution of (x,s) |
See Qualified constant resolution |
uqmr(s) |
unqualified method resolution of s |
See Unqualified method resolution |
qmr(x,s) |
qualified method resolution of (x,s) |
See Qualified method resolution |
pmr(x,o,s) |
parent method resolution of (x,o,s) |
Not supported (?) |
x.hcodes |
list of hash-codes of a hash x |
Not supported (?) |
x.keys |
list of keys of a hash x |
x.keys |
x.values |
list of values of a hash x |
x.values |
x.dflt |
the default value/evaluator of a hash x |
x.default_proc || x.default |
x.dflt_call? |
the .dflt interpretation switch of a hash x |
!!x.default_proc |
| Method name | Owner | Semantics |
singleton_methods |
Kernel |
|
instance_methods |
Module |
x.instance_methods(inherit) equals
x.protected_instance_methods(inherit)
+
x.public_instance_methods(inherit)
up to member order
|
| Owner | Methods |
BasicObject |
!
==
!=
equal?
initialize
instance_eval
|
Object (Kernel) |
class
clone
define_singleton_method
dup
eql?
extend
freeze
frozen?
hash
initialize_dup
initialize_clone
initialize_copy
instance_of?
kind_of?
method
object_id
send
singleton_class
singleton_methods
taint
tainted?
to_s
untaint
untrusted?
|
Module |
ancestors
class_eval
const_defined?
const_get
const_missing
const_set
constants
define_method
extended
include
include?
included_modules
instance_method
instance_methods
method_defined?
module_eval
module_function
name
private
private_class_method
private_instance_methods
private_method_defined?
protected
protected_instance_methods
protected_method_defined?
public
public_class_method
public_instance_method
public_instance_methods
public_method_defined?
remove_const
remove_method
to_s
undef_method
|
Module.ec |
nesting
new (削除ここまで) |
Class |
new
superclass
|
Class.ec |
new (削除ここまで) |
Kernel |
global_variables
lambda
p
puts
|
Array |
[]
+
<<
drop
each
empty?
first
include?
index
join
last
length
map
map!
pop
push
replace
reverse
select
shift
slice
uniq
unshift
zip
|
String |
[]
%
+
bytes
chars
encode
encoding
length
match
to_sym
|
Encoding |
ascii_compatible?
dummy?
name
|
Hash |
[]
compare_by_identity?
default
default_proc
keys
values
|
Method |
name
owner
receiver
unbind
|
UnboundMethod |
bind
name
owner
receiver
|
Symbol |
to_s
|
Symbol.ec |
all_symbols
|
ObjectSpace.ec |
_id2ref
count_objects
each_object
|
Rational |
denominator
numerator
|
Complex |
imag
real
|
Range |
begin
end
exclude_end?
|
Marshal.ec |
dump
|
Notes:
Object
indicates that this class is stated to be method owner only by convention.
new of Class.
In the expression Class.new,
the instance method of the Class class is invoked.
This can be introspected by Class.method(:new).owner == Class.
new of Module.
ℍ corresponds to set inclusion ⊆.
.ec ◦ ℍ (*)
corresponds to set membership ∈.
Note:
(*)
.ec ◦ ℍ equals
.ec ◦ ≤ range-restricted to non-terminals.
(In this restriction, no object can be kind-of a module.)
The .ec ◦ ≤ relation is introduced in the S2 structure
and corresponds to the .kind_of? / .is_a?
reflection method.
The document also provides alternative axiomatization of S1 structures.
Μ relation
(self-or-own-includer-of).
The document
[]
uses the ≤ symbol for .sc-inheritance
(which is called simply inheritance).
In addition, both ≤ and Μ are reflexive
over the whole set of objects, including all terminals.
Strings, Arrays, Hashes,
Numerics, Ranges)
introduced.
.culturality (削除ここまで)trusted? attribute added.
super logic.
pure instanceintroduced as a synonym to
non-includer.
S1₀₁ structure,
.ec-.aclass interchangeability, conventional actuality.
.terminative? attribute removed from S1 signature,
(S1~1) and (S1~2) interchanged.
Class.ec not being an owner of new.
c symbol for the Class class.
qcr/uqcr).