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

Implement -cond-> and -cond->> macros. #349

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

kinderferraz
Copy link

Hi.

This is the implementation I mentioned in #330.
My main difficulty when porting this from Clojure's definition was regarding make-symbol vs. intern.
When using make-symbol, I ran into the same problem described here/ that is, when calling the macro, only the last expression would be tested and run.
Therefore I decided for intern. In my tests it did not interfere with other variables, but more tests are required.

Hope this code fits with the package.
Also, some feedback would be appreciated, since this is my first PR.

@magnars
Copy link
Owner

magnars commented Dec 8, 2020

Hi!

First of all, thanks for your work on this. :)

There seems to be a long-standing issue with the CI-tests, which is why the checks are failing. I haven't been active in maintaining this repo for quite some time. Sorry about that.

Now, I have a few comments:

  • please add some examples in dev/examples.el
  • the README is actually generated from the docstring and the examples, so no need to update that manually.

Also, I would prefer if -cond-> had the same semantics as cond-> in clojure. Fewer surprises that way. From what I could tell from the examples in your README, it differs in two major ways:

  • the it binding
  • a default clause with no predicate in front.

I might have read that last one too quickly, so do correct me if I am wrong.

I haven't enough Emacs Lisp knowledge to know if intern is a good substitute for make-symbol. Maybe @Fuco1 has some ideas?

Cheers!

@kinderferraz
Copy link
Author

kinderferraz commented Dec 8, 2020

Hi,
you're welcome.
Thank you for answering.

please add some examples in dev/examples.el
the README is actually generated from the docstring and the examples, so no need to update that manually.
You're welcome :)

I wasn't aware of these documenting features, dev/examples.el is updated now.
Should the updated README be committed?

As for the differences from Clojure's definitions,

  • a default clause with no predicate in front

I might have read that last one too quickly, so do correct me if I am wrong.

Indeed, there was a mistake in my examples, it must be correct now.

  • the it binding

This came as a surprise when i was testing. I tested a variable with the same name as the internally bound one, trying to see if there was a bug or an error, and this showed up. So I changed the symbol to it as to match other macros. I'm afraid I can't offer a solution to this.

I haven't enough Emacs Lisp knowledge to know if intern is a good substitute for make-symbol.

Just tested a version with make-symbol, and everything worked fine...
had to remove a few commas, though, but they shouldn't be there in the first place.

(Also, I've completely mistyped last commit's message, sorry
hahahahaha)

@yyoncho
Copy link
Contributor

yyoncho commented Dec 8, 2020

Using it is not desired here since it will unexpectedly shadow the it variable which is commonly used in other dash macros. Introducing it should happen in --cond->. You may generate a symbol following what dash--match-make-source-symbol do.

@kinderferraz
Copy link
Author

I'll look into it! :)

Just out of curiosity, I tested some pieces of code, it would appear that these macros don't overshadow each other. Some of these lines were made complex on purpose.

(-cond->> '(10 20 30 40 50 60)
  (listp it) (--map (/ it 2))
  (numberp it) (--filter (--> it (% it 2) (= it 0)))
  (numberp (car it)) (--reduce (format "%s-%d" acc it))) ;; => "5-10-15-20-25-30"

(--map (-cond-> it
         (numberp it) (/ 2)
         (odd? it) (number-to-string))
       '(10 20 30 40 50)) ;; => '("5" 10 "15" 20 "25")

(--> "abc"
     (-cond-> it
       t (concat "def" "ghi")
       (string= it "abc") it
       (string= it "abcdefghi") (split-string "" t)) ;; =>  '("a" "b" "c" "d" "e" "f" "g" "h" "i")
     (--reduce-from (concat acc it) "" it)) ;; => "abcdefghi"

Unfortunately, this is no exhaustive test of all available macros, and breaks uniformity of available macros.

Use `dash--match-make-source-symbol` to make different, unused symbol
every time. Apparently, symbol does not need to be interned, Just
consistently evaluated.
@kinderferraz
Copy link
Author

kinderferraz commented Dec 9, 2020

Just committed using dash--match-make-source-symbol, with updated examples.
There should be no way to access current value in tests, and unless there's a variable named it outside the macro, there should be an error. :)

I guess this leaves for two options for anaphoric versions of this macro, one with --> macro, which would prevent accessing current values in tests, i.e:

(-cond--> '(10 20)
    t (--map (/ it 2) it)
    (= 1 1) (setf (car it) 11) ;; => '(11 10)

and another following the last attempt, exposing the symbol it. Let me know which one is best! :)

@magnars
Copy link
Owner

magnars commented Dec 9, 2020

I don't think it makes any sense in cond->, but if I'm wrong it should certainly be named --cond-> and --cond->>, to match the other anaphoric macros.

@kinderferraz
Copy link
Author

I see.
You'll find current committed code to behave as you expect, and strictly follows Clojure's semantics.

@magnars
Copy link
Owner

magnars commented Dec 11, 2020

I'm glad to hear that. :) It seems the example generating code needs to be run another time, since the examples in the readme are outdated, but otherwise this looks good to me.

@kinderferraz
Copy link
Author

I wasn't sure if i should commit the generated README.
I'll do it right now :)

@magnars
Copy link
Owner

magnars commented Dec 11, 2020

Well done. In order for this code to be allowed into the ELPA repo, all contributors must sign a copyright assignment.

basil-conto said:

Just email the completed form at https://git.savannah.gnu.org/cgit/gnulib.git/tree/doc/Copyright/request-assign.future to assign@gnu.org; they'll give you further instructions. It's generally a matter of scanning and emailing a scanned document, or in some countries digitally signing a document. See (info "(emacs) Copyright Assignment") for more.

@kinderferraz
Copy link
Author

Sent! 😄
I'll leave a message here when the process is completed.

Copy link
Collaborator

@basil-conto basil-conto left a comment

Choose a reason for hiding this comment

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

Thanks for working on this! See some comments below.

README.md Outdated Show resolved Hide resolved
dash.el Outdated Show resolved Hide resolved
dash.el Outdated Show resolved Hide resolved
dash.el Outdated Show resolved Hide resolved
dash.el Outdated Show resolved Hide resolved
dev/examples.el Outdated Show resolved Hide resolved
dev/examples.el Outdated Show resolved Hide resolved
dev/examples.el Outdated Show resolved Hide resolved
dev/examples.el Outdated Show resolved Hide resolved
dev/examples.el Outdated Show resolved Hide resolved
@basil-conto
Copy link
Collaborator

My main difficulty when porting this from Clojure's definition was regarding make-symbol vs. intern.

All symbols that you normally refer to while using Emacs, such as the setq, var, and nil in the form (setq var nil) are interned, i.e. they are stored in an obarray. Obarrays can be thought of as weird hash tables, where interning a symbol is the same as inserting it into a bucket in the obarray that is determined by hashing the symbol's name. Emacs stores variable, function, and keyword symbols, etc. in a global obarray called obarray, but you can also create your own obarrays and intern symbols in them.

Since interning symbols by default happens in the global obarray, macros that create local variable bindings have to be careful to use symbols that are guaranteed to not clash with the forms that are passed to those macros as arguments. Enter make-symbol: it returns a symbol object (with the usual value, function, property list slots, etc.) that is crucially not interned in any other obarray, so binding it to different values will not overwrite any other variables in the program.

See more at (info "(elisp) Symbols"), specifically under (info "(elisp) Creating Symbols").

@basil-conto basil-conto added copyright Waiting for FSF copyright assignment enhancement Suggestion to improve or extend existing behavior labels Dec 14, 2020
@kinderferraz
Copy link
Author

@basil-conto
Thank you so much for this review!!
I'll read it all and start working on this asap.

- Use two pops and a push to build steps from
  clauses, using built-in functions only

- Favor let over -let

- If no clauses, return expression unchanged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
copyright Waiting for FSF copyright assignment enhancement Suggestion to improve or extend existing behavior
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants