Release 23nd September 2025
Change Control note Hiperspace --version 2.4.6 HiLang --version 2.4.6
Overview
This release introduces three new features for references to views, the Any
polymorphic union type and Get<TEntity>(string sid)
function. Together they provide the Hiperspace.DB
functionality to combine any number of Hiperspaces in a single Graph view without duplication or copying.
View References
Views in Hiperspace are generated to combine all entities, segments, aspects and views that can be projected as the view, but also provide an additional role for sub-types and complex associations.
All references in Hiperspace
can be navigated to either elements key or an index that matches the predicate used to reference it. The last release completed the optimization of view access to parts, this release adds indexing to efficiently access view items with criteria other than the key.
Indexed views
Consider the example of a Trading problem where Trades are booked in a Book, but each trade type {Fixed Income, Forex, Equity} can have specific properties for the product traded. When the view is referenced by a member other than the key (Book in this example), an index is created to efficiently access the set of matching values.
view Banking.Trade (Id : String) {Book : Banking.Book};
entity Banking.FI.Trade : Banking.Trade = Banking.Trade();
entity Banking.FX.Trade : Banking.Trade = Banking.Trade();
entity Banking.EQ.Trade : Banking.Trade = Banking.Trade();
entity Banking.Book (Id : String) [Trades : Banking.Trade (Book = this)];
Trades : Trade (Book = this)
selects the set of Trade that have a reference to this
Book. This access causes an index to be created in the view Trade
that is inherited by each implementation.
Sub type
This UML diagram represents the logical view of the .hilang
schema above. When a relational database is used, one of three strategies are normally followed:
- Direct: Each entity {Trade, FI_Trade, FX_Trade, EQ_Trade} is mapped separately, and joined as needed
- Down denormalization: Properties of the parent are duplicated in each of the sub-types and with context specific joining
- Up denormalization: Properties of each sub-type are added to the base type (optionally with aliases for duplicate names)
Hiperspace does not need denormalization of the elements because types can inherit from a base type. The code entity FX_Trade : Trade = Trade;
states that the entity FX_Trade inherits(:) from Trade and can be viewed(=) as a Trade
In the example above, each sub-type will be queried in parallel using the index on the Book reference
Modelled as Aspect
The schema could also have been modelled using aspect rather than realization, depending on business requirement, e.g.
- Convertible where final exchange is either equity or cash
- Hybrid where cashflow is calculated from either from a yield-curve or equity price
- Participation where the equity price uses FX exchange rates
entity Banking.Trade (Id : String) {Book : Banking.Book}
[FI : Banking.FI.Trade, FX : Banking.FX.Trade, EQ : Banking.EQ.Trade];
aspect Banking.FI.Trade;
aspect Banking.FX.Trade;
aspect Banking.EQ.Trade;
entity Banking.Book (Id : String) [Trades : Banking.Trade (Book = this)];
Aspects appear to be properties of the owning entity, so the choice has minimal impact on usage ( Banking.FI.Trade { YieldCurve = ... }
vs Banking.Trade { FI = new Banking.FI.Trade { YieldCurve = ...}}
)
Segment / Aspect reference
Segments and Aspects are owned by the entity, segment, aspect
that references them as an extension. They appear (to users) as a set property for segments, and value property for aspects, with the owner
added transparently when a value is assigned.
entity Customer [Accounts : Has];
segment Has (Account : Account);
entity Account [HasCustomer : Has (Account = this)];
HiLang
translates the segment Has
into an element CustomerHas
(adding the key owner : Customer
), and the segment Has
is transformed into a view that CustomerHas
provides. If there are no other implementations of Has
(e.g. for a sub-account of Account) the view is pruned and not generated.
In this example Has
is referenced by Account
to provide a link from Account
to Customer
using the HasCustomerIndex
. As a segment
/aspect
can be applied to any stored element, Has
view includes the key owner : Any
which in this case can be cast to Customer
.
Any
The new type Any
has been introduced to support segment
/aspect
views, and can be converted to any of the domain types like a union
in F#. Unlike object
, Any
can be serialized and used anywhere in the schema, when a generic reference is required. Any
is code generated by HiLang
to include a constructor for the each of the domain types, together will cast operators to convert to each of the domain types. Elements also include a cast operator to convert to an Any
when assigned.
Any
uses constructors and conversion operators to behave like a base type for any domain element Any value = customer
will use Customer’s operator Any function, Customer? value = any
will use Any’s operator Customer function to return the value or null
. Explicit cast functions are created for each element that could be stored.. for the Customer type it includes
- Customer() : cast the Any to a customer or null
- CustomerAsync() : async function to return the value (especially useful for WebAssembly where network read may be required)
Any
includes the functions
Type AnyType()
to inspect the content of theAny
class for match/switch statementbool Is<T>()
: returns true if the content of theAny
is of typeT
T? As<T>()
: returns the value of theAny
cast to the typeT
Task<T?> AsAsync<T>()
: returns the value of theAny
cast to the typeT
using an async network call if necessary
Get(string sid)
SubSpace
provides an object? Get (string sid)
function that can be used to get an element in Hiperspace without knowing the type, just using the stringified key of the element. This function is now obsolete and has been replaced by TEntity Get<TEntity>(string sid)
that allows the desired type to be retrieved.
This method was added for Hiperspace.DB
that includes GraphSpace
to aggregate Node
/Edge
/HiperEdge
across domains. For GraphSpace
it is necessary to call Get()
with the SKey
of a Node
and then cast to Node
which was not possible with object
because elements do not need be viewable as a Node
. GraphSpace
uses Get<Node>(skey)
for the lookup.