-
Notifications
You must be signed in to change notification settings - Fork 1
Templates
Templates are blocks of Datalog code that can be instantiated multiple times and connect with other template instantiations. They provide a way to split a Datalog program into module-like parts. Template code can only see parts of the environment that are passed as parameters to the template during instantiation. On the opposite direction, template code is visible to anyone on the outside.
template Simple {
Z(x) : int
P(x) : string
Q(x) <- Z(x), P(x)
}
A template may extend another template. Extending a template has the semantics of simply including its contents in the second template. Currently, templates cannot be nested.
template Base {
P3(x) <- Q(x)
}
template Core : Base {
P4(x) <- P3(x)
}
The above snippet is equivalent to the following
template Core {
P3(x) <- Q(x)
P4(x) <- P3(x)
}
Instantiations of components happen using the as
keyword.
Simple as Step1, Step2
Instantiating a component has the semantics of cloning every Datalog clause it contains, differentiating them by qualifying them with the instantiation id. In the above snippet, instantiating component Simple
as Step1
is semantically equivalent to the following
Step1.Z(x) : int
Step1.P(x) : string
Step1.Q(x) <- Step1.Z(x), Step1.P(x)
Step2.Z(x) : int
Step2.P(x) : string
Step2.Q(x) <- Step2.Z(x), Step2.P(x)
Templates may access code from other instantiations through their parameters.
template Base <X> {
P1(x) <- X.P(x)
P1(x) : string
}
template Core <T, G> : Base <G> {
P2(x) <- G.Q(x, _)
P3(x) <- G.Q(_, x)
W(x) <- T.Z(x)
M(x) <- P3(x), !W(x)
M(x) <- W(x), x>1000
}
In the snippet above, T
and G
are parameters of template Core
that abstractly refer to other instantiated templates. Relations and types from those parameters are accessed via qualifying them with the parameter name (e.g. G.Q
).
Parameter passing happens during template instantiation. As a convention, the global scope can also be passed as a template parameter with the name _
.
Base<_> as S1
Simple as S2
Core<S2, _> as S3
Types have similar behavior to relations. They can be referred via template parameters, and types declared in a template are visible to the outside world.
@type Shape
template Test<B> {
Z(x) <- B.Q(x)
@Type Animal
@type Circle : A.Shape
ZZ(x, y) : int, A.Shape
}
Test<_> as T1
Bar(x, y) : int, T1.Animal
@type Cat : T1.Animal
In general, template code is read-only for the outside world. Relations and types can be used in other places, but no outside declarations or rules are allowed to inject anything to a template. The following rule for relation W
is valid.
template Simple {
Z(x) : int
P(x) : string
Q(x) <- Z(x), P(x)
}
Simple as S1
W(x) <- S1.Q(x)
But the following are invalid usages.
@type S1.Animal // ERROR
S1.P(x) <- Z(x) // ERROR
An exception is the @output
annotation that is allowed to appear in global scope while referring to a relation declared in a template.
Simple as S1, S2, S3, S4
@output S3.Q