Author
|
Document date
|
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.
(2)←(1) |
|
(1)←(2) |
|
||
i.e. .p⊆) satisfying (∗), | is the smallest function on X (w.r.t.(X,≤) is the reflexive transitive closure of (X, .p ), (∗) | ||||
(3)←(1) |
| (1)←(3) |
|
||
i.e. .p is the smallest relation on X
satisfying (∗), the
reflexive transitive reduction of (X,≤) (a.k.a Hasse diagram), |
(X,≤) is the reflexive transitive closure of (X, .p) (∗). |
Notes:
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.
Primary objects |
Secondary objects
|
||
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 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 i
th application of .sc
(resp. .ec
)
to x
(we allow 0
th application
whenever the 1
st 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.
(S1~1) |
The structure (O, .ec, .pr) is a
primorder algebra.
We will apply established notation and terminology, in particular, definition of .ce and .eci .
|
(S1~2) |
The inheritance ℍ
is an algebraic tree on non-terminals, its root is r .
More specifically, for every object x ,
|
(S1~3) |
.sc and .ec are commutative in the following sense:
if( .sc -links per .ec -chains are parallel.) |
(S1~4) |
.sc preserves primary objects on non-terminals, i.e.
x.sc is a class for every non-root class x .
|
(S1~5) |
.ec.sc preserves primary objects on terminals, i.e.
x.ec.sc is primary for every terminal x .
|
(S1~6) |
.sc maps to classives, i.e.
for every object x ,
if x.sc is defined then x.sc.pr is a class.
|
(S1~7) |
c equals
r.ec.sc and is different from r
(this prevents the degenerate case of r being the only class).
|
(S1~8) |
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)
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 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:
State transitions are substructural in the S1 structure,
i.e.
for every states 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 ,
| |
For every consecutive states 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)).
Classes
Metamodules
|
Modules
Terminals
|
(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:
(S2~1) |
The root metamodule m equals
the already presented helix class Module .
|
(S2~2) |
x.incs is defined iff x is a module or a non-terminal.
|
(S2~3) | Only modules can appear in inclusion lists. |
(S2~4) | A module cannot appear in its own inclusion list. |
(S2~5) | All inclusion lists are non-repetitive (injective), i.e. no object appears more than once in the same inclusion list. |
.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 X
s 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
X
s as kindsof
X
.
X
is a class then X
s means instances of X
.
kind of BasicObject |
Pure instances
(non-includers) |
|||
kind of Object |
||||
kind of Module |
Modules |
|||
kind of Class |
Classes and Eigenclasses |
Module
subtable provides the following nomenclature of includers:
Classes | ⊂ | Class es |
⊂ | Module s |
⊃ | Modules |
∥ | ∥ | ∥ | ∥ | |||
Class es − Eigenclasses |
Non-terminals | Includers | Module s − Class es |
Module
.
(Classes ⊂ Module
s.)
Class
.
(Modules ∩ Class
es == ∅.)
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.
Object
s
Object
are considered
blank slate objects.
BasicObject
s
== Object
s ⊎ (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 :
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 i
th application of
.super
to (x,y)
.
(Μ, ≤)
is defined by
(x,y) ≤ (a,b) iff
|
|
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 i
th
(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:
State transitions S → S' are substructural in the S2 structure.
|
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
include
and extend
might be considered
outer
methods of the
form
<outer-method> ==
<inner-method> + <hook-method>
hooks:
0 |
1 |
||
|
|||
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 φvalue
d.
Note:
Gray color indicates that merging V1 and S2 structures yields
a single meaning for the O
symbol.
The following condition is required:
(V1~1) |
Tuples of atomic elements belong to the value domain,
i.e. all finite products
ΦA × ΦA × ⋯ × ΦA
are subsets of Φ .
|
Note:
φvalue
d:
Symbol
s and String
s are
φvalue
d by pairs from ℬ × ℬ
(via estrings).
Rational
s are
φvalue
d 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 (Symbol
s).
Υ
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 φvalue d 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:
φvalue
s.
.φ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:
(S4~1) | Only finitely many objects can be actual. |
(S4~2) | Only eigenclasses can be non-actual. |
(S4~3) |
.ce preserves actuals.
For every eigenclass x ,
if x is actual then x.ce is actual.
|
(S4~4) |
.sc preserves actuals.
For every eigenclass x ,
if x is actual then x.sc is actual.
|
If r.ec(i) is actual then so is r.class.ec(i) .
| |
r.class.ec ,
the Class 's eigenclass, is actual.
| |
(S4~7) | Only actuals can be included or have includees. |
(S4~8) | Eigenclasses of immediate values are non-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
.
Helix actual lists are equally sized. | |
Eigenclasses of helix classes are actual. |
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
.
≤
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 |
For every terminative eigenclass 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.
State transitions S → S'
are incremental in the S4 structure in the following sense.
For every object x from
O ∩ O' ,
|
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).
Numeric
s
that are not Fixnum
s
(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 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
.
(S5~1) |
The containment Ϲ is an algebraic forest
(with .cparent undefined on roots).
|
(S5~2) |
There is exactly one containment root x such that x.cname
is defined, namely the Object class.
|
(S5~3) |
If x.cparent is defined then
x.cname is defined.
(Non-roots are named.)
|
(S5~4) |
For every eigenclass x , x.cname is undefined.
(Eigenclasses are anonymous, therefore roots.)
|
(S5~5) | Anonymous classes and modules have no containment descendants. (Thus, being roots, they are containment singletons.) |
(S5~6) | Non-actuals have no containment descendants. (Thus, being roots, they are containment singletons.) |
(S5~7) |
Containment names are constant names.
(This statement comes into effect as soon as the meaning of being a constant nameis defined.) |
(S5~8) |
If 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.
State transitions 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 |
||||
B | x |
x::Q = q |
||||
x |
x.class_eval { self::Q = q } |
Notation / Expression | Terminology / Description | Domain | Generating map | Relation characteristics |
ℍ
(Class es, ≤) |
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.
(S6~1) |
If x is frozen then x.ec is frozen.
|
(S6~2) |
If x is tainted then x.pr is tainted.
(.ec -chains are tainted as a whole.)
|
(S6~3) |
If x is trusted then x.pr is trusted.
(.ec -chains are trusted as a whole.)
|
(S6~4) | Immediate values are trusted. |
Transitions S → S'
preserve being frozen, i.e. if x is frozen in S
then it is so in S' .
| |
Inclusion lists of frozen includers cannot be extended. | |
Transitions preserve taintedness of frozen objects. | |
Transitions preserve trust of frozen objects. | |
Transitions preserve .φ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.
(S7~1) |
Classes and modules that appear in nesting are named.
|
(S7~2) |
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.
The main context 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 Φ
.
(S8~1) |
ω(a,b) is defined iff the following are satisfied:
|
ω(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
OID
s
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
cname
s should be more precisely described
as φvalue
s of Symbol
s.
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 Class es.
|
||
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 .
|
||
Class es |
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 Encoding
s, 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 Encoding
s to
ascii bytestrings.
(Even .name.upcase
is injective.)
e8b
is a distinguished ascii-compatible encoding,
called binary and name
d 'ASCII-8BIT'
.
e7b
is a distinguished ascii-compatible encoding,
called ascii and name
d '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:
(D0~1) |
('',e) is a valid estring for every encoding e .
|
(D0~2) |
(s,e8b) is a valid estring for every bytestring s .
|
(D0~3) |
(s,e7b) is a valid estring iff
s is an ascii bytestring.
|
(D0~4) |
(s,e7b) ≘ (s,e8b) whenever
(s,e7b) is a valid estring.
|
(D0~5) |
(s,e) ≘ (t,e) implies s == t ,
i.e. valid ≘ -equivalent estrings with the same encoding are equal.
|
(D0~6) |
For every ascii-compatible encoding e and
every valid estring (s,e7b) ,
(t,e) .
|
(D0~7) |
For every encoding e from ℇ¢ and every
bytestrings s , t such that (s,e) is valid,
|
(D0~8) |
If a , b are leading characters
∗
of x , y , then
|
(∗)
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.
For transitions 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.
| |
Encoding name s are preserved across transitions.
|
(String, .estr)
where
String
is a class, a direct descendant of Object
.
.estr
is a function from
String
s and Symbol
s to the set ⅀
of estrings.
(D1~1) |
String s and Symbol s are φvalue d by
pairs of bytestrings as follows:
(s,e) == x.estr .
|
(D1~2) |
For every symbol x ,
the estring x.estr is valid.
|
(D1~3) |
For every symbol x ,
if x.estr ≘ (s,e7b) for some s then
x.estr == (s,e7b) .
(I.e. ascii-only symbols are ascii.) |
Notes:
String
s.
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 Array
s to finite lists of objects.
Array
s 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. |
Transitions 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
.
Hash
es 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:
(D3~1) |
The list x.hcodes.zip(x.keys)
(x.hcodes paired with x.keys ) is non-repetitive.
|
(D3~2) |
If x.compare_by_identity? is true then
even the list x.keys is non-repetitive.
|
(D3~3) |
If x.dflt_call? is true then
x.dflt is an instance of Proc .
|
(D3~4) |
If 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.
|
|
Transitions S → S'
preserve the 6 member maps
.hcodes, …, .dflt_call? on frozen hashes.
| |
True values of .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
Proc
s × Hash
es × 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
Hash
es × 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
.
Bignum
s are called bignums.
.real
and .imag
are functions from Complex
es to Numeric
s
that are not Complex
es.
Note:
The Fixnum < Integer < Numeric
class chain has
already been introduced, see
Immediate values.
The structure is subject to the following axioms:
(D4~1) |
Integer s are either Fixnum s or Bignum s.
|
(D4~2) |
Bignum , Float , Rational and Complex
are quasifinal
(in addition to Fixnum ).
|
(D4~3) |
Integer s are φvalue d by the integers ℤ .
|
(D4~4) |
Bignum s and Fixnum s have disjoint .φvalue images.
|
(D4~5) |
Float s are φvalue d by the floating point numbers ℱ .
|
(D4~6) |
Rational s are φvalue d
by pairs (n,d) from ℤ × ℤ such that
|
The .real and .imag maps
are preserved on all Complex es.
|
(Range, .start, .end, .exclude_end?)
where
Range
is a direct descendant of Object
.
Range
s are called ranges.
.start
and .end
are functions from Range
s to objects.
.exclude_end?
is a boolean attribute of Range
s.
The .start , .end and .exclude_end? maps
are preserved on all Range s.
|
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} |
|
Encoding s |
name |
ℬ |
encoding name |
dummy? |
boolean | indicates statefulencoding without proper character handling |
|
ascii_compatible? |
boolean | indicates ascii-compatible encoding | |
Symbol s and String s |
bytes |
ℬ |
byte-sequence together with encoding |
encoding |
ENUM |
||
valid_encoding? |
boolean | indicates whether bytes is a valid sequence w.r.t
encoding |
|
Array s |
length |
ℤ |
array length |
Hash es |
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 |
|
Fixnum s |
value |
ℤ |
the φvalue of a fixnum |
Bignum s |
value |
ℤ |
the φvalue of a bignum |
Float s |
value |
ℱ |
the φvalue of a float |
Rational s |
numerator |
ℤ |
the first component of the φvalue |
denominator |
ℤ |
the second component of the φvalue , needs to be positive |
|
Complex es |
real |
OID | the real-part object |
imag |
OID | the imaginary-part object | |
Range s |
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:
Only actual objects can have own methods. | |
Only includers can have own methods. |
If in initial state, an A1 structure satisfies the following:
(A1~3) |
The inheritance root 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.
Eigenclasses x.ec of Numeric s x
cannot have own methods.
|
.mowner()
from O × Υ × {true, false}
to O
,
called method owner map,
by
x.mowner(s, wo) == y |
iff |
|
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
.
Transitions 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:
(A2~1) | Only actual objects can have own properties. |
(A2~2) | Only includers can have own constants. |
(A2~3) | Only includers can have own class variables. |
(A2~4) |
Only the 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:
(A2~5) |
The metamodule root m is an nwo-method owner of
κ (the const_missing method).
|
weak formof the following condition applies:
(A2~6*) |
For every includers 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)
|
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
|
|
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
(1) x.powner(s) == x.cowner(s) |
if s is a constant name
and x.cowner(s) is defined, |
(2) x.powner(s) == x |
if s is an instance variable name
and x.pty(s) is defined, |
(3) (value not specified) |
if s is a class variable name
and (condition not specified), |
(4) x.powner(s) == Kernel |
if s is a global variable name
and Kernel.pty(s) is defined, |
(5) 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
Fixnum
s 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
Transitions 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.
(CNC~0) |
the pathname estrings 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.
(CNC~1) |
for every includers 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
(CNC~2) |
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
.)
(CNC~3) |
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 Array s × ℤ to Α ,
|
ι(x,i) is defined iff x._list[i] is defined.
|
ϰ() is a partial map
from Hash es × ℤ 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.
|
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 | source Domain |
name |
target Domain |
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:
|
|
|
|
ι
-arrows and ϰ
-arrows:ι
-arrows and ϰ
-arrows:
|
|
|
|
α
-arrows and β
-arrows:α
-arrows and β
-arrows:
|
|
|
|
|
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 | i th-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) |
|
|
|
|
|
αɦ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.
(A5~1) | All subclasses of a dynamic class are dynamic. |
(A5~2) |
Of the already introduced classes, only 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 , Fixnum s, Symbol s) |
'ref' |
|
Encoding s |
||
the inheritance root r
(i.e. the BasicObject class) |
||
Numeric s except Fixnum s |
'clone' |
|
includers such that x.croot == Object except r (i.e. full-namedclasses and modules except BasicObject ) |
'clone' |
'ref' |
instances of dynamic classes
(Proc s, Method s, UnboundMethod s, …) |
'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
y = x.dup
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 name
d 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.
(A6~1) |
For every non-whiteout method α ,
|
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 α
.
The .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,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 super
s.
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 Object
s.
A test whether an object x
is not a blank slate object
is performed by
Object === x
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 Symbol
s 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 |
|
||||
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
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>> |
||
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 endUnfortunately, the
.pr
implementation requires 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 |
Fixnum s |
Not supported (?)∗ | |
Symbol s |
Using Symbol.all_symbols |
|
Classes and terminals that are not immediate values | Using ObjectSpace.each_object |
|
Actual eigenclasses | Not supported (?) |
Note:
(∗)
Fixnum
s 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 |
|
|
|
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.
Ruby Hacking Guide, 2004, , Hawthorne Press 2008, http://www.hawthorne-press.com/WebPage_RHG.html) | ,|
Union mounts/writable overlays design, 2009, LWN.net http://lwn.net/Articles/355351/ | ,|
The Well-Grounded Rubyist, Manning Publications 2009 | ,|
Abstract State Machines: A Method for High-Level System Design and Analysis, Springer 2003 | ,|
Introduction to lattices and order, Cambridge University Press 2002 | ,|
The double inclusion problem, 2005, http://eigenclass.org/hiki/The+double+inclusion+problem | ,|
Ruby and otherwise, http://www.klankboomklang.com/category/ruby-internals/ | ,|
The Ruby Programming Language, O'Reilly 2008 | ,|
Putting Metaclasses to Work, Addison Wesley 1998 | ,|
Evolving algebras: An attempt to discover semantics, in Current trends in theoretical computer science: essays and tutorials, Grzegorz Rozenberg, Arto Salomaa (eds), World Scientific 1993, http://www.eecs.umich.edu/gasm/tutorial/tutorial.html | ,|
Evolving Algebras 1993: Lipari Guide, in Specification and Validation Methods, E. Börger (ed.), Oxford University Press 1995 | ,|
The Ruby Object Model - Structure and Semantics, 2009, http://www.hokstad.com/ruby-object-model.html | ,|
Ruby Draft Specification, 2009, http://ruby-std.netlab.jp/ | ,|
Ruby's Implementation Does Not Define its Semantics, 2010, http://yehudakatz.com/2010/02/25/rubys-implementation-does-not-define-its-semantics/ | ,|
The Secret Life Of Singletons, 2008, http://banisterfiend.wordpress.com/2008/10/25/the-secret-life-of-singletons/ | ,|
Ruby objects, classes and eigenclasses, 2008, http://mccraigmccraig.wordpress.com/2008/10/29/ruby-objects-classes-and-eigenclasses/ | ,|
nLab tree, 2011, http://ncatlab.org/nlab/show/tree#as_digraphs_6 | ,|
The Linux VFS Model: Naming structure, 2011, http://www.atalon.cz/vfs-m/linux-vfs-model/ | ,|
The Ruby Object Model: Comparison with Smalltalk-80, 2012, http://www.atalon.cz/rb-om/ruby-object-model/co-smalltalk/ | ,|
The Ruby Object Model: S1 superstructure representation, 2012, http://www.atalon.cz/rb-om/ruby-object-model/s1-rep/ | ,|
Ruby Object Model – The S1 structure, 2012, http://www.atalon.cz/rb-om/ruby-object-model/rb-om-s1.pdf | ,|
Object Membership: The core structure of object-oriented programming, 2012, http://www.atalon.cz/om/object-membership/ | ,|
Metaprogramming Ruby, Pragmatic Bookshelf 2010 | ,|
Read Ruby, http://ruby.runpaint.org | ,|
Ruby Doc, http://ruby-doc.org | |
Ruby Forum, http://www.ruby-forum.com | |
The Python 2.3 Method Resolution Order, 2003, http://www.python.org/2.3/mro.html | ,|
The Ruby Object Model and Metaprogramming, 2008, http://pragprog.com/screencasts/v-dtrubyom/the-ruby-object-model-and-metaprogramming | ,|
Wikipedia: The Free Encyclopedia, http://wikipedia.org |
March | 2 | 2011 | The initial release. |
September | 2 | 2011 |
Major update. Main changes:
|
October | 18 | 2011 | Improved description of module inclusion. |
January | 2 | 2012 |
|
January | 4 | 2012 | An example of parent method resolution added. |
January | 11 | 2012 |
|
Febuary | 10 | 2012 |
|
March | 2 | 2012 |
|
April | 3 | 2012 | Renumbering of S4~ conditions so that conditions that only depends on S1 are listed first. |
April | 23 | 2012 | New appendix: The S1 structure (a PDF article). |
April | 27 | 2012 |
Note about Class.ec not being an owner of new .
|
June | 21 | 2012 |
|
June | 28 | 2012 |
Corrected & improved description of constant resolution
(qcr /uqcr ).
|
June | 29 | 2012 | The definition of a primorder algebra introduced explicitly (moved from []). |
October | 10 | 2012 | A reference to object membership [] added. |