You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There are often times when it would be useful to be able to collect interface implementations (attributes owned and roles played) into high-level abstractions. In many data models, this can result in very large reductions in the complexity of schema definitions and queries, and increased clarity in the same. This also closes the gap between the capabilities of TypeDB and OOP, further reducing object model mismatch.
Current languages capabilities
Consider the following schema excerpt.
define
person sub entity,
abstract,
owns name;
racing-driver sub person,
plays car-ownership:owner,
owns licence-number,
owns penalty-points,
owns competitive-rank,
owns nationality-represented;
courier sub person,
plays car-ownership:owner,
owns licence-number,
owns penalty-points;
dentist sub person,
owns licence-number;
car-dealer sub person,
plays car-ownership:owner;
chess-player sub person,
owns licence-number,
owns penalty-points,
owns competitive-rank,
owns nationality-represented;
In this schema, if we want to query for people who can drive, we must use one the following or similar:
match
$p isa $career;
{
$career type racing-driver;
} or {
$career type courier;
};
not { $p has penalty-points >= 12; };
fetch
$p: name;
match
$p isa $career;
$career plays car-ownership:owner;
$career owns licence-number;
$career owns penalty-points;
not { $p has penalty-points >= 12; };
fetch
$p: name;
Neither is a good solution. The first is vulnerable to false negatives when new careers are introduced that allow driving, requiring those new careers be added to the query. The second is vulnerable to false positives if a career involves the same interfaces implemented, which would (in the worst case) require ownership of a dummy attribute type-is-driver to indicate that the career is one that allows driving. Grouping racing-driver and courier under a driver supertype is not a good move, as we might want to do other groupings like racing-driver and chess-player under a competitor supertype. This is a classic use case for composition over inheritance, but the composition capabilities of TypeQL are currently limited.
Proposed feature
These problems could be solved by allowing the composition of interface implementations into high-level abstractions, called "traits" akin to those in many OOP languages. A new root type trait is proposed. Its subtypes would always be abstract and able to implement interfaces as object types can. A trait could then be implemented by an object type using a new implements keyword. This would cause the object type to implement all interface implementations defined on the trait.
In the following example, we define and then implement driver and competitor as traits.
define
person sub entity,
abstract,
owns name;
driver sub trait,
plays car-ownership:owner,
owns licence-number,
owns penalty-points;
competitor sub trait,
owns competitive-rank,
owns nationality-represented;
racing-driver sub person,
implements driver,
implements competitor;
courier sub person,
implements driver;
dentist sub person,
owns licence-number;
car-dealer sub person,
plays car-ownership:owner;
chess-player sub person,
implements competitor,
owns licence-number,
owns penalty-points;
Now, in order to query for people who can drive, we can use the following.
match
$p isa $career;
$career implements driver;
not { $p has penalty-points >= 12; };
fetch
$p: name;
Or even the following, by casting of object types into trait types.
match
$p isa driver;
not { $p has penalty-points >= 12; };
fetch
$p: name;
With very large schemas, it can be imagined how this feature would reduce verbosity of schemas and queries significantly, as well as increasing the expressivity of the language.
Additional benefits in TypeDB 3.0
With the introduction of more powerful annotations, traits would be even more useful.
define
driver sub trait,
plays car-ownership:owner,
owns licence-number @card(1),
owns penalty-points @default(0);
match
$p isa driver, has penalty-points < 12;
fetch
$p: name;
Even more so after the introduction of query templates.
define
fun can-drive(person: $p) -> bool:
$p isa driver, has penalty-points < 12;
return; check;
match
can-drive($p);
fetch
$p: name;
Challenges remaining
There remain challenges. Obviously, the syntax suggested is only a first pass, but there would be additional complexity introduced to the language regardless of the final syntax. This would lead to syntax bloat, but the gain here would likely outweigh the cost.
More difficult, some ambiguous situations can be envisioned. For instance, how would an object type that implemented two traits be treated, if those two traits implement the same interface with conflicting annotations (e.g. cardinality).
The text was updated successfully, but these errors were encountered:
Overview
There are often times when it would be useful to be able to collect interface implementations (attributes owned and roles played) into high-level abstractions. In many data models, this can result in very large reductions in the complexity of schema definitions and queries, and increased clarity in the same. This also closes the gap between the capabilities of TypeDB and OOP, further reducing object model mismatch.
Current languages capabilities
Consider the following schema excerpt.
In this schema, if we want to query for people who can drive, we must use one the following or similar:
Neither is a good solution. The first is vulnerable to false negatives when new careers are introduced that allow driving, requiring those new careers be added to the query. The second is vulnerable to false positives if a career involves the same interfaces implemented, which would (in the worst case) require ownership of a dummy attribute
type-is-driver
to indicate that the career is one that allows driving. Groupingracing-driver
andcourier
under adriver
supertype is not a good move, as we might want to do other groupings likeracing-driver
andchess-player
under acompetitor
supertype. This is a classic use case for composition over inheritance, but the composition capabilities of TypeQL are currently limited.Proposed feature
These problems could be solved by allowing the composition of interface implementations into high-level abstractions, called "traits" akin to those in many OOP languages. A new root type
trait
is proposed. Its subtypes would always be abstract and able to implement interfaces as object types can. A trait could then be implemented by an object type using a newimplements
keyword. This would cause the object type to implement all interface implementations defined on the trait.In the following example, we define and then implement
driver
andcompetitor
as traits.Now, in order to query for people who can drive, we can use the following.
Or even the following, by casting of object types into trait types.
With very large schemas, it can be imagined how this feature would reduce verbosity of schemas and queries significantly, as well as increasing the expressivity of the language.
Additional benefits in TypeDB 3.0
With the introduction of more powerful annotations, traits would be even more useful.
Even more so after the introduction of query templates.
Challenges remaining
There remain challenges. Obviously, the syntax suggested is only a first pass, but there would be additional complexity introduced to the language regardless of the final syntax. This would lead to syntax bloat, but the gain here would likely outweigh the cost.
More difficult, some ambiguous situations can be envisioned. For instance, how would an object type that implemented two traits be treated, if those two traits implement the same interface with conflicting annotations (e.g. cardinality).
The text was updated successfully, but these errors were encountered: