More screenshots for docs

This commit is contained in:
Fedor Isakov 2018-08-23 12:29:16 +02:00
parent 4fc5c0e12a
commit 6085481ede
38 changed files with 93 additions and 329 deletions

View File

@ -5,10 +5,11 @@ weight: 40
---
# Evaluating Code Rules
![](img/errorDialog.png) **TODO two stage process**
***Two stage process***
## Aspects
![](img/errorDialog.png) **TODO aspects**
***Aspects***
## Applying rule templates
@ -24,17 +25,18 @@ Generated productions constitute the constraints program, which is then executed
In the second stage the constraints program is run. To begin execution, a query is selected, which contains the list of constraints to be activated. Query can be viewed as a regular production, which is triggered unconditionally right after all headless productions (marked as «on start») have fired.
![](img/errorDialog.png) **TODO order of productions firing**
***Order of productions firing***
Order of productions fired:
- on start
- query
While constraints program is run, it is allowed to report feedback, such as assign calculated types or report problems, using special predicates.
![](img/errorDialog.png) **TODO feedback predicates example**
***Feedback predicates example***
Failures during evaluation are caught with the help of alternative body branches, where those are provided. An uncaught failure terminates program execution and is reported to the user.
## Activation trace view
![](img/errorDialog.png) **TODO activation trace view**
***Activation trace view***

View File

@ -8,4 +8,4 @@ weight: 510
# Control Flow Analysis
![](img/errorDialog.png) **TODO**
***Contents***

View File

@ -15,152 +15,61 @@ For purposes of keeping this sample small, we keep the language confined to bool
The dataform table contains obvious declarations for the only primitive type `bool`, the functional `fun` type, and universal type `forall`. The macros table responsible for constructing dataform instances is trivial.
```
DataForm Table terms
Bool (
<no features>
)
Fun (
child arg
child res
)
Forall (
child type
)
```
![](img/ex-stlc-dataformtable-300.png)
_(dataform table)_
There is only one query of kind `TYPECHECK`, which launches types recovery. All the type checking is done by the automatic productions «on start».
Handlers are separated into types recovery, operations with universal type, and the rest, which is assigning types to expressions. Type checking follows the programs syntactic structure.
```
typeOf_LamVarBind matching LamVarBind lvb <with subconcepts> <always apply>
{
on <term T> start
activate
%%
// T is free
<% typeOf(@lvb.var, T) %>
%%
}
```
![](img/ex-stlc-lambdavar-600.png)
_(typing rule for lambda variable binding)_
A variable introduced by lambda abstraction is assigned a type, which is a fresh logical variable.
```
typeOf_Lam matching Lam lam <with subconcepts> <always apply>
{
on <term T, term A, R>
typeOf(@lam.binding.var, A), typeOf(@lam.expr, R)
activate
T := call Fun<>, T = Fun(arg: A res: R), typeOf(@lam, T)
}
```
![](img/ex-stlc-lambdaabs-500.png)
_(typing rule for lambda abstration)_
A lambda expression is assigned a function type. This rule is a direct translation of the theoretic rule for lambda abstraction.
```
typeoOf_App matching App app <with subconcepts> <always apply>
{
on <term F, A, term FA, FR, FI, AI, string E>
typeOf(@app.fun, F), typeOf(@app.arg, A)
activate
inst(FI, F), FI = Fun(arg: FA res: FR), inst(AI, A), FA = AI, typeOf(@app, FR)
else
eval(app.report error(String.format("cannot unify '%s' and '%s'", valueOf(FA), valueOf(A))))
}
```
![](img/ex-stlc-app-700.png)
_(typing rule for application expression)_
Similarly, the type of an application expression is pretty much follows the standard textbook form with a notable addition of the `else` branch: this is the way errors are caught in case unification fails. Here, `inst` constraint ensures a universal type is unwrapped and the inner type dataform is copied, so that all free variables in the resulting type are fresh.
```
typeOf_IfThenElse matching IfThenElse ite <with subconcepts> <always apply>
{
on <term C, P, N>
typeOf(@ite.cond, C), typeOf(@ite.pos, P), typeOf(@ite.neg, N)
activate
C = Bool(), P = N, typeOf(@ite, P)
else
eval(ite.report error("mismatched type"))
}
```
![](img/ex-stlc-ifthenelse-650.png)
_(typing rule for if-then-else)_
The type checking for `if-then-else` ensures that the types of both branches unify, and assigns the resulting unified type to the whole expression.
```
typeOf_Fix matching Fix fix <with subconcepts> <always apply>
{
on <term F, A> start
activate
F = Forall(type: Fun(arg: Fun(arg: A res: A) res: A)), typeOf(@fix, F)
}
```
![](img/ex-stlc-fix-650.png)
_(typing rule for fix operator)_
Finally, the `fix` operator, which represents general recursion, is given the type `forall.(a -> a) -> a`.
A separate handler is dedicated to producing and instantiating universal type instances, which are represented by `Forall()` dataform.
```
gen <no input> <always apply>
{
on <term G, T>
~gen(G, T)
when
isFree(G)
activate
G = Forall(type: T)
}
```
![](img/ex-stlc-gen-300.png)
_(rule producing forall type)_
This production assigns the output parameter `G` a new type universal type wrapping the `T` parameter.
```
inst_forall <no input> <always apply>
{
on <term I, G, T, term C>
~inst(I, G = Forall(type: T))
when
isFree(I)
activate
C == copyOf(T), I = C
}
```
![](img/ex-stlc-instforall-350.png)
_(rule instantiating forall type)_
The constraint `inst` is responsible for unwrapping (instantiating) an universal type. Here the `copyOf()` is a call to internal API, which makes a copy of the term passed as parameter, ensuring all logical variables within it are replaced with fresh ones.
The handler `recover` is responsible for translating the calculated types to `SNode` form and is pretty straightforward.
```
recover_var <no input> <always apply>
{
on <node<> Node, term Var, string Name>
~recover(Node, Var),
varname(Var, Name)
when
isFree(Var)
activate
Node == `<$( VarType Names.asName(Name) )$>`
}
```
![](img/ex-stlc-recovervar-450.png)
_(recover var type)_
The constraint `varname` is used to track the names of type variables, so that the resulting types have the form `t1`, `t2`, and so on.
```
recover_var_assign <no input> <always apply>
{
on <node<> Node, term Var, string Name>
~recover(Node, Var)
when
isFree(Var)
activate
Name == `Names.nextIndex()`, varname(Var, Name), Node == `<$( VarType Names.asName(Name) )$>`
}
```
![](img/ex-stlc-recovervarassign-500.png)
_(recover var type assigning name)_
The above production ensures all free logical variables representing type variables are assigned unique name.
[^hm]: See for example: Cardelli, Luca. "Basic polymorphic typechecking." Science of computer programming 8.2 (1987): 147-172.
[^tclc]: Type Checking Lambda Calculus: https://github.com/fisakov/typechecking-lambdacalc
[^tclc]: Type Checking Lambda Calculus: [https://github.com/fisakov/typechecking-lambdacalc](https://github.com/fisakov/typechecking-lambdacalc)

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -9,9 +9,8 @@ This project is an attempt to bring logic programming to JetBrains MPS[^mps] to
Type system in MPS is traditionally defined with help of type checking rules, in particular inference rules, which allow to make logical statements about types, such as «*is a*» or «*is a subtype of*», enabling the internal engine to infer the specific type based on a collection of such statements, referred to as type equations and inequalities.
```
typeOf(assignment.right) :<=: typeof(assignment.left)
```
![](img/intro-assignment-550.png)
_(statement in `j.m.lang.typesystem` language)_
Albeit brief and concise, this notation leaves many questions unanswered when it comes to how exactly the system of equations and inequalities is processed. In other words, type inference is — for the most part — left up to the internal engine to decide. This limits the options for the author of type system to control how exactly subtyping is defined, and what happens with type parameters when computing sub- or supertype. Java, for instance, has several kinds of «conversions» with clearly defined rules controlling how types are transformed and what types are compatible in certain situations. All of this has to be emulated with «strong» and «weak» subtyping in MPS.
@ -19,14 +18,8 @@ Another example is the all too well known *when_concrete*, which is basically a
Consider how the type of a method call is calculated: the details aside, in essence *when_concrete* has to be applied to the type of each argument. Then we should either turn to inequalities and rely on the inference engine, or analyse the type structure and run closing computation when the *last* unknown type is finalised.
```
foreach arg in methodCall.arguments {
when_concrete (typeof(arg) as A) {
// analyse the type of arg, such as extract its parameters, etc
// when all argument types are known, infer the type of call
}
}
```
![](img/intro-methodcall-550.png)
_(example of processing method arguments)_
Code rules may have a solution to these and other issues. The core idea is to employ a **production system** to process facts and relations, collectively known as constraints, with the user being in full control of what productions to generate for given source code (model). With the ability to use logical variables to represent unknowns, and with support for term algebra and unification, it is pretty straightforward to define the core of type inference or similar framework without having to rely on opaque implementation and pre-defined relations.
@ -41,5 +34,5 @@ A framework for evaluating a constraints program, enabling reporting of problems
Finally, an embedded engine capable of processing constraints, which accepts a list of productions, an initial active constraint, and, optionally, a store of inactive constraints, and yields an updated store after all matched productions have been fired.
[^mps]: [https://jetbrains.com/mps](https://jetbrains.com/mps)
[^mps]: Meta Programming System [https://jetbrains.com/mps](https://jetbrains.com/mps)
[^chr]: Constraint Handling Rules [http://www.informatik.uni-ulm.de/pm/fileadmin/pm/home/fruehwirth/constraint-handling-rules-book.html](http://www.informatik.uni-ulm.de/pm/fileadmin/pm/home/fruehwirth/constraint-handling-rules-book.html)

View File

@ -5,7 +5,7 @@ weight: 30
---
# Code Rules Language
![](img/errorDialog.png) **TODO This section should teach how to use the language**
***This section should teach how to use the language***
Root concepts should be contained by a language aspect model importing the language `jetbrains.mps.lang.coderules`. For example, language `jetbrains.mps.lang.typechecking` defines aspect *types* to contain code rules related to type checking.
@ -28,31 +28,13 @@ The aim of rule templates is again twofold: firstly, they may serve as regular
The following example is from the experimental *control flow* aspect for baseLanguage. It demonstrates how a production is constructed using a template. A template is enclosed into a pair of `%% … %%` symbols and yields constraints wrapped into special `<% … %>` brackets. In this particular case `write()` constraint is optional and is only added to the body of the production in case the condition is satisfied (a location is written to, only if the local variable has an initialiser).
```
write_localVarDecl matching LocalVariableDeclaration lvd <with subconcepts> <always apply>
{
on start
activate
%%
<% loc(@lvd) %>
if (lvd.initializer.isNotNull) {
<% write(@lvd, @lvd) %>
}
%%
}
```
![](img/language-writelocal-750.png)
_(example of code rule template)_
A handler is a concept in language `jetbrains.mps.lang.coderules`.
```
handler CheckAll extends TypeOf
checkAll() / 0
<< … >>
```
> (example of a handler declaration)
![](img/language-handlerhead-300.png)
_(example of a handler declaration — without contents)_
Keyword `extends` allows to specify another handler, which is extended by this one. All constraint productions generated by rules in this handler will have higher priority. This enables to override the original handlers behaviour.
@ -60,49 +42,24 @@ Only declared constraints are allowed to be used in the heads of productions in
Rule templates can specify applicable concept, either exactly or including its subconcepts. One can also declare *standalone* templates, which are triggered automatically on every invocation of constraints program.
```
checkLocalVariable matching VariableDeclaration lvd <with subconcepts>
apply if { lvd.initializer.isNotNull; }
{
on <term VariableType, InizrType>
typeOf(@lvd, VariableType), typeOf(@lvd.initializer, InizrType)
activate
convertsTo(InizrType, VariableType)
}
```
> (rule template matching concept instances with condition block)
![](img/language-vardecl-600.png)
_(rule template matching concept instances with condition block)_
A condition, is specified, is checked before applying the rule.
```
hasBound_captureOf <no input> <always apply>
{
on <term CapOfType, CapUBnd, Bnd>
~hasBound(CapOfType = captureOfType(ubound: CapUBnd), Bnd)
activate
convertsTo(CapUBnd, Bnd)
}
```
> (standalone rule template without input)
![](img/language-hasbound-550.png)
_(standalone rule template without input)_
The contents of a rule template is a block of code that gets executed when the template is applied to the source model. If the input is specified, the declared parameter is available in the body. When no input is specified, the rule becomes «standalone» and is automatically executed once per every application of templates.
![](img/errorDialog.png) **TODO what other features are available aside from constraint productions?**
***What other features are available aside from constraint productions?***
Constraint productions can be created at any place within the body of a rule template. A production has three main parts called head, guard, and body. The head defines what constraint trigger this production, and it can only contain constraints defined by the handler or one of the handlers it extends. The body can contain any visible constraints, as well as *predicates*. A production must include either the body or the head, no production can omit both. The guard is optional, and it can only contain predicates.
Both the head and the body can declare *logical variables*. By default a logical variable ranges over *terms*, although any POJO may be serve as a value.
```
on <term S, T>
~compatibleWith(S, T)
activate
convertsTo(S, T)
```
> (constraint production declaring logical variables)
![](img/language-compatibleWith-300.png)
_(constraint production declaring logical variables)_
A production with an empty head, that is not declaring any constraints to serve as the input, is considered an *automatic* production and is triggered automatically on start of constraints program execution.
@ -112,25 +69,13 @@ A production is triggered when there are constraint occurrences matching all con
Pattern matching is also possible with terms, optionally containing logical variables, serving as patterns. For this to work, a logical variable that serves as a constraint argument should have a pattern attached.
```
on <term Capture, LBnd, From>
~convertsTo(From, Capture = captureOfType(lbound: LBnd))
activate
convertsTo(From, LBnd)
```
> (production with pattern matching)
![](img/language-convertsto-500.png)
_(production with pattern matching)_
Everywhere where locations in the source code (model) must be referenced, node references are used, which are introduced with the `@` symbol.
```
on <term Condition, term BoolType>
typeOf(@ifs.condition, Condition)
activate
BoolType := call booleanType<>, convertsTo(Condition, BoolType)
```
> (example of a constraint argument referring to a location)
![](img/language-typeof-550.png)
_(example of a constraint argument referring to a location)_
Whereas the productions head can only contain constraints, there are also other logical clauses that are used in the guard and the body.
@ -138,87 +83,47 @@ A *predicate* is a built-in construct that serves two purposes: when used in the
Productions guard can only contain predicates, and the body can contain both constraints and predicates.
```
on <term S, T>
~containedIn(S, T)
when
isBound(S), isBound(T), S = T
activate
S = T
```
> (`unifies` predicate used in the guard and in the body)
![](img/language-unify-300.png)
_(`unifies` predicate used in the guard and in the body)_
Arbitrary Java code can be called with `eval()` predicate. It accepts either an expression of `boolean` type, in which case it can be used in the guard, or expression of any type, such as static method call, which can be inserted in the body.
```
on <node<> Node, term Type>
recoverAll(), typeOf(@op, Type)
activate
recover(Node, Type), eval(op._type_.set(valueOf(Node)))
```
![](img/language-recover-500.png)
_(example of using `eval()` predicate)_
> (example of using `eval()` predicate)
***Expand/call pseudo predicates***
![](img/errorDialog.png) **TODO Expand/call pseudo predicates**
***Code templates within production body***
![](img/errorDialog.png) **TODO Code templates within production body**
![](img/errorDialog.png) **TODO Alternative body**
***Alternative body***
#### Query
![](img/errorDialog.png) **TODO**
Query is the entry point to a constraint program. The only purpose of a query is to activate constraints that trigger computation defined by productions created by handlers.
```
query Typecheck
parameter: << ... >>
kind: TypecheckingQueryKind.TYPECHECK
check
on start
activate
checkAll(), recoverAll()
```
![](img/language-query-350.png)
_(example of a query)_
A query is discovered by the API through its *kind*. Query kind specifies parameters that can be passed to it. The body of a query can contain same constraints and predicates as regular constraints production.
#### Macro table
![](img/errorDialog.png) **TODO**
Macros allow to have parameterised constructors for terms. They can either create terms using nodes coming from the source model, or use the parameters to fill in values. By contrast with rule templates, macros are always explicitly invoked.
This feature may come in handy when data types used by the constraint program are complicated and include many levels of containment. For example, type checking with *code rules* relies on types being terms, which means types can contain other types.
Macros are defined in a root `Macro table`.
Macros are defined in a root `Macro table`. Its contents is made of *macro definitions*.
```
macro table Types
macro classifierType expands node<ClassifierType> classifierType
node<Classifier> cls1 = classifierType.classifier;
nlist<> arguments = classifierType.parameter;
produce <term DefaultParams[cls1.typeVariableDeclaration.size]>
%%
%%
```
> (macro table with a macro definition)
![](img/language-macro-500.png)
_(macro definition in a macro table)_
Macro *expands* instances of specified concept. This means the macro can be invoked on an instance of this concept. In case macro is *expanded*, its parameters are automatically initialised to appropriate values. Otherwise, a macro can be directly *called* with parameters specified explicitly.
![](img/errorDialog.png) **TODO expand/call from production body**
***Expand/call from production body***
Macro body is a code template, with every constraint or predicate produced from this template being inserted into the production body at the invocation site. There is a special logical variable available within macro body, `macroLogical`, which captures the logical variable on the left side of `expand` or `call` predicate.
![](img/errorDialog.png) **TODO substitutions**
***Substitutions***
#### DataForm table
@ -226,24 +131,12 @@ DataForm table contains *data form* declarations, a.k.a. *terms*. A term is a si
Data form is defined by its symbol and its features, which can be a POJO, a child data form, or a list of data forms. A feature may have a default value provider. A data form extending another data form adds new features or provides default values for existing ones.
```
classifierType (
value classifier
value kind
list parameter
)
```
![](img/language-classifiertype-300.png)
_(example of a dataform definition for classifier type)_
```
longType : primType (
final value kind = concept/LongType/
value val = 0L
)
```
> (examples of *data form* declarations)
![](img/errorDialog.png) **TODO terms**
![](img/language-longtype-350.png)
_(examples of dataform definition for long type)_
***Terms***
[^dft]: this concept is defined in another language, `jetbrains.mps.logic`

View File

@ -15,15 +15,8 @@ This phase runs in «read action», therefore blocking potential writes, which m
The second phase is evaluation of the constraints program that was created in phase one. The evaluation starts with a query, which serves as an entry point to the program. For example, type system defines `TYPECHECK` and `CONVERT` queries, aimed at running type checking and testing if a type can be converted to another type, correspondingly. Queries are declared in the same aspect model.
```
query ConvertsTo
parameter: node<> from, node<> to
kind: TypecheckingQueryKind.CONVERT
on <term A, B> start
activate
A := expand from, B := expand to, convertsTo(A, B)
```
![](img/overview-convertsto-500.png)
_(example of a query)_
In the above example the query defines two logical variables (A and B) of type `term`, which serve to represent types internally. First, both query parameters `to` and `from` are *expanded*, meaning that their `SNode` representations are converted to terms, and then the constraint `convertsTo(A, B)` is activated, kicking off the process of evaluating the program. In case productions triggered by `convertsTo()` constraint all evaluate to true, the query is deemed successful, and if there is at least one production that evaluates to false, the query fails accordingly.
@ -31,37 +24,22 @@ One nice feature of using code rules is the ability to abstract away from type s
Having an internal representation for types also means, that if type system is required to represent types as instances of `SNode` to the user, this has to be addressed by the query design. For instance, a type checking query may consist of two constraints:
```
on start
activate
checkAll(), recoverAll()
```
![](img/overview-check-300.png)
_(example of a production)_
Here, the first constraint `checkAll()` fires type checking, whereas the second `recoverAll()` is responsible for restoring terms to `SNode` instances and reporting them back to the user. Joining the two constraints with a conjunction establishes the order in which these are evaluated.
An of course, if something can go wrong, it will. In case type inference is unsuccessful, the second stage has no chance of being evaluated. To account for that, a partial backtracking was added to the language of production templates, which helps recover from certain failures.
```
on start
activate
checkAll()
else
recoverAll()
```
![](img/overview-check2-300.png)
_(example of a production with alternative body branch)_
Here, `recoverAll()` constraint is moved out to an «*alternative branch*» of production body, which allows it to be activated even if there was an error while processing the main branch.
To illustrate how automatic binding of logical variables work, consider the following example. The constraint `typeOf()` associates a type with a location in source code, and `convertsTo()` ensures its 1st argument can be converted to the 2nd, which must both be types.
```
assignmentExpression matching AssignmentExpression ae <with subconcepts> <always apply>
{
on <term LType, RType>
typeOf(@ae.lValue, LType), typeOf(@ae.rValue, RType)
activate
convertsTo(RType, LType)
}
```
![](img/overview-assignment-700.png)
_(example of rule template)_
The production is triggered when both locations referred to by `ae.lValue` and `ae.rValue` have their types assigned, as both `typeOf()` constraints must be present for a match to be successful. Once productions head is matched, both logical variables `LType` and `RType` become bound to whatever was the 2nd argument of first and second `typeOf()` constraint, respectively.
@ -69,12 +47,5 @@ Its important to note, that although on successful match both `lValue` and `r
The following example illustrates the use of *pattern matching* in productions head. Here the first argument to constraint `convertsTo()` in the head is a logical variable with pattern expression. In this case the production will only be triggered if the active constraints first argument is not a free variable, and it matches the pattern.
```
converts_captureOf_upperBound <no input> <always apply>
{
on <term Capture, UBnd, To>
~convertsTo(Capture = captureOfType(ubound: UBnd), To)
activate
convertsTo(UBnd, To)
}
```
![](img/overview-converts-500.png)
_(example of rule template)_

View File

@ -1,11 +1,12 @@
---
layout: page
title: Processing Constraints
title: Constraints Processing System
menu: Processing Constraints
weight: 50
---
# Constraints processing system
# Constraints Processing System
![](img/errorDialog.png) **TODO Discuss how constraints processing works (abstractly)**
***Discuss how constraints processing works (abstractly)***
- Discuss semantics of constraints processing.
- Loosely follows CHR semantics.
@ -22,8 +23,7 @@ Constraints processing relies heavily on the use of terms as data type. Abstract
node(name("List"), arg(node(name("Int"), arg())))
```
> (examples of terms)
_(examples of terms)_
A term variable ranges over terms. A substitution is a mapping of variables to terms. Unification searches for a substitution $\sigma$, such that for two terms being unified, $f$ and $g$, the following holds: $\sigma f = \sigma g$.
@ -51,8 +51,7 @@ Logical variables serve to identify an object that is to be determined in the fu
assertTrue(Y.find() == X)
assertTrue(Y.find().value() == "foo")
```
> (example of using logical variables)
_(example of using logical variables)_
Combining logical variables and terms gives a very powerful instrument. A term variable can also be a logical variable, so that when two terms are unified, the substitution has the calculated value for this variable.
@ -69,7 +68,6 @@ Combining logical variables and terms gives a very powerful instrument. A term v
assertTrue(X.find().value() == term("h"))
```
## Constraints and predicates
Constraints are simple tuples with fixed arity and a symbol attached. In some respects constraints correspond to rows in a database table. Logically they can be understood as facts, relations, or propositions. An argument to a constraint can be a term, a logical variable, or any POJO, except another constraint.
@ -80,24 +78,23 @@ The following figure shows the lifecycle of a constraint. The big rounded square
A successfully fired production, which declares one or more of its head constraints to be «replaced», causes these to be terminated.
![](img/constraint-lifecycle.svg)
![](img/constraint-lifecycle.svg)
_(lifecycle of a constraint)_
To illustrate the idea of using *stored* constraints to fill vacant positions when matching a production, lets consider an example…**TODO**
To illustrate the idea of using *stored* constraints to fill vacant positions when matching a production, lets consider an example…***unfinished***
![](img/errorDialog.png) **TODO example of multi-head production**
***Example of multi-head production***
Whereas a constraint serves to embody a relation among objects simply by being a witness of such a relation, a *predicate*[^pred] helps to establish a relation or check if a relation exists by means of executing a procedure. Same is true for facts and propositions.
![](img/errorDialog.png) **TODO example of a predicate**
***Example of a predicate***
Predicates must implement ask/tell protocol. If a predicate is invoked from productions guard clause, it represents a query (ask), and if it is invoked from the body, it is an assertion (tell).
![](img/errorDialog.png) **TODO example of ask/tell**
***Example of ask/tell***
## Constraint production (rule)
![](img/errorDialog.png) **TODO**
Constraints program is built from productions. Each production has three parts: the part that is responsible for triggering the production, called «head», the part that checks for pre-conditions, called «guard», and the part that is evaluated when production is fired, which is called «body».
Head is a set of constraints which are all required to be alive in order for production to fire. This set is divided into «kept» part and «replaced» part, the latter containing constraints that are to be discarded as soon as the production fires.
@ -115,8 +112,7 @@ Guard is a conjunction of predicates, which are checked before a match of availa
Body is a conjunction of predicates and constraint activations. When evaluated, each body clause is evaluated in order, with predicates serving as *assertions* and constraint activations producing new constraints. Each newly activated constraint is checked against any productions that can be fired, and so on.
![](img/errorDialog.png) **TODO alternative body**
***Alternative body***
## Semantics of constraint program

View File

@ -6,7 +6,7 @@ weight: 55
---
# Constraints Reactor
![](img/errorDialog.png) **TODO details of constraints processing implementation**
***Details of constraints processing implementation***
Constraints reactor is a small library written in Kotlin and Java, which implements the extended semantics of constraints processing. Its main features are: