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

:= macro equivalent? #46

Open
xificurC opened this issue Jan 19, 2022 · 13 comments
Open

:= macro equivalent? #46

xificurC opened this issue Jan 19, 2022 · 13 comments

Comments

@xificurC
Copy link
Contributor

The infix := operator, while looking nice, can be problematic. First of all it's impossible to align code correctly with it. Secondly there are edge cases like #40 and #42 where the expansion isn't working currently. Thirdly some people might prefer a more lisp-y style of defining the assertions.

Would it be complicated to allow asserting manually? Something like

(rcf/assert= (+ 2 3) 5)
@ggeoffrey
Copy link
Member

ggeoffrey commented Jan 19, 2022

We are considering compatibility with clojure.test/assert-expr. Then:

  • := would just be a convenient default,
  • clojure.test/is or rcf/is would allow infix prefix expressions.

@xificurC
Copy link
Contributor Author

you mean prefix, right?

@dustingetz
Copy link
Member

First of all it's impossible to align code correctly with it

@xificurC can you give me an example of what you mean by this?

@xificurC
Copy link
Contributor Author

xificurC commented Apr 7, 2022

Sure. The examples on the homepage like

{:a :b, :b [2 :b]} := {:a _, _ [2 _]}

look very nice, but once your test doesn't nicely fit on a single line, how do you align it to look well?

{:a :b, :b [2 :b]}
:=
{:a _, _ [2 _]}

This leaves a whole line for the poor :=, so lonely!

{:a :b, :b [2 :b]}
:= {:a _, _ [2 _]}

or

{:a :b, :b [2 :b]} :=
{:a _, _ [2 _]}

are just hard to read, especially when the code is bigger, one has to search for the :=, which is not good.

With a prefix operator this problem is solved

(assert= {:a :b, :b [2 :b]}
         {:a _, _ [2 _]}

@dustingetz
Copy link
Member

Thank you! You're right, we do write trailing := a bit.

So many people are asking for this that we are possibly willing to reconsider. So if I can follow up:

How would you write repl-style code that uses bindings to *1 etc?

 (tests
    "REPL bindings work"
    (keyword "a") := :a
    (keyword "b") := :b
    (keyword "c") := :c
    *1 := :c
    *2 := :b
    *3 := :a
    *1 := :c                   ; inspecting history does not affect history

    (keyword "d") := :d
    *1 := :d
    *2 := :c
    *3 := :b
    (symbol *2) := 'c          ; this does affect history
    (symbol *2) := 'd)

Note you don't want *1 to be bound to the result of the assertion, you want it bound to the LHS. Should we change the rules of *1?

@xificurC
Copy link
Contributor Author

xificurC commented Apr 7, 2022

The "in the wild" example doesn't seem to be using RCF?

How would you write repl-style code that uses bindings to *1 etc?

Care to elaborate how does infix notation pose challenges to this? If (keyword "a") := :a is written as e.g. (rcf/assert= (keyword "a") :a), we still know what LHS is.

If you're thinking of changes in a broader sense I'd like to propose to allow third-party extensions like matcher-combinators or https://github.com/lomin/sinho to integrate into rcf. It would also be nice to get reporting right

@dustingetz
Copy link
Member

dustingetz commented Apr 7, 2022

If you paste (assert= (keyword "a") :a) into the REPL, *1 would be expected to bind to the result of the assert (a bool or something), not :a, which is broken, we'd have to hack the behavior right?

Thanks for the sinho and matcher combinators links, we definitely feel the need for m/embeds etc, happy to document and support the best patterns after #50 ships.

Thanks for the ideas regarding reporting - can you please open a new issue for reporting and specify exactly how reporting should work?

@xificurC
Copy link
Contributor Author

xificurC commented Apr 8, 2022

I thought you are already hacking the REPL bindings?

user=> (keyword "B") := :a
:B
:=
:a
user=> *1
:a

Pasting the infix check into the REPL doesn't bind the result of (keyword "B") into *1

@dustingetz
Copy link
Member

Our worldview is that in this real-world RCF:

; https://clojuredocs.org/clojure.core/map
(map inc [1 2 3 4 5])
;;=> (2 3 4 5 6)

To learn what this does, you send this to the repl: (map inc [1 2 3 4 5]); *1 is now '(2 3 4 5 6)

The point of the RCF is to document what the target does (as if in a tutorial) without altering the target

@xificurC
Copy link
Contributor Author

xificurC commented Apr 8, 2022

in this real-world RCF

I don't think you documented what RCF stands for and I don't know what the acronym means, care to define it for me? Thanks!

The "REPL bindings work" example test you posted shows that *1 and friends are somehow made to work. That means you already have some code that makes them work. Can't you reuse that code for a prefix operator? I'm sorry but I still don't see how adding a prefix operator would make your work any harder.

@dustingetz
Copy link
Member

Ah the question is not can we do it, the question is what is the least surprising behavior. Consider:

in (tests (inc 1) := _) it is clear that *1 should bind to 2; in (tests (is (= (inc 1) 2))) what is *1? There are two reasonable behaviors - what is returned and what = returned. And neither of those behaviors are the thing you actually want, which is to refer to the result of (inc 1) in the next assertion so that you can do (tests (inc 1) := 2 (dec *1) := 1).

The root cause of the mess is that the testing boilerplate has infected the target expression.

RCF means rich comment form

@xificurC
Copy link
Contributor Author

xificurC commented Apr 8, 2022

I understand now, thanks.

I see a couple of options:

  1. prefix matchers will have to implement this behavior somehow. If the typical setup is (operator actual expected) then a simple default might already cover 90% of the use cases. If this is documented it doesn't seem too surprising, to me at least.
  2. prefix matchers will not support REPL history.

The root cause of the mess is that the testing boilerplate has infected the target expression.

But the point of the library is to test, you can't consider it boilerplate! :)

Anyway, there are other alternatives too

(testing (keyword "B")
  (= :B)
  (= (namespace %) nil)
  (= (name %) "B"))

Off the top of my head :)

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

No branches or pull requests

3 participants