Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISSUE-362: Refactor defrule and defquery for ClojureScript to better support... #365

Merged
merged 5 commits into from
Feb 5, 2018

Conversation

sparkofreason
Copy link
Contributor

...customization of rule/query DSL.

@sparkofreason sparkofreason changed the title Refactor defrule and defquery for ClojureScript to better support... ISSUE-362: Refactor defrule and defquery for ClojureScript to better support... Dec 14, 2017
@WilliamParker
Copy link
Collaborator

@sparkofreason just wanted to let you know that I've seen this PR and will review it. I like the idea, I just haven't had time to review it recently.

@sparkofreason
Copy link
Contributor Author

@WilliamParker thanks, no problem. One thing worth noting is that the current solution only applies to CLJS, and I think parity in CLJ should probably also be a goal.

@WilliamParker
Copy link
Collaborator

The idea here looks sound to me @sparkofreason . As you suggest though it would probably be better to have parity between Clojure and ClojureScript here. My suggestion would be to add the build-rule and build-query functions to the dsl namespace, and then call them in both clara.rules.macros (for ClojureScript) and clara.rules (for Clojure). The signatures would need to change slightly to account for the (meta &form) argument that are being passed in to parse-rule* and parse-query* in Clojure. If ClojureScript has the same available it could do likewise; the usage appears to be in giving more informative exceptions from throw-dsl-ex.

Fundamentally, the build-rule and build-query functions seem like DSL logic to me, and putting them there would allow sharing between clara.rules, clara.rules.macros, and other users in both Clojure and ClojureScript macros. It would also reduce our codebase's size a bit and enforce more similarity between Clojure and ClojureScript.

Thoughts/objections/alternative suggestions?

@sparkofreason
Copy link
Contributor Author

I'll dig into this tomorrow, just got back from holiday travels. That all sounds fine, just note that once we move those functions to a different namespace we won't be able to use the CLJS env/*compiler* any more for storing productions during CLJS compilation. I tested on my local copy with an atom defined in the DSL namespace, and everything seems to work fine. I can't think of any reason why env/*compiler* is required for this case. It's just an atom, with the added feature that the CLJS compiler resets it for each namespace being compiled, but that isn't being used here AFAICT.

@sparkofreason
Copy link
Contributor Author

Also, I think I mentioned this in the discussion of #362: as part of improving support for 3rd-party DSLs, I'd like to support keyword-named rules and queries. Let me know if that's okay to include here, or if I should make a separate issue/PR.

@WilliamParker
Copy link
Collaborator

@sparkofreason I can't speak for anyone else, but I was thinking of having build-rule be in the dsl namespace, not the defrule! function. I think two different implementations of the latter in the macros and rule namespaces for clj and cljs respectively seems reasonable. Even if they were the same function you'd end up with two different implementations controlled by a switch between clj and cljs, just in the same fn body. I don't think build-rule has the same concerns though unless I'm missing something?

Regarding keyword names, so you're proposing something like

(defrule! :your-name-here rule-body-here)

That seems like something we could discuss, but I'd suggest logging another issue for it so that it can be discussed in a more visible way than deep in the comments of another issue.

@sparkofreason
Copy link
Contributor Author

sparkofreason commented Jan 4, 2018

Re: defrule! and build-rule namespaces, seems reasonable.

For keyword support, I was thinking of just adding the support internally so other DSLs could use it, rather than modifying the default clara DSL. The former is a pretty minor change change, just checking if the name is a keyword and transforming appropriately (see Provisdom@6dd773b). The latter seems like a bigger deal with no obvious benefit, and feels like it breaks the var/namespace abstraction in the current DSL.

@sparkofreason
Copy link
Contributor Author

See #371.

Updated Clojure implementation of defrule/defquerie utilize
build-rule/build-query.
Copy link
Collaborator

@WilliamParker WilliamParker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a few minor comments but basically the change looks good.

(cond-> (parse-query* binding definition {} form-meta)
name (assoc :name (if (com/compiling-cljs?)
(str (clojure.core/name (com/cljs-ns)) "/" (clojure.core/name name))
(str (clojure.core/name (ns-name *ns*)) "/" (clojure.core/name name))))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: it might be a little easier to read if we reduced the duplication in forming the name by factoring out the ns construction logic into a let binding and sharing the rest. Something along the lines of:

name (assoc :name (let [query-ns (clojure.core/name (if (com/compiling-cljs?) (com/cljs-ns) (ns-name *ns*)))]
                               (str query-ns "/" (clojure.core/name name)))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

definition (if properties (rest body) body)
{:keys [lhs rhs]} (split-lhs-rhs definition)]
(cond-> (parse-rule* lhs rhs properties {} form-meta)
name (assoc :name (if (com/compiling-cljs?)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the comment on building the name in build-query

CHANGELOG.md Outdated
@@ -5,6 +5,8 @@ This is a history of changes to clara-rules.
* Fix issue with incorrect namespace qualification of rule and query code in ClojureScript. See [issue 359](https://github.com/cerner/clara-rules/issues/359).
* Add clear-ns-productions! functionality to support clean reloading of rule and query definitions. See [issue 316](https://github.com/cerner/clara-rules/issues/316) for details.
* Support session inspection and fact-graph in ClojureScript. See [issue 307](https://github.com/cerner/clara-rules/issues/307) for details.
* Fix issue when compiling CLJS and session, rules, and fact-types are not in the same namespace or explicitly referred. See [issue 359](https://github.com/cerner/clara-rules/issues/359).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue 359 is in the changelog already on line 5. I believe I changed the changelog message when I did the release on that particular entry; basically I just wanted to specifically indicate what the issue was rather than just indicating that an issue existed. If you think it could be more clear I'm open to making changes, we'll want to have a single bullet point in the changelog though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just messed up the merge from upstream. Removing.

@@ -43,33 +43,25 @@
(coll? source) (seq source)
:else (throw (IllegalArgumentException. "Unknown source value type passed to defsession"))))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking back at this I'm not sure why we don't add a docstring to the var def the same way as in Clojure. That's the status quo though, we don't need to make changes in this PR.

Copy link
Collaborator

@WilliamParker WilliamParker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed a "query-ns" local let binding in the build-rule function to "rule-ns" using the GitHub web editor; I presume that was just a copy-paste error from build-query. Travis will verify that that compiles/builds properly.

This looks good to me now, setting aside the issues in #371. Since this one looks ready I suggest going and merging this; then I need to take a look at the issues in #371 when I get a chance.

Does anyone else want to look this over before merging?

@alex-dixon
Copy link
Contributor

LGTM.

Maybe we should add an issue to document how it can/should be used?

Didn't notice this before but looks like parse-rule* depends on the binding of ns. build-rule does also (because it calls parse-rule*) but also here: https://github.com/Provisdom/clara-rules/blob/d1c903dde46b9b3f364af4c66ca2116936d2b3c9/src/main/clojure/clara/rules/dsl.clj#L307

I don't know any ramifications to any of that...maybe we could consider mentioning it in the docstring like we have for parse-rule*?

Should there be an optional ns arg that defaults to *ns*? Not strictly necessary in CLJ since you can always call with (binding [*ns* my-ns] ...), but in CLJS doesn't seem like we give the user the option over what namespace the rule is defined in.

@sparkofreason
Copy link
Contributor Author

I'm working on a project (https://github.com/Provisdom/maali) which uses a custom DSL, and would be happy to contribute some documentation for DSL-builders based on that.

@WilliamParker
Copy link
Collaborator

@alex-dixon actually I think that the ns can be controlled in ClojureScript; see this code qualifying symbols. Basically it is just another dynamic variable to bind, namely *cljs-ns*. I'd be open to discussing allowing the user to pass in an explicit argument, but IMO we can do that in another issue, noting that we haven't released these changes yet even if merged. I do think we'd want to have the ns of the rule name and the logical namespace of the rule (i.e. the ns where the forms are evaluated) to be the same if the name corresponds to a var in a namespace. Technically this doesn't need to be the case, but it seems confusing if it isn't. This could related to #371 as well since that would change the expectations around production names.

@alex-dixon @sparkofreason Adding some documentation for this sounds like a fine idea. :) Perhaps an additional section under "Advanced Topics" at clara-rules.org? The source for the site is at https://github.com/cerner/clara-site in case you didn't know. Feel free to suggest improvements, submit PRs, etc. to that project as well, for this or other topics.

I'm going to merge this since it has been in its current state for a while without any objections to doing so. The further issues on this seem like things we can iterate on after a merge.

@WilliamParker WilliamParker merged commit af5403b into oracle-samples:master Feb 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants