Summary of Datalog language deviations
Getty Ritter
8 years ago
| 3 | 3 | standard Datalog is the existence of a simple type system for rows as |
| 4 | 4 | well as a choice of tuple- or record-structured data. |
| 5 | 5 | |
| 6 | Typical datalog facts are tuple-structured: | |
| 6 | # Deviation 1: Record-Structured Data | |
| 7 | ||
| 8 | Any place where Datalog uses tuple-structured data (i.e. where you | |
| 9 | have comma-separated expressions in between parentheses) you can also | |
| 10 | use record-structured data, which allow fields to appear in any | |
| 11 | order. So the classic example | |
| 7 | 12 | |
| 8 | 13 | ``` |
| 9 |
|
|
| 14 | - parent ( terry, austin ). | |
| 10 | 15 | ``` |
| 11 | 16 | |
| 12 | Facts are inserted into the database by stating them. | |
| 17 | can be expressed nearly equivalently using record-structured data as | |
| 13 | 18 | |
| 14 | 19 | ``` |
| 15 |
|
|
| 20 | - parent { parent: terry, child: austin }. | |
| 16 | 21 | ``` |
| 17 | 22 | |
| 18 | Queries against this are done in the typical Datalog way: | |
| 23 | Similarly, a query to find all the children of `terry` could be | |
| 24 | written as | |
| 19 | 25 | |
| 20 | 26 | ``` |
| 21 | ancestor(X, Y) :- parent(X, Y). | |
| 22 | ancestor(X, Y) :- ancestor(X, Z), parent(Z, Y). | |
| 27 | - parent { parent: terry, child: X }? | |
| 23 | 28 | ``` |
| 24 | 29 | |
| 25 | However, for utility, you can also express this in a record-structured | |
| 26 | way with field names. Predicates/queries can also use record | |
| 27 | structures, which act as keyword arguments. | |
| 30 | # Deviation 2: Static Types | |
| 31 | ||
| 32 | Datalog facts are usually dynamically typed, but this implementation | |
| 33 | requires (in part to support the syntax above) that facts and | |
| 34 | predicates have a consistent statically known type. For facts, this | |
| 35 | often involves a "table declaration" which dictates the structure of | |
| 36 | all future facts in that table. Some table declarations might look | |
| 37 | like | |
| 28 | 38 | |
| 29 | 39 | ``` |
| 30 | table parent { parent : atom, child : atom }. | |
| 31 | parent { parent: terry, child: austin }. | |
| 32 | ancestor { parent: X, child: Y } | |
| 33 | :- parent { parent: X, child: Y }. | |
| 34 | ancestor { parent: X, child: Y } | |
| 35 | :- ancestor { parent: X, child: Z }, parent { parent: X, child: Y }. | |
| 40 | - @table edge ( integer, integer ). | |
| 41 | - @table parent { parent: atom, child: atom }. | |
| 36 | 42 | ``` |
| 43 | ||
| 44 | Queries also have static types, but these can _generally_ be inferred | |
| 45 | from context in Datalog, and so do not need to be defined. If | |
| 46 | necessary, though, a `query` definition can be used. | |
| 47 | ||
| 48 | ``` | |
| 49 | - @query ancestor { parent: atom, child: atom }. | |
| 50 | - ancestor { parent: X, child: Y} | |
| 51 | :- parent { parent: X, child: Y }. | |
| 52 | - ancestor { parent: X, child: Y} | |
| 53 | :- parent { parent: X, child: Z}, | |
| 54 | ancestor { parent: X, child: Y }. | |
| 55 | ``` | |
| 56 | ||
| 57 | # Deviation 3: Implicit Variables | |
| 58 | ||
| 59 | When defining a table's type signature, we can also include a few | |
| 60 | extra annotations about the meaning of that variable. For example, we | |
| 61 | might want a particular field of a table to be an auto-incrementing | |
| 62 | integer, so that every row would include a fresh value without us | |
| 63 | having to discover it first. If we define a table with the | |
| 64 | `@autoincrement` keyword, like so | |
| 65 | ||
| 66 | ``` | |
| 67 | - @table person { id: integer @autoincrement, name: string }. | |
| 68 | ``` | |
| 69 | ||
| 70 | then we can use the placeholder `@auto` value, which mean Datalog will | |
| 71 | examine the table to find the necessary "next" value. | |
| 72 | ||
| 73 | ``` | |
| 74 | - person { id: @auto, name: "alice" }. | |
| 75 | - person { id: @auto, name: "bob" }. | |
| 76 | - person { id: @auto, name: "carol" }. | |
| 77 | ``` |