noprompt / meander

Tools for transparent data transformation
MIT License
922 stars 55 forks source link

Is there a simpler way to express complex relationships between two logic variables? #139

Closed lucywang000 closed 4 years ago

lucywang000 commented 4 years ago

Given a sequence [[1 2] [1 3] [2 3]], say I want to find all pairs [?x ?y] such that y = x + 1

I can think of using let to do this, but it's a bit verbose:

(me/search [[1 2] [1 3] [2 3]]
  (me/scan [?x (me/let [?y (inc ?x)]
                 ?y)])
  ?x)

Or using a custom syntax:

(me/defsyntax expr [expr]
  (if (me/match-syntax? &env)
    `(me/let [?result# ~expr]
       ?result#)
    &form))

(me/search [[1 2] [1 3] [2 3]]
  (me/scan [?x (expr (+ 1 ?x))])
   ?x)

I can improve the expr syntax so it could support (expr (+ 1 ?x) :as ?y). But I find this is a very common case and shall have some built-in support. m/let works but it's kinda too verbose. Am I missing anything?

lucywang000 commented 4 years ago

Here is the improved version of expr:

(me/defsyntax expr
  ([_expr]
   (expr _expr :as (gensym "?result")))
  ([_expr _ lvr]
   (if (me/match-syntax? &env)
     `(me/let [~lvr ~_expr]
        ~lvr)
     &form)))

(me/search [[1 2] [1 3] [2 3]]
  (me/scan [?x (expr (+ 1 ?x) :as ?y)])
  [?x ?y])
;; ([1 2] [2 3])
jimmyhmiller commented 4 years ago

So one way to accomplish this that is a bit different from what you are doing is with app.

(m/search [[1 2] [1 3] [2 3]]
  (m/scan [?x (m/app dec ?x)])
  [?x])

What we are saying here, is if we applied dec to this value would it match on ?x.

If we just switch around the app, we got something fairly close to what you are doing. For example:

(m/search [[1 2] [1 3] [2 3] [2 4]]
  (m/scan [(m/app #(* 2 %) ?x) ?x])
  [?x])
lucywang000 commented 4 years ago

@jimmyhmiller I tried to write that way, too, but decided that's far from ideal. IMHO, this kind of "describing the other way around" could lose a big part of the declarative power we want in meander.

In my original example, it's simple to rewrite the constraint y = x + 1 as x = y - 1, which is easy to code and at the same time not losing much readability, but what if a constraint like y = x * x + 1 ?

With the expr syntax we could still have pretty good-shaped solution:

(me/search [[1 2] [2 2] [3 10]]
  (me/scan [?x (expr (inc (* ?x ?x)) :as ?y)])
  [?x ?y])
;; => ([1 2] [3 10])

But to rewrite it the other way around, it's much more troublesome.

update: now I see the way of using m/app is almost the same as my expr approach, the trick is to use m/and to bind the original value to ?x and at the same time bind the transformed value to ?y.

(me/search [[1 2] [2 2] [3 10]]
  (me/scan [(me/and ?x (me/app square-inc ?y)) ?y])
  [?x ?y])