As its title implies, this document describes an extensibility scheme that is intended to be “generic”, meaning that it is not tied to any particular data model or application.
The scheme was designed with two specific applications in mind: the OME-Zarr imaging file format and an upcoming imaging metadata specification to be developed as part of the Imaging-PHD project.
However, the scheme should be applicable to any other data model that one wishes to make extensible with the same aims as those stated below.
This document will talk a lot about “objects”. In this context, an object is basically a JSON dictionary containing pre-determined keys.
The keys that an object can contain are defined by the object’s “model”, also referred to as the class of the object. Those keys are indistinctly called the fields, the attributes, or the slots of the class.
The use of the word “class” does not imply that this scheme is only intended to be used when working in a programming language that supports object-oriented programming. In this document, a “class” is merely an agreement that a given JSON dictionary is expected to contain a pre-defined set of keys, which are themselves expected to contain some pre-defined types of values. That agreement has no bearing on how the dictionary should be represented in a computer program; it can be represented using an actual class in a programming language that has such a concept (e.g. a Python class or a Java class), but it does not have to.
Likewise, when this document says that “class C inherits from class P” (or that it derives from P, or that it is a subclass of P), it merely means that C describes a dictionary that is expected to contain the same of pre-defined keys as P, plus any additional keys that are specific to C.
The world “model”, used alone, can also refer to the entire set of classes that describes all the objects that can be found in a given JSON document.
In the context of this document, a “schema” is a formal representation, using some specialised schema definition language (SDL), of a model or of a fragment of a model.
LinkML is, among other things, a schema definition language.
The extensibility scheme described in this document has been designed, among other criteria, to be compatible with the use of LinkML as the schema definition language to represent the model that one wants to be extensible. Furthermore, some aspects of the scheme have been designed to explicitly benefit from the use of LinkML, notably in that those aspects would be easy to implement using LinkML – provided that your programming language of choice has decent support for LinkML.
That being said, the extensibility scheme is not dependent on LinkML. Its principles stand on their own, and it should be possible to implement it without ever using LinkML.
The following example will be used throughout this document to illustrate how the proposed extensibility scheme works. It is loosely based on the OME model’s “Instrument” branch, that describes a microscope.
{
"manufacturer:": "Zeiss",
"model": "LSM-880",
"light_source":
{
"object_type": "https://example.org/ome/ArcLightSource",
"power": 45,
"lamp_type": "Hg"
}
}Here is what that example could look like, with an extension aiming to describe the kind of microscopes that one could find a long time ago, in a galaxy far, far away:
{
"manufacturer": "Imperial Microscope Builders",
"model": "K2SO",
"light_source":
{
"object_type": "https://example.com/swm/KyberLightSource",
"power": 900,
"crystal_color": "red"
},
"extensions":
{
"https://example.com/swm/MicroscopeExtension":
{
"planet": "Coruscant"
}
},
"extension_manifest":
[
{
"id": "https://example.com/swm/",
"version": "1.0",
"schemas":
[
{
"type": "LinkML",
"url": "https://schemas.example.com/swm/swm.yaml"
},
{
"type": "JSONSchema",
"url": "https://schemas.example.com/swm/swm.json"
}
],
"homepage": "https://example.com/swm/"
}
]
This example shows a single extension
(https://example.com/swm/, also referred to in the rest of this
document as “the SWM extension”) making use of the two types of extension
mechanisms supported by this scheme: a natural extension class
(https://example.com/swm/KyberLightSource) and a class
extension (https://example.com/swm/MicroscopeExtension).
Read on for details.
This extensibility scheme aims to achieve the following three goals:
The scheme is designed to allow for runtime data interoperability across implementations that support a different set of extensions (including the empty set, i.e. an application that supports no extensions) of a same “base” model.
Given a base model M and two extensions E1 and E2 (which would ideally all be expected to be represented as schemas in an appropriate schema definition language, though strictly speaking this is not necessary), we can define the following derived models:
The basic goal of “runtime data interoperability” is that an application that is only aware of the base model M can still read and manipulate data conformant to either of the three derived models, while (1) preserving any extended data it does not understand (the part of the data that is conformant with one of the extensions), (2) recognising the presence of extended data, and (3) offering the users some ways of accessing (and possibly manipulating) the extended data.
Likewise, an application that is only aware of the M(E1) model (base model + extension E1) should still be able to read and manipulate data that is conformant with all other models, under the same conditions.
This is of course not limited to only two extensions: the principle is generalizable to an arbitrary number of extensions.
To fully understand the goal of runtime data interoperability, it is necessary to explain what is exactly meant by “an application aware of an extension” or “aware of an extended model”.
In this scheme, we consider three “levels” of awareness:
Compile-time awareness1 of an extension (or of an extended model) is when the developers of the application know about the extension during the development of the application, and write code to explicitly deal with data conformant with the extended model. Typically, this level of awareness will not only allow to read and manipulate the extended data, but also to offer specific features that explicitly make use of said extended data.
When schemas (such as LinkML) are used to formally describe the base model and its extension(s), compile-time awareness notably means that developers can rely on any code generation feature enabled by the type of schema used to automatically generate at least part of the code needed to read and manipulate the (extended) data.
Runtime awareness of an extension is when an application has been developed without explicit support for the extension (either because the developers did not know about that extension, or because they made a conscious decision not to implement it for whatever reason), but is able to dynamically learn about the extension at runtime. This requires that (1) a formal schema describing the extension can be obtained somehow (this should be the role of the “extension manageability” layer envisioned further below and (2) that the application is able to exploit said schema.
This level of awareness should allow an application to at least validate the extended data, even if it cannot make explicit use of it. Depending on the “richness” of the provided schema, it could also allow the application to display the extended data to the user in a more meaningful way.
When schemas are used, this level of awareness does not allow to use schema-powered code generation! By definition, the application is already running at that point, so code generation is no longer an option.2
Unawareness of an extension is when an application has been developed without explicit support for the extension and is unable to dynamically learn about the extension at runtime – either because a schema describing the extension cannot be obtained, or because the application is not equipped to exploit the schema (for example, if the extension comes with a LinkML schema, the application must have developed so that it can understand and exploit LinkML schemas).
This level of “awareness” (or rather this absence of awareness) obviously drastically limits what an application can do with the extended data it may find. However, an application that is compatible with this extensibility scheme, even if it is completely unaware of a particular extension, should still minimally be able to recognise the presence of extended data and ensure that it is fully preserved.
This extensibility scheme is expressly designed to ensure that, even if two extensions, which are being developed separately without any kind of coordination between their developers, extend the base model in an identical fashion (for example by adding a field with the same name, but possibly with a different meaning, to the same class), it is still possible to use both extensions at the same time in the same data, without any possible confusion or, worse, data loss.
This extensibility scheme makes extensions manageable as first-class entities.
This notably means that, when a data file is making use of one or more extensions to the base model, an application (even an application that is not compile-time aware of any of the extensions being used) can know
Extensions as first-class entities are also intended to provide applications (and users) with additional information about the extensions, such as who is responsible for a given extension or where more information can be found about the extension.
Notably, “more informations about the extension” can include schemas that formally describe the extension, allowing an implementation that does not explicitly support the extension at compile-time to learn about the extension at runtime and to at least validate the extended data against a reference schema.
This extensibility scheme is not concerned about who is allowed to create an extension E? to a base model M. As far as this scheme is concerned, anyone can create an extension (and the scheme is explicitly designed with such a scenario in mind). Adding (and enforcing) restrictions about the source of an extension (for example, “only the authors of the base model can create an extension”, or “an extension can only be used after it has been formally approved by the International Steering Committee for the Base Model in one of its annual plenary session”) can easily be done if needed by an additional layer on top of this extensibility scheme.
The requirement for data runtime interoperability across implementations with varying extension support creates some fundamental constraints on what an extension can do – about how the base model can be extended.
Simply put, an extension cannot change the base model in a way that would make data conformant to the extended model not conformant with the base model. So, an extension is really about literally extending the base model – the extended model should be a strict superset of the base model.
Therefore, an extension can:
An extension cannot:
This scheme distinguishes two mechanisms by which a base model can be extended:
They are not mutually exclusive: the same extension can extend the same model at different places using either of the two mechanisms at each place where it needs to extend the model.
A natural extension class exploits what this scheme calls a “natural extension point” in the base model. Basically, a natural extension point is a place in the base model where the model is already set up to allow for some variability.
To illustrate, let us consider again our base microscope example:
{
"manufacturer:": "Zeiss",
"model": "LSM-880",
"light_source":
{
"object_type": "https://example.org/ome/ArcLightSource",
"power": 45,
"lamp_type": "Hg"
}
}We are interested in the light_source field, which expects an
object that can be an instance of slightly different classes, each class
representing a different type of “light source”. In the example above, the
object is an instance of the
https://example.org/ome/ArcLightSource class, representing a
light source that is some kind of arc lamp.
Here is another example with a different light source object:
{
"manufacturer:": "Zeiss",
"model": "LSM-880",
"light_source":
{
"object_type": "https://example.org/ome/LaserLightSource",
"power": 45,
"wavelength": 488,
"laser_type": "HeNe"
}
}The light source object in this case is an instance of the
https://example.org/ome/LaserLightSource class, which has the
same power field than the
https://example.org/ome/ArcLightSource class but has also some
specific fields of its own (wavelength and
laser_type).
The object_type field, which is also common to both classes,
is what allows an application to recognise which type of light source it is
dealing with, and therefore which fields to expect in the rest of the
object.
In LinkML, the object_type field would be known as the
type designator slot for all the classes representing light
sources.
In the Rust language with the Serde
library, the light_source field would be said to accept
an internally tagged
enum, with the object_type being the “tag”.
Other (de)serialisation libraries might refer to the same concept under other names. In this document, we will be using the LinkML terminology.
The important point here is that, even when just using the base model
(before adding any extension to the mix), an application using the base model
must already be prepared to deal with the facts that (i) there are several
types (“classes”) of light source objects, and (ii) the exact type of light
source used in a given Microscope object is not known in advance, but
has to be discovered at runtime (by looking up the value of the
object_type field). Therefore, it would be reasonably trivial to
add a new type of light source to the pre-existing list of types. This is why
we say that the model is naturally extensible at the
light_source point (or that light_source is a
“natural extension point”).
This scheme strongly recommends that model designers create natural
extension points in their models whenever possible, as it is the simplest way
to extend a model. All that is required to create a natural extension point is
a base class (in the example above, the base class does not explicitly appear,
but that is the class from which both the
https://example.org/ome/ArcLightSource and
https://example.org/ome/LaserLightSource are derived) with a type
designator field.
When a natural extension point is available, a third-party can then extend the model simply by deriving new classes from the base class.
This is what our example “SWM extension” extension shown earlier in this
document is doing. It creates a new light source class (which is a “natural
extension class”) called
https://example.com/swm/KyberLightSource, which can be used
wherever a light source is expected:
{
"light_source":
{
"object_type": "https://example.com/swm/KyberLightSource",
"power": 900,
"crystal_color": "red"
}
}Upon encountering a Microscope object containing such a light source:
light_source field as
a generic light source, but could then use the schema describing the
extension to learn about any field that is specific to the
https://example.com/swm/KyberLightSource class;light_source field as a
generic light source (because it knows that the light_source
field cannot contain anything else than a light source, whatever its exact
type is), and would at least preserve any additional field that it does
not know about.The second extensibility mechanism allowed by this scheme is intended to allow adding new fields to a pre-existing class.3
In our running example, the SWM extension needs to add a
planet field to the Microscope object (presumably
representing the planet where the microscope has been manufactured). This is
done as follows:
{
"manufacturer": "Imperial Microscope Builders",
"model": "K2SO",
"extensions":
{
"https://example.com/swm/MicroscopeExtension":
{
"planet": "Coruscant"
}
}
}The principle is that any class intended to be extensible has an implicit
field named extensions specifically intended to store all the
fields added by extensions.
That field is itself a dictionary where each key corresponds to a
particular extension, and the value is an object fragment containing
the fields added to the class by the extension (in this example, the
planet field).
This principle naturally ensures that independently developed extensions
cannot interfere with each other, even when used within the same instance
data. If another extension also wanted to add its own planet
field, along with maybe other fields of its own, this would look like
this:
{
"manufacturer": "Imperial Microscope Builders",
"model": "K2SO",
"extensions":
{
"https://example.com/swm/MicroscopeExtension":
{
"planet": "Coruscant"
},
"https://example.net/stm/MicroscopeExtension":
{
"planet": "Vulcan",
"user": "Spock"
}
}
}Of course, we would likely want to avoid having different extensions to represent the same thing; but preventing this situation from happening is out of reach of any extensibility scheme, if only because it is mostly a social problem – basically getting different people to agree on working together – rather than purely a technical one. The role of the extensibility scheme in that regard is merely to minimize the impact of such a situation, not to avoid it.
Upon encountering data where a Microscope instance contains a
https://example.com/swm/MicroscopeExtension object fragment:
planet field out of the extension object
fragment and into the Microscope object directly (allowing client
code to behave as if the planet field is intrinsically part
of the Microscope class, without having to worry about where that
field had effectively been stored in the serialised data), or more
generally provide any interface it deems useful to expose the
planet field to client code (this scheme does not mandate any
mechanism for that, especially since such mechanisms are likely to vary
depending on the programming language used);The “extension management” part of this proposed scheme is intended, as its name implies, to fulfil the goal of making extensions manageable as first-class entities.
It relies on a simple data structure (hereafter called the extension
definition structure) that can be used to describe an extension. Such a
structure can then be used in any context where extensions need to be managed.
Notably, this scheme proposes that the top-level object of a data file should
include a extension_manifest key containing a list of extension
definitions for every extension effectively used anywhere else in the data
file (thereby providing implementations with a single place to look at in
order to determine which extensions are needed to fully understand the entire
file).
The same extension definition structure could also serve as the basis for a hypothetical, centrally managed “extension registry”, should such a registry be needed.
As currently envisioned, the extension definition structure would contain the following fields:
id:the unique identifier for the extension (in our running
example, this is https://example.com/swm/);version: the version of the extension used;schemas: a list of schemas that formally describe the
extension;homepage: a link to a page providing human-readable
information about the extension (contrary to schemas which
are intended to provide machine-readable information).Each item in the schemas list is a simple, two-fields
structure like this:
{
"type": "LinkML",
"url": "https://schemas.example.com/swm/swm.yaml"
}where the type field identifies the type of the schema, and
url is a link to the schema itself.
The scheme allows specifying schemas of different types (e.g.
LinkML, JSONSchema, etc.) to avoid binding itself to
a particular schema definition language. This does not mean that (i) all
extensions should always provide a schema for each of the available schema
definition languages (though providing more than one type of schema would
increase the likelihood that an application can understand at least one of the
schemas), and that (ii) applications should support all possible types of
schemas (though supporting more than one type would again increase the
likelihood that the application can understand at least one of the schemas
provided by an extension).
As stated in a preliminary note, the scheme discussed here is intended to be generic. Among other things, this means that some aspects of it might be “customized” when the scheme is actually applied to a data model.
In particular, the scheme involves several special attributes that must be added to the data model that one wishes to make extensible, namely:
In this document, those attributes are named object_type,
extensions, and extension_manifest,
respectively.
Those names may or may not be sensible when the scheme is applied to a particular data model. If they are deemed not to be sensible, it is up to the model designers to pick alternative names as needed.
One may also consider prefixing all those names with some special character
(e.g. ~object_type or %extensions) to highlight the
special roles of those attributes and help distinguish them from all the
other, non-special attributes present in the data model.
The name of the type designator attribute and the name of the attribute
holding the extension object fragments may vary throughout the same model. For
example, the designator attribute could be named object_type for
one natural extension point and simply type in another natural
extension point. We recommend always using the same name, but that may not
always be practical especially when this scheme is applied to a pre-existing
model (rather than a new model designed from scratch). When the name of the
type designator attribute may vary from one extension point to another, it is
up to the model designers to ensure that they always clearly specify which
attribute is the type designator attribute for one particular extension point.
Likewise for the attribute holding the extension object fragments.
This scheme requires three different types of identifiers:
As shown in the running example used in this document, this scheme strongly recommends:
For example, in the case of the SWM extension:
https://example.com/swm/;https://example.com/swm/KyberLightSource;https://example.com/swm/MicroscopeExtension.The first recommendation (using URI as identifiers) is intended to automatically ensure uniqueness of identifiers across all extensions, which is critical to ensure that independently developed extensions cannot interfere with each other.4
It also offers a straightforward way to distinguish between “official” extensions and “third-party” extensions (should such a distinction be desired – this scheme is neutral on that aspect), simply by declaring that all extensions whose base URI is in a given domain are “official”, and all extensions whose base URI is outside of that domain are third-party extensions. The people responsible for managing the data model can then control who should be allowed to create official extensions (again, should such a control be desired) by controlling who can allocate URIs in the “official domain”.
The second recommendation (building all the identifiers for a same extension out of the same base URI) offers a straightforward way to identify the extension a given natural extension class or class extension belongs to, without even having to look at the formal schemas that describe the extension (assuming the extension provides at least one such schema, and the application can understand it).
Both of those recommendations may be relaxed in a particular application of this scheme, but model designers must carefully weigh the consequences. In particular, allowing short, non-URI-based identifiers may produce slightly more “human-friendly” JSON (for as much as JSON can be human-friendly…) but removes all guarantees that independently developed extensions cannot interfere with each other – putting the onus on the people responsible for the model and on extension developers to ensure that it will not happen.
The extensibility scheme currently does not strictly mandate how extensions
should be versioned; it simply provides a way for extensions to
self-describe their own version in the version attribute of
their extension definition.
However, we strongly recommend that all changes to an extension should be constrained in such a way that data that is conformant to version X of an extension remains conformant with X+n.
Should an extension developer wish to create breaking changes in its own extension, the recommended way to do that is to effectively create a distinct extension, with its own base URI that must be different from the base URI of the original extension.
For example, should the SWM extension be updated so that the
planet field added to the Microscope class is entirely
removed and replaced by a new planets field accepting a
list of values rather than a single value (maybe reflecting the facts
that a single microscope may be made of parts manufactured on different
planets), this should be considered a new extension. This could look
like this:
{
"manufacturer": "Imperial Microscope Builders",
"model": "K2SO",
"extensions":
{
"https://example.com/swm/v2/MicroscopeExtension":
{
"planets":
[
"Coruscant",
"Alderaan",
"Chandrila"
]
}
}
"extension_manifest":
[
{
"id": "https://example.com/swm/v2/",
"version": "1.0"
}
]
}That is, why not doing something like this:
{
"manufacturer": "Imperial Microscope Builders",
"model": "K2SO",
"swm:planet": "Coruscant",
"stm:planet": "Vulcan",
"stm:user": "Spock"
}The author of this document strongly believes that such attempts to mimic XML-style qualified names in JSON or YAML are ill-inspired.
Qualified names work in XML because XML has built-in support for them! The concepts of qualified names and of namespaces are a core part of the XML specification, and any XML-compliant library must support them. JSON does not have any such concept (and neither does YAML), which means that applications would either need to hack their own namespace management feature, or accept that such “qualified names” do not in fact constitute a proper scoping mechanism.
“Hacking a namespace management feature” could look like this:{
"namespaces":
{
"swm": "https://example.com/swm/",
"stm": "https://example.net/stm/"
},
"manufacturer": "Imperial Microscope Builders",
"model": "K2SO",
"swm:planet": "Coruscant",
"stm:planet": "Vulcan",
"stm:user": "Spock"
}
but this will never be directly supported by a JSON or YAML parsing library, meaning that applications will need to implement all the namespace management on their own.
Without a custom namespace management system, then “qualified names” are in fact just slightly longer names in a single, global, flat namespace. The presence of a colon does not automagically transform the global namespace into a tree of scoped namespaces. This is in fact no different than this:
{
"manufacturer": "Imperial Microscope Builders",
"model": "K2SO",
"swm_planet": "Coruscant",
"stm_planet": "Vulcan",
"stm_user": "Spock"
}
which does not enclose the extension fields in their own
namespace, it merely gives them longer names – with no intrinsic guarantee
that no other extension developer will ever want to use the same “prefixes”
swm_ or stm_.
This section illustrates how the scheme can be implemented in a data model that is formally defined using LinkML.
This is informative only. The scheme does not _need_ LinkML to be implemented, and even when LinkML is used it could possibly be done differently than what is proposed here.
To create a “natural extension point” in a LinkML schema:
The type designator SHOULD be URI- or CURIE-typed. This scheme currently favours URI-typed designators, since they dispense from having to manage CURIE prefixes.
Some limitations currently exist in LinkML-Py that implementers should be aware of.
The most important is that, for now, instance data containing a type designator value that does not correspond to a known subclass of the class carrying the type designator will be considered invalid. This means that an application that is only aware of the base schema will always reject data containing a “natural extension” – basically making the entire concept of “natural extension” void! Work is in progress with the LinkML community to fix that issue.
Another issue (which may in fact be fixed at the same time as the previous one) is that, when generating Python code, the generated code will not include what is needed to properly recognise type designators when the schema does not contain at least one subclass of the class carrying the type designator (basically, the code generator assumes that, since there are no subclasses, there is no need for a way to designate the effective type at runtime).
Lastly (but much less importantly – it’s more an annoyance than anything else), because of another bug in some components of LinkML-Py, type designators for now should always be defined as global, schema-wide slots, rather than as class-specific attributes, even if the slot is only ever used in one class.
The “light source” extension point in the running example of this document can be implemented in LinkML as follows:
slots:
object_type:
description: The type designator for natural extension points.
range: uri
designates_type: uri
classes:
LightSource:
description: The base class for all light source objects.
slots:
- object_type
attributes:
power:
range: integer
# All other attributes common to all light source types
# ...
extra_slots:
allowed: true
ArcLightSource:
description: Represents a light source that is specifically an arc lamp.
is_a: LightSource
attributes:
# Attributes specific to this type of light source
# ...
LaserLightSource:
description: Represents a light source that is specifically a laser.
is_a: LightSource
attributes:
# Attributes specific to a laser-based light source
# ...
Microscope:
attributes:
light_source:
description:
The light source available on this microscope.
This is the actual extension point.
range: LightSource
# Other attributes for the Microscope class
# ...
To allow classes from the base schema to be extended according to the mechanism described in this scheme, the base schema must:
extensions field to store the extension object
fragments.Here is a possible implementation:
slots:
extension_type:
description: The type designator for all extension nodes.
range: uri
designates_type: true
key: true
alias: type
classes:
IsExtensibleMixin:
description:
An object that can carry arbitrary extension object fragments.
Reuse this mixin in any class to allow the class to be
extended.
mixin: true
attributes:
extensions:
description: The collection of extension object fragments.
range: ExtensionNode
multivalued: true
inlined: true
inlined_as_list: false
ExtensionNode:
description:
Represents a fragment of an object that is defined by an extension
rather than by the base schema.
slots:
- extension_type
extra_slots:
allowed: true
You may notice at this point that the extensions attribute
looks similar to a natural extension point, since its range is set to a class
that has a type designator. This is because it is indeed a natural
extension point! Conceptually, the way this extensibility scheme allows
extending classes is by giving each class (or at least, each class reusing the
IsExtensibleMixin mixin) a “generic” natural extension point.
Applying this to our running example, when we define the
Microscope class we only need to make it inherit from the
IsExtensibleMixin, which will give it the extensions
field intended to store the extension object fragments:
classes:
Microscope:
mixins:
- IsExtensibleMixin
attributes:
# all the attributes for the Microscope class defined in the
# base model...
If the base model has a natural extension point (such as the
light_source field in the Microscope class we have seen
above), then creating a natural extension intended to be used at that point is
simply a matter of:
For example, to create the KyberLightSource extension:
classes:
KyberLightSource:
description:
A light source that uses a Kyber crystal; allows to see things
through the Force, but can only be used by Jedi microscopists.
is_a: LightSource
attributes:
# Attributes that specifically describes this type of light source
# ...
Of note, the class does not need to derive directly from the base, it can derive from another subclass if it happens to be more appropriate.
This is how the https://example.com/swm/MicroscopeExtension
extension object fragment (which aims to add a planet field to
the base Microscope class) could be implemented:
classes:
MicroscopeExtensionMixin:
description:
A mixin that contains the attributes we want to extend the
Microscope class with.
This is a mixin in order to facilitate extension composability.
mixin: true
attributes:
planet:
description: The planet where the microscope was manufactured.
MicroscopeExtension:
description:
The actual extension object. This (i) inherits from ExtensionNode
(so that it is recognized as an extension object fragment) and
(ii) reuses the MicroscopeExtensionMixin above.
is_a: ExtensionNode
mixins:
- MicroscopeExtensionMixin
ExtendedMicroscope:
description:
This is a subclass of the base Microscope class with the added
MicroscopeExtensionMixin.
This class is not, strictly speaking, needed for the extension
scheme described in this document, but its presence will allow
an application to support the MicroscopeExtension “natively”,
by using this class whenever the use of the Microscope class is
expected.
is_a: Microscope
mixins:
- MicroscopeExtensionMixin
System.Reflection.Emit system in the .NET framework). We do
not consider such cases here.