{"action":"created","issue":{"url":"https://api.github.com/repos/DalekBaldwin/check-it/issues/17","repository_url":"https://api.github.com/repos/DalekBaldwin/check-it","labels_url":"https://api.github.com/repos/DalekBaldwin/check-it/issues/17/labels{/name}","comments_url":"https://api.github.com/repos/DalekBaldwin/check-it/issues/17/comments","events_url":"https://api.github.com/repos/DalekBaldwin/check-it/issues/17/events","html_url":"https://github.com/DalekBaldwin/check-it/pull/17","id":99930924,"number":17,"title":"Added the macro with-generators.","user":{"login":"malisper","id":6210567,"avatar_url":"https://avatars.githubusercontent.com/u/6210567?v=3","gravatar_id":"","url":"https://api.github.com/users/malisper","html_url":"https://github.com/malisper","followers_url":"https://api.github.com/users/malisper/followers","following_url":"https://api.github.com/users/malisper/following{/other_user}","gists_url":"https://api.github.com/users/malisper/gists{/gist_id}","starred_url":"https://api.github.com/users/malisper/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/malisper/subscriptions","organizations_url":"https://api.github.com/users/malisper/orgs","repos_url":"https://api.github.com/users/malisper/repos","events_url":"https://api.github.com/users/malisper/events{/privacy}","received_events_url":"https://api.github.com/users/malisper/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":18,"created_at":"2015-08-09T21:34:04Z","updated_at":"2016-02-07T05:45:24Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/DalekBaldwin/check-it/pulls/17","html_url":"https://github.com/DalekBaldwin/check-it/pull/17","diff_url":"https://github.com/DalekBaldwin/check-it/pull/17.diff","patch_url":"https://github.com/DalekBaldwin/check-it/pull/17.patch"},"body":"It makes it easier to bind generators to variables and run tests with\r\nthem. For example:\r\n\r\n    (with-generators ((x (integer)) \r\n                      (y (integer)))\r\n      (check-that (= (+ x y) (+ x y)))\r\n      ;; Case designed to fail.\r\n      (check-that (= (+ x y) (+ x y 1))))\r\n\r\nI plan on writing a more powerful version that would allow something such as the following:\r\n\r\n    (with-generators ((x (integer))\r\n                      (y (integer)))\r\n      (let ((result (+ x y)))\r\n        (check-that (= result result))\r\n        (check-that (= result (+ result 1)))))\r\n\r\nI can think of no easy way to implement it except by using a code walker."},"comment":{"url":"https://api.github.com/repos/DalekBaldwin/check-it/issues/comments/180956759","html_url":"https://github.com/DalekBaldwin/check-it/pull/17#issuecomment-180956759","issue_url":"https://api.github.com/repos/DalekBaldwin/check-it/issues/17","id":180956759,"user":{"login":"DalekBaldwin","id":1544692,"avatar_url":"https://avatars.githubusercontent.com/u/1544692?v=3","gravatar_id":"","url":"https://api.github.com/users/DalekBaldwin","html_url":"https://github.com/DalekBaldwin","followers_url":"https://api.github.com/users/DalekBaldwin/followers","following_url":"https://api.github.com/users/DalekBaldwin/following{/other_user}","gists_url":"https://api.github.com/users/DalekBaldwin/gists{/gist_id}","starred_url":"https://api.github.com/users/DalekBaldwin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DalekBaldwin/subscriptions","organizations_url":"https://api.github.com/users/DalekBaldwin/orgs","repos_url":"https://api.github.com/users/DalekBaldwin/repos","events_url":"https://api.github.com/users/DalekBaldwin/events{/privacy}","received_events_url":"https://api.github.com/users/DalekBaldwin/received_events","type":"User","site_admin":false},"created_at":"2016-02-07T05:45:24Z","updated_at":"2016-02-07T05:45:24Z","body":"Here's a sketch of what the overall DSL is turning into, demonstrated by combining most of the features into a single top-level form. As long as you can specify how all the low-level atomic values you need can be generated, you can specify almost any other more complex -- yet still shrinkable -- generation strategy using only mapping and chaining generators. (Some of the other built-in generators should probably be redefined in terms of a mapping generator anyway.)\r\n\r\n```lisp\r\n;; sketch of lexical contextual dataflow DSL for property-based testing\r\n(defparameter x #g(integer 0 10)) ;; at global scope, define a generator of\r\n                                  ;; integers between 0 and 10\r\n(let ((y #g(integer 4 6))) ;; at local scope, define a generator of integers\r\n                           ;; between 4 and 6\r\n  (deftest blah-test () ;; define a top-level test function in some unit test\r\n                        ;; framework\r\n    (generating (x ;; lexically register name to same generator bound at global\r\n                   ;; scope\r\n                 y ;; lexically register name to same generator bound in\r\n                   ;; surrounding lexical scope\r\n                 (z #g(integer 15 20)) ;; lexically register new generator\r\n                 )\r\n      (is ;; treated as single test case in whatever unit test framework you use\r\n       (checking ;; now we actually generate individual values from\r\n                 ;; lexically-registered generators `x`, `y`, and `z`, and\r\n                 ;; rebind the results to the same names as the generators that\r\n                 ;; produced them\r\n         (that (= x y z)) ;; check/shrink this property only\r\n         ))\r\n      (mapping ((a (+ x y)) ;; specifies how values of `a` will be produced from\r\n                            ;; values of `x` and `y` once we actually generate\r\n                            ;; values within a `checking` form\r\n                (b (+ x y z)) ;; likewise, specifies how values of `b` will be\r\n                              ;; produced\r\n                )\r\n        (chaining ((w ;; chaining: generate results from surrounding generators\r\n                      ;; in order to create parameters for this generator; then\r\n                      ;; this generator will have its values generated through a\r\n                      ;; separate value-generation event. (if multiple distinct\r\n                      ;; `chaining` forms are lexically composed, we can only\r\n                      ;; conduct a shrinkage search with respect to the nearest\r\n                      ;; enclosing `chaining` generator and any other `mapping`\r\n                      ;; generators that derive their values from that chained\r\n                      ;; generator's values)\r\n                    #g(list (integer)\r\n                            :min-length a\r\n                            :max-length b)))\r\n          (is\r\n           (checking ;; generate/rebind `x`, `y`, `z`, `a`, `b`, and `w` for\r\n                     ;; each set of generated values...\r\n             (that (<= a (length w) b)) ;; check this property (this one should\r\n                                        ;; actually hold! not just a foo-bar\r\n                                        ;; example)\r\n             (that (= a b)) ;; check this property at the same time\r\n             (that (something something)) ;; this one too\r\n             )\r\n           ;; and during shrinking, report whenever we discover a sample value\r\n           ;; that fails a different **combination** of checked properties than\r\n           ;; those that the last sample value failed, and issue as the final\r\n           ;; shrunken value whatever value did not fail to satisfy any stated\r\n           ;; properties and did not trigger and unhandled errors\r\n           ))\r\n        (is\r\n         (checking ;; generate/rebind `x`, `y`, `z`, `a`, and `b` for each set\r\n                   ;; of generated values...\r\n           (that nil) ;; this property will always fail, obviously\r\n           (that (= x y z a b)) ;; this will surely fail, just a BS example\r\n           ))))))\r\n;; the above form specifies three different test cases from the perspective of a\r\n;; surrounding unit-test framework, with some overlapping dependencies on\r\n;; randomized generator specifications. we take advantage of the concept of\r\n;; lexical scope, but apply it to concepts that are not typically lexically\r\n;; scoped, to allow users to write test specifications that are extremely\r\n;; compact yet readable to anybody who knows how to apply compositional\r\n;; reasoning. the specifications compose, but not in the sense of composition\r\n;; usually applied in pure functional programming. the surrounding definitions\r\n;; are only fully combined into a useable generating/shrinking context once you\r\n;; enter a `checking` form.\r\n```\r\n\r\nWithout the comments:\r\n\r\n```lisp\r\n(defparameter x #g(integer 0 10))\r\n(let ((y #g(integer 4 6)))\r\n  (deftest blah-test ()\r\n    (generating (x y (z #g(integer 15 20)))\r\n      (is\r\n       (checking (that (= x y z))))\r\n      (mapping ((a (+ x y))\r\n                (b (+ x y z)))\r\n        (chaining ((w #g(list (integer)\r\n                              :min-length a\r\n                              :max-length b)))\r\n          (is\r\n           (checking (that (<= a (length w) b))\r\n                     (that (= a b))\r\n                     (that (something something)))))\r\n        (is\r\n         (checking (that nil)\r\n                   (that (= x y z a b))))))))\r\n```\r\n\r\nI'm not aware of any other language/framework that uses present participles to indicate a related group of linguistic constructs. (For a short while, Clojure used them for transducers (`mapping`, `filtering`, `mapcatting`, etc.), but now they've been combined with the same symbols for the corresponding higher-order functions (`map`, `filter`, `mapcat`, etc.)). I think they work pretty well here. They describe a lexical context that is continually being built up, but not actually being actively used until a specific form relies on them. I'm just not sure whether that specific form should also be a present participle (`checking`) or if it would be clearer to distinguish it some other way."}}