{"action":"created","comment":{"url":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/comments/215068648","pull_request_review_id":152225917,"id":215068648,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxNTA2ODY0OA==","diff_hunk":"@@ -1,40 +1,650 @@\n # Tutorial: Differential Datalog\n \n-Differential Datalog is a *bottom-up*, *incremental*, *programmer-friendly* Datalog engine designed for ...\n+**Note:**: All examples from this tutorial can be found in\n+[`test/datalog_tests/tutorial.dl`](../../test/datalog_tests/tutorial.dl).  Test inputs are in\n+[`test/datalog_tests/tutorial.dat`](../../test/datalog_tests/tutorial.dat) and outputs are in\n+[`test/datalog_tests/tutorial.dump.expected`](../../test/datalog_tests/tutorial.dump.expected).\n \n-To this end, it has the following properties\n+## Introduction\n \n-1. **Bottom-up evaluation**\n-1. **Efficient incremental updates** in-memory\n-1. **App integration**\n-1. **Rich syntax**\n+Differential Datalog, or **DDlog** for short, is a *bottom-up*, *incremental*, *in-memory* Datalog\n+engine for writing *application-integrated* deductive database engines.  Let's decipher some of\n+these keywords:\n \n-## Installing\n+1. **Bottom-up**: DDlog starts from a set of *ground facts* (i.e., facts provided by the user) and\n+computes *all* possible derived facts by following Datalog rules in a bottom-up fashion.  This is in\n+contrast to top-down engines that are optimized to answer individual user queries without computing\n+all possible facts ahead of time.  For example, given a Datalog program that computes pairs of\n+connected vertices in a graph, a bottom-up engine maintains the set of all such pairs.  A top-down\n+engine, on the other hand, is triggered by a user query to determine whether a pair of vertices is\n+connected and handles the query by searching for a derivation chain back to ground facts.  The\n+bottom-up approach is preferable in applications where all derived facts must be computed ahead of\n+time and in applications where the cost of initial computation is amortized across a large number of\n+queries.\n \n-## \"Hello, World!\" in Datalog\n+1. **Incremental**: whenever the set of ground facts changes, DDlog updates derived facts\n+incrementally, avoiding complete recomputation as much as possible.  In practical terms, the means\n+that a small change to ground facts often triggers only a trivial amount of computation.\n \n-## Compiling and running the \"Hello, World!\" example\n+1. **In-memory**: DDlog stores and processes data in memory.  In a typical use case, a DDlog program\n+is used in conjunction with a persistent database, with database records being fed to DDlog as\n+grounds, and derived facts computed by DDlog being written back to the database.\n \n-*Explain the three interfaces to the Datalog program*\n-*Named vs positional arguments*\n+At the moment, DDlog can only operate on databases that completely fit in memory of a single host.\n+(This may change in the future, as DDlog builds on the differential dataflow library that supports\n+distributed computation over partitioned data).\n+\n+1. **Integrated**: while DDlog programs can be run interactively via a command line interface, its\n+primary use case is to integrate with other applications that require deductive database\n+functionality.  A DDlog program is compiled into a Rust library that can be linked against a Rust or\n+C/C++ program (bindings for other languages can be easily added, but Rust and C are the only ones we\n+support at the moment).  This enables good performance, but somewhat limits the flexibility, as\n+changes to the relational schema or rules require re-compilation.\n+\n+### Not your textbook Datalog\n+\n+Although Datalog is typically described as a programming language, in its classical textbook form it\n+is more of a mathematical formalism than a practical tool for programmers.  In particular, pure\n+Datalog does not have concepts like data types, arithmetics, strings or functions.  To facilitate\n+writing of safe, clear, and concise code, DDlog extends pure Datalog with these concepts, including:\n+\n+1. A powerful type system, including Booleans, integers, bitvectors, strings, tuples, and tagged\n+unions.\n+\n+1. Standard integer and bitvector arithmetic.\n+\n+1. A simple procedural language that allows expressing many computations natively in DDlog without\n+resorting to external functions.\n+\n+1. String operations, including string concatenation and interpolation.\n+\n+## Installing DDlog\n+\n+**TODO: document proper installation procedure when we have one.**\n+\n+Follow instructions in Installation sections of the\n+[README](README.md).\n+\n+## \"Hello, World!\"\n+\n+We start with the obligatory \"Hello, World!\" example.  Let's create a file with `.dl` extension, say\n+`playpen.dl`, in the `test/datalog_tests` directory.  Next time you run `stack test`, the test\n+framework will pickup the program, and automatically compile and run it.  Alternatively, running\n+`stack test --ta '-p playpen'` will execute just your test instead of the entire test framework.\n+\n+**TODO: replace these instructions once issue #64 has been fixed**\n+\n+Copy the following code to `playpen.dl`:\n+\n+```\n+/* First DDlog example */\n+\n+typedef Category = CategoryStarWars\n+                 | CategoryOther\n+\n+input relation Word1(word: string, cat: Category)\n+input relation Word2(word: string, cat: Category)\n+\n+relation Phrases(phrase: string)\n+\n+// Rule\n+Phrases(w1 ++ \" \" ++ w2) :- Word1(w1, cat), Word2(w2, cat).\n+```\n+\n+This may not look like a typical \"Hello, world!\" program, yet that is exactly what it is.  Let's\n+dissect this code snippet.  First, note the use of C++ style comments (`//` and `/* */`).\n+\n+Comments aside, this program contains several DDlog declarations: a data type declaration, three\n+relations, and a rule.  We discuss the type system in more detail below.  For the moment, think of\n+the `Category` type as a C-style enum with two possible values: `CategoryStarWars` and\n+`CategoryOther`.\n+\n+Datalog relations are similar to database tables in that they store sets of records.  `Word1` and\n+`Word2` are two *input* relations in our example.  Input relations (aka *extensional* relations in\n+the Datalog world) are populated by user-provided ground facts.  The other kind of relations are\n+*derived* (or *intensional*) relations that are computed based on rules.  The two kinds do not\n+overlap: a relation must be populated exclusively by either ground or derived facts.  The `input`\n+qualifier is used to declare an input relation; relations declared without this qualifier are\n+derived relations.\n+\n+`Word1` and `Word2` relations have the same record type, including a `word` field of type `string`\n+and a `cat` field of type `Category`.  We would like to combine strings from `Word1` and `Word2`\n+relations into phrases and store them in the `Phrases` relation.  However, we only want to combine\n+words that belong to the same category.  This is captured by the DDlog rule in the end of the\n+listing.  The rule can be read as follows: *for every `w1`, `w2`, and `cat`, such that `(w1, cat)`\n+is in `Word1` and `(w2, cat)` is in `Word2`, there is a record `w1 ++ \" \" ++ w2` in Phrases*, where,\n+`++` is a string concatenation operator.\n+\n+Clauses of the form `<Relation_name>(<arguments>)` are called *atoms*.  An atom is true iff the\n+relation contains the specified record.  The atom to the left of `:-` is the *head* of the rule;\n+atoms to the right of `:-` form the *body* of the rule.  Commas in the body of the rule represent\n+logical conjunctions, i.e., if all atoms in the body are true, then the head of the rule holds.\n+Finally, note the mandatory dot in the end of the rule.\n+\n+The above rule computes an inner join of `Word1` and `Word2` relations on the `cat` field.\n+\n+### Capitalization rules\n+\n+1. A relation name must start with an upper-case letter.\n+\n+1. A variable name must start with a lower-case letter or underscore\n+(`_`).\n+\n+1. A type name can start with an upper-case or a lower-case letter or\n+an underscore.\n+\n+### Ordering of declarations\n+\n+DDlog does not care about the order in which various program entities (types, relations, rules,\n+functions) are declared.  Reordering declarations in the above program does not have any effect on\n+program semantics.\n+\n+## Compiling the example\n+\n+Run `stack test --ta '-p playpen'` to compile the \"Hello, world!\" program.  This command performs\n+two actions.  First, it runs the DDlog compiler to generate a Rust library that implements the\n+semantics of your DDlog program, along with C bindings to invoke the library from C/C++ programs.\n+Second, it invokes the Rust compiler to compile the library.  The latter step is unfortunately quite\n+slow at the moment, typically taking a couple of minutes.  Once the `stack test` command completes,\n+you should find the following artifacts in the same directory with the `playpen.dl` file.\n+\n+1. The DDlog compiler creates three Rust packages (or \"crates\") that comprise a Rust library that\n+implements your DDlog relations and rules:\n+    * `./differential_dataflow/`\n+    * `./cmd_parser/`\n+    * `./playpen/` (this is the main crate, which imports the two\n+      others)\n+\n+1. If you plan to use this library directly from a Rust program, have a look at the\n+`./playpen/lib.rs` file, which contains the Rust API to DDlog.\n+\n+**TODO: link to a separate document explaining the structure and API of the Rust project**\n+\n+1. If you plan to use the library from a C/C++ program, your program must link against the\n+`./playpen/target/release/libplaypen.so` library, which wraps the DDlog program into a C API.  This\n+API is declared in the auto-generated `./playpen/playpen.h` header file.\n+\n+**TODO: link to a separate document explaining the use of the C FFI**\n+\n+1. Throughout this tutorial, we will use DDlog via its text-based interface.  This interface is\n+primarily meant for testing and debugging purposes, as it does not offer the same performance and\n+flexibility as the API-based interfaces.  The text-based interface is implemented by an\n+auto-generated executable `./playpen/target/release/playpen_cli`.\n+\n+## Running the example\n+\n+What kind of a \"Hello, world!\" program does not contain \"Hello\" or \"world\" strings anywhere in the\n+code?  Well, those strings are data, and DDlog offers several ways to feed data to the program:\n+\n+1. Statically, by listing ground facts as part of the program.\n+\n+1. Via text-based interface.\n+\n+1. From a Rust program.\n+\n+1. From a C or C++ program.\n+\n+In the following sections, we expand on each method.\n+\n+### Specifying ground facts statically in the program source code\n+\n+**TODO: This does not currently work: DDlog will accept such ground facts, but they will not\n+actually be added to the DB, see issue #55**\n+\n+This method is useful for specifying ground facts that are guaranteed to hold in every instantiation\n+of the program.  Such facts can be specified statically to save the hassle of adding them manually\n+on every program instantiation.  A ground fact is just a rule without a body.  Add the following\n+declarations to the program to pre-populate `Word1` and `Word2` relations:\n+\n+```\n+Word1(\"Hello,\", CategoryOther).\n+Word2(\"world!\", CategoryOther).\n+```\n+\n+### Feeding data through text-based interface\n+\n+The text-based interface offers a convenient way to interact with the DDlog program during\n+development and debugging.  Let's try this interface in the command-line mode first.  Execute the\n+`./playpen/target/release/playpen_cli` program from terminal and enter the following commands in its\n+command line:\n+\n+```\n+start;\n+\n+insert Word1(\"Hello,\", CategoryOther),\n+insert Word2(\"world!\", CategoryOther);\n+\n+commit;\n+dump Phrases;\n+```\n+\n+You should see the following output:\n+\n+```\n+>> start;\n+>>\n+>> insert Word1(\"Hello,\", CategoryOther),\n+>> insert Word2(\"world!\", CategoryOther);\n+>>\n+>> commit;\n+insert 30 Word1(Word1{\"Hello,\",Category::CategoryOther{}})\n+insert 31 Word2(Word2{\"world!\",Category::CategoryOther{}})\n+insert 21 Phrases(Phrases{\"Hello, world!\"})\n+>> dump Phrases;\n+Phrases{\"Hello, world!\"}\n+>>\n+```\n+\n+Note the three lines printed by the program in response to the `commit;` command.  At commit time,\n+DDlog reports incremental changes to all of its relations, including input and derived relations.\n+Use the `--no-print` command-line option to disable this behavior.  See [this\n+document](../testing/testing.md#command-reference) for a complete list of commands supported by the\n+tool.\n+\n+You won't get very far by typing commands manually every time.  Fortunately, you can also run the\n+program in batch mode, feeding commands via a UNIX pipe from a file or another program.  Create a\n+file called `playpen.dat` with the following content:\n+\n+```\n+start;\n+\n+insert Word1(\"Hello,\"      , CategoryOther),\n+insert Word1(\"Goodbye,\"    , CategoryOther),\n+insert Word2(\"World\"       , CategoryOther),\n+insert Word2(\"Ruby Tuesday\", CategoryOther);\n+\n+insert Word1(\"Help me,\"      , CategoryStarWars),\n+insert Word1(\"I am your\"     , CategoryStarWars),\n+insert Word2(\"Obi-Wan Kenobi\", CategoryStarWars),\n+insert Word2(\"father\"        , CategoryStarWars);\n+\n+commit;\n+\n+echo Phrases:;\n+dump Phrases;\n+```\n+\n+You can run the program either manually:\n+\n+```\n+playpen/target/release/playpen_cli < playpen.dat\n+```\n+\n+or using DDlog's test framework:\n+```\n+`stack test --ta '-p playpen'`\n+```\n+\n+The latter will automatically recompile the program if it has changed, check if a file named\n+`playpen.dat` exists under `tests/datalog_tests` and invoke `playpen_cli` on this file, dumping its\n+output to `playpen.dump`.  In this example, it will produce the following output:\n+\n+```\n+Phrases:\n+Phrases{\"Goodbye, Ruby Tuesday\"}\n+Phrases{\"Goodbye, World\"}\n+Phrases{\"Hello, Ruby Tuesday\"}\n+Phrases{\"Hello, World\"}\n+Phrases{\"Help me, Obi-Wan Kenobi\"}\n+Phrases{\"Help me, father\"}\n+Phrases{\"I am your Obi-Wan Kenobi\"}\n+Phrases{\"I am your father\"}\n+```\n+\n+### Set semantics\n+\n+DDlog implements set semantics for relations, i.e., multiple identical records are merged into a\n+single record.  Try modifying the `.dat` file to add the same input record twice and dump the\n+relation to make sure that it contains one instance of the record.  The same is true for derived\n+relations: when the same record can be derived in multiple ways, the resulting relation will still\n+contain a single copy.\n+\n+### Incremental evaluation\n+\n+Before moving on to more interesting programs, we use the \"Hello, world!\" example to demonstrate\n+the incremental aspect of DDlog.  Add the following commands to the `.dat` file to delete one of the\n+input records.  All phrases ending with \"World\" should disappear from `Phrases`.  DDlog performs\n+this computation incrementally, without recomputing the entire relation from scratch.\n+\n+```\n+start;\n+delete Word2(\"World\", CategoryOther);\n+commit;\n+\n+echo Phrases:;\n+dump Phrases;\n+```\n \n ## Expressions\n \n-*Extract subnet mask from IP address*\n-*wildcard*\n-*Lower case/upper case rules*\n+DDlog features a powerful expression language.  Expressions are used inside DDlog rules to filter\n+input records and compute derived records.  In this section of the tutorial, we introduce the use of\n+expressions with a series of examples.  See the [language\n+reference](../language_reference/language_reference.md#expressions) for a complete description.\n+\n+The following example illustrates the use of expressions to filter records in the body of a rule\n+along with several other features of DDlog.  Given a set of IP hosts and subnets, this DDlog program\n+computes a host-to-subnet mapping by matching IP addresses against subnet masks.\n+\n+```\n+// Type aliases improve readability.\n+typedef UUID    = bit<128>\n+typedef IP4     = bit<32>\n+typedef NetMask = bit<32>\n+\n+//IP host specified by its name and address.\n+input relation Host(id: UUID, name: string, ip: IP4)\n+\n+//IP subnet specified by its IP prefix and mask\n+input relation Subnet(id: UUID, prefix: IP4, mask: NetMask)\n+\n+// HostInSubnet relation maps hosts to known subnets\n+relation HostInSubnet(host: UUID, subnet: UUID)\n+\n+HostInSubnet(host_id, subnet_id) :- Host(host_id, _, host_ip),\n+                                    Subnet(subnet_id, subnet_prefix, subnet_mask),\n+                                    ((host_ip & subnet_mask) == subnet_prefix). // filter condition\n+```\n+\n+First, note the three *type alias* declarations in the first lines of the example.  A type alias is\n+a shortcut that can be used interchangeably with the type it aliases.  Type aliases improve code\n+readability and simplify refactoring.  For example, while in this example `IP4` and `bit<32>`\n+refer to the exact same type (a 32-bit unsigned integer), the former makes it explicit that the\n+corresponding variable or field stores an IP address value.\n+\n+Next we declare the two input relations that store known IP hosts and subnets respectively, followed\n+by the derived `HostInSubnet` relation.  `HostInSubnet` is computed by filtering all host-subnet\n+pairs where host address matches subnet prefix and mask.  This is captured by the rule in the end of\n+the program.  The first two clauses use already familiar syntax two *bind* `host_id`, `host_ip`,\n+`subnet_id`, `subnet_prefix`, `subnet_mask` variables to records from `Host` and `Subnet` relations.\n+\n+The first two clauses iterate over all host-subnet pairs.  We would like to narrow this set down to\n+only those pairs where the host's IP address belongs to the subnet.  This is captured by the\n+expression in the third clause of the rule: `(host_ip & subnet_mask) == subnet_prefix`, where `&` is\n+the bitwise \"and\" operaror, and `==` is the equality operator.  In DDlog, a clause that is a Boolean\n+expression is a *filter* clause that eliminates all tuples that do not satisfy the condition.\n+\n+Note that this rule does not use the `name` field of the `Host` relation.  We do not want to pollute\n+the name space with an unused variable and instead use the *wildcard* symbol (`_`) in the first\n+clause of the rule to indicate that the value of the field is immaterial:\n+\n+```\n+Host(host_id, _, host_ip)\n+```\n+\n+DDlog supports an alternative syntax for atoms, identifying relation arguments by name instead of by\n+position:\n+\n+```\n+Host(.id=host_id, .ip=host_ip)\n+```\n+\n+While this syntax is more verbose, it improves code clarity and prevents errors due to incorrect\n+ordering of arguments.  In addition, it only requires the programmer to list arguments that are used\n+in the rule.  Omitted arguments are automaticaly wildcarded.\n+\n+### String literals\n+\n+DDlog has built-in support for strings, allowing many string-manipulating programs to be expressed","path":"doc/tutorial/tutorial.md","position":402,"original_position":402,"commit_id":"b80bde0e3d11dee28b22d51304756dcae656236f","original_commit_id":"b80bde0e3d11dee28b22d51304756dcae656236f","user":{"login":"mbudiu-vmw","id":19397042,"node_id":"MDQ6VXNlcjE5Mzk3MDQy","avatar_url":"https://avatars1.githubusercontent.com/u/19397042?v=4","gravatar_id":"","url":"https://api.github.com/users/mbudiu-vmw","html_url":"https://github.com/mbudiu-vmw","followers_url":"https://api.github.com/users/mbudiu-vmw/followers","following_url":"https://api.github.com/users/mbudiu-vmw/following{/other_user}","gists_url":"https://api.github.com/users/mbudiu-vmw/gists{/gist_id}","starred_url":"https://api.github.com/users/mbudiu-vmw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mbudiu-vmw/subscriptions","organizations_url":"https://api.github.com/users/mbudiu-vmw/orgs","repos_url":"https://api.github.com/users/mbudiu-vmw/repos","events_url":"https://api.github.com/users/mbudiu-vmw/events{/privacy}","received_events_url":"https://api.github.com/users/mbudiu-vmw/received_events","type":"User","site_admin":false},"body":"supports a small set of operations on strings","created_at":"2018-09-04T21:12:41Z","updated_at":"2018-09-04T21:55:09Z","html_url":"https://github.com/ryzhyk/differential-datalog/pull/75#discussion_r215068648","pull_request_url":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/75","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/comments/215068648"},"html":{"href":"https://github.com/ryzhyk/differential-datalog/pull/75#discussion_r215068648"},"pull_request":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/75"}}},"pull_request":{"url":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/75","id":212521884,"node_id":"MDExOlB1bGxSZXF1ZXN0MjEyNTIxODg0","html_url":"https://github.com/ryzhyk/differential-datalog/pull/75","diff_url":"https://github.com/ryzhyk/differential-datalog/pull/75.diff","patch_url":"https://github.com/ryzhyk/differential-datalog/pull/75.patch","issue_url":"https://api.github.com/repos/ryzhyk/differential-datalog/issues/75","number":75,"state":"open","locked":false,"title":"First sections of tutorial","user":{"login":"ryzhyk","id":8323135,"node_id":"MDQ6VXNlcjgzMjMxMzU=","avatar_url":"https://avatars2.githubusercontent.com/u/8323135?v=4","gravatar_id":"","url":"https://api.github.com/users/ryzhyk","html_url":"https://github.com/ryzhyk","followers_url":"https://api.github.com/users/ryzhyk/followers","following_url":"https://api.github.com/users/ryzhyk/following{/other_user}","gists_url":"https://api.github.com/users/ryzhyk/gists{/gist_id}","starred_url":"https://api.github.com/users/ryzhyk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ryzhyk/subscriptions","organizations_url":"https://api.github.com/users/ryzhyk/orgs","repos_url":"https://api.github.com/users/ryzhyk/repos","events_url":"https://api.github.com/users/ryzhyk/events{/privacy}","received_events_url":"https://api.github.com/users/ryzhyk/received_events","type":"User","site_admin":false},"body":"First sections of tutorial and accompanying examples.","created_at":"2018-09-01T01:36:43Z","updated_at":"2018-09-04T21:55:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"463a5c8909786fd9c6419a4190b473968768ad1b","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/75/commits","review_comments_url":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/75/comments","review_comment_url":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ryzhyk/differential-datalog/issues/75/comments","statuses_url":"https://api.github.com/repos/ryzhyk/differential-datalog/statuses/b80bde0e3d11dee28b22d51304756dcae656236f","head":{"label":"ryzhyk:tutorial","ref":"tutorial","sha":"b80bde0e3d11dee28b22d51304756dcae656236f","user":{"login":"ryzhyk","id":8323135,"node_id":"MDQ6VXNlcjgzMjMxMzU=","avatar_url":"https://avatars2.githubusercontent.com/u/8323135?v=4","gravatar_id":"","url":"https://api.github.com/users/ryzhyk","html_url":"https://github.com/ryzhyk","followers_url":"https://api.github.com/users/ryzhyk/followers","following_url":"https://api.github.com/users/ryzhyk/following{/other_user}","gists_url":"https://api.github.com/users/ryzhyk/gists{/gist_id}","starred_url":"https://api.github.com/users/ryzhyk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ryzhyk/subscriptions","organizations_url":"https://api.github.com/users/ryzhyk/orgs","repos_url":"https://api.github.com/users/ryzhyk/repos","events_url":"https://api.github.com/users/ryzhyk/events{/privacy}","received_events_url":"https://api.github.com/users/ryzhyk/received_events","type":"User","site_admin":false},"repo":{"id":126076620,"node_id":"MDEwOlJlcG9zaXRvcnkxMjYwNzY2MjA=","name":"differential-datalog","full_name":"ryzhyk/differential-datalog","owner":{"login":"ryzhyk","id":8323135,"node_id":"MDQ6VXNlcjgzMjMxMzU=","avatar_url":"https://avatars2.githubusercontent.com/u/8323135?v=4","gravatar_id":"","url":"https://api.github.com/users/ryzhyk","html_url":"https://github.com/ryzhyk","followers_url":"https://api.github.com/users/ryzhyk/followers","following_url":"https://api.github.com/users/ryzhyk/following{/other_user}","gists_url":"https://api.github.com/users/ryzhyk/gists{/gist_id}","starred_url":"https://api.github.com/users/ryzhyk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ryzhyk/subscriptions","organizations_url":"https://api.github.com/users/ryzhyk/orgs","repos_url":"https://api.github.com/users/ryzhyk/repos","events_url":"https://api.github.com/users/ryzhyk/events{/privacy}","received_events_url":"https://api.github.com/users/ryzhyk/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ryzhyk/differential-datalog","description":"An in-memory incremental Datalog engine based on Differential Dataflow","fork":false,"url":"https://api.github.com/repos/ryzhyk/differential-datalog","forks_url":"https://api.github.com/repos/ryzhyk/differential-datalog/forks","keys_url":"https://api.github.com/repos/ryzhyk/differential-datalog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ryzhyk/differential-datalog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ryzhyk/differential-datalog/teams","hooks_url":"https://api.github.com/repos/ryzhyk/differential-datalog/hooks","issue_events_url":"https://api.github.com/repos/ryzhyk/differential-datalog/issues/events{/number}","events_url":"https://api.github.com/repos/ryzhyk/differential-datalog/events","assignees_url":"https://api.github.com/repos/ryzhyk/differential-datalog/assignees{/user}","branches_url":"https://api.github.com/repos/ryzhyk/differential-datalog/branches{/branch}","tags_url":"https://api.github.com/repos/ryzhyk/differential-datalog/tags","blobs_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/refs{/sha}","trees_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ryzhyk/differential-datalog/statuses/{sha}","languages_url":"https://api.github.com/repos/ryzhyk/differential-datalog/languages","stargazers_url":"https://api.github.com/repos/ryzhyk/differential-datalog/stargazers","contributors_url":"https://api.github.com/repos/ryzhyk/differential-datalog/contributors","subscribers_url":"https://api.github.com/repos/ryzhyk/differential-datalog/subscribers","subscription_url":"https://api.github.com/repos/ryzhyk/differential-datalog/subscription","commits_url":"https://api.github.com/repos/ryzhyk/differential-datalog/commits{/sha}","git_commits_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/commits{/sha}","comments_url":"https://api.github.com/repos/ryzhyk/differential-datalog/comments{/number}","issue_comment_url":"https://api.github.com/repos/ryzhyk/differential-datalog/issues/comments{/number}","contents_url":"https://api.github.com/repos/ryzhyk/differential-datalog/contents/{+path}","compare_url":"https://api.github.com/repos/ryzhyk/differential-datalog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ryzhyk/differential-datalog/merges","archive_url":"https://api.github.com/repos/ryzhyk/differential-datalog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ryzhyk/differential-datalog/downloads","issues_url":"https://api.github.com/repos/ryzhyk/differential-datalog/issues{/number}","pulls_url":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls{/number}","milestones_url":"https://api.github.com/repos/ryzhyk/differential-datalog/milestones{/number}","notifications_url":"https://api.github.com/repos/ryzhyk/differential-datalog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ryzhyk/differential-datalog/labels{/name}","releases_url":"https://api.github.com/repos/ryzhyk/differential-datalog/releases{/id}","deployments_url":"https://api.github.com/repos/ryzhyk/differential-datalog/deployments","created_at":"2018-03-20T20:14:11Z","updated_at":"2018-09-01T17:52:12Z","pushed_at":"2018-09-03T21:10:00Z","git_url":"git://github.com/ryzhyk/differential-datalog.git","ssh_url":"git@github.com:ryzhyk/differential-datalog.git","clone_url":"https://github.com/ryzhyk/differential-datalog.git","svn_url":"https://github.com/ryzhyk/differential-datalog","homepage":null,"size":433,"stargazers_count":10,"watchers_count":10,"language":"Haskell","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":21,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":21,"watchers":10,"default_branch":"master"}},"base":{"label":"ryzhyk:master","ref":"master","sha":"1209ede3080dfb510ce3545ac5711f73ebd28be2","user":{"login":"ryzhyk","id":8323135,"node_id":"MDQ6VXNlcjgzMjMxMzU=","avatar_url":"https://avatars2.githubusercontent.com/u/8323135?v=4","gravatar_id":"","url":"https://api.github.com/users/ryzhyk","html_url":"https://github.com/ryzhyk","followers_url":"https://api.github.com/users/ryzhyk/followers","following_url":"https://api.github.com/users/ryzhyk/following{/other_user}","gists_url":"https://api.github.com/users/ryzhyk/gists{/gist_id}","starred_url":"https://api.github.com/users/ryzhyk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ryzhyk/subscriptions","organizations_url":"https://api.github.com/users/ryzhyk/orgs","repos_url":"https://api.github.com/users/ryzhyk/repos","events_url":"https://api.github.com/users/ryzhyk/events{/privacy}","received_events_url":"https://api.github.com/users/ryzhyk/received_events","type":"User","site_admin":false},"repo":{"id":126076620,"node_id":"MDEwOlJlcG9zaXRvcnkxMjYwNzY2MjA=","name":"differential-datalog","full_name":"ryzhyk/differential-datalog","owner":{"login":"ryzhyk","id":8323135,"node_id":"MDQ6VXNlcjgzMjMxMzU=","avatar_url":"https://avatars2.githubusercontent.com/u/8323135?v=4","gravatar_id":"","url":"https://api.github.com/users/ryzhyk","html_url":"https://github.com/ryzhyk","followers_url":"https://api.github.com/users/ryzhyk/followers","following_url":"https://api.github.com/users/ryzhyk/following{/other_user}","gists_url":"https://api.github.com/users/ryzhyk/gists{/gist_id}","starred_url":"https://api.github.com/users/ryzhyk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ryzhyk/subscriptions","organizations_url":"https://api.github.com/users/ryzhyk/orgs","repos_url":"https://api.github.com/users/ryzhyk/repos","events_url":"https://api.github.com/users/ryzhyk/events{/privacy}","received_events_url":"https://api.github.com/users/ryzhyk/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ryzhyk/differential-datalog","description":"An in-memory incremental Datalog engine based on Differential Dataflow","fork":false,"url":"https://api.github.com/repos/ryzhyk/differential-datalog","forks_url":"https://api.github.com/repos/ryzhyk/differential-datalog/forks","keys_url":"https://api.github.com/repos/ryzhyk/differential-datalog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ryzhyk/differential-datalog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ryzhyk/differential-datalog/teams","hooks_url":"https://api.github.com/repos/ryzhyk/differential-datalog/hooks","issue_events_url":"https://api.github.com/repos/ryzhyk/differential-datalog/issues/events{/number}","events_url":"https://api.github.com/repos/ryzhyk/differential-datalog/events","assignees_url":"https://api.github.com/repos/ryzhyk/differential-datalog/assignees{/user}","branches_url":"https://api.github.com/repos/ryzhyk/differential-datalog/branches{/branch}","tags_url":"https://api.github.com/repos/ryzhyk/differential-datalog/tags","blobs_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/refs{/sha}","trees_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ryzhyk/differential-datalog/statuses/{sha}","languages_url":"https://api.github.com/repos/ryzhyk/differential-datalog/languages","stargazers_url":"https://api.github.com/repos/ryzhyk/differential-datalog/stargazers","contributors_url":"https://api.github.com/repos/ryzhyk/differential-datalog/contributors","subscribers_url":"https://api.github.com/repos/ryzhyk/differential-datalog/subscribers","subscription_url":"https://api.github.com/repos/ryzhyk/differential-datalog/subscription","commits_url":"https://api.github.com/repos/ryzhyk/differential-datalog/commits{/sha}","git_commits_url":"https://api.github.com/repos/ryzhyk/differential-datalog/git/commits{/sha}","comments_url":"https://api.github.com/repos/ryzhyk/differential-datalog/comments{/number}","issue_comment_url":"https://api.github.com/repos/ryzhyk/differential-datalog/issues/comments{/number}","contents_url":"https://api.github.com/repos/ryzhyk/differential-datalog/contents/{+path}","compare_url":"https://api.github.com/repos/ryzhyk/differential-datalog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ryzhyk/differential-datalog/merges","archive_url":"https://api.github.com/repos/ryzhyk/differential-datalog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ryzhyk/differential-datalog/downloads","issues_url":"https://api.github.com/repos/ryzhyk/differential-datalog/issues{/number}","pulls_url":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls{/number}","milestones_url":"https://api.github.com/repos/ryzhyk/differential-datalog/milestones{/number}","notifications_url":"https://api.github.com/repos/ryzhyk/differential-datalog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ryzhyk/differential-datalog/labels{/name}","releases_url":"https://api.github.com/repos/ryzhyk/differential-datalog/releases{/id}","deployments_url":"https://api.github.com/repos/ryzhyk/differential-datalog/deployments","created_at":"2018-03-20T20:14:11Z","updated_at":"2018-09-01T17:52:12Z","pushed_at":"2018-09-03T21:10:00Z","git_url":"git://github.com/ryzhyk/differential-datalog.git","ssh_url":"git@github.com:ryzhyk/differential-datalog.git","clone_url":"https://github.com/ryzhyk/differential-datalog.git","svn_url":"https://github.com/ryzhyk/differential-datalog","homepage":null,"size":433,"stargazers_count":10,"watchers_count":10,"language":"Haskell","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":21,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":21,"watchers":10,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/75"},"html":{"href":"https://github.com/ryzhyk/differential-datalog/pull/75"},"issue":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/issues/75"},"comments":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/issues/75/comments"},"review_comments":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/75/comments"},"review_comment":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/pulls/75/commits"},"statuses":{"href":"https://api.github.com/repos/ryzhyk/differential-datalog/statuses/b80bde0e3d11dee28b22d51304756dcae656236f"}},"author_association":"OWNER"}}
{"actor":{"display_login":"mbudiu-vmw"}}