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

It's time to turn on lexical-binding #130

Closed
tarsius opened this issue Mar 18, 2015 · 31 comments
Closed

It's time to turn on lexical-binding #130

tarsius opened this issue Mar 18, 2015 · 31 comments

Comments

@tarsius
Copy link
Contributor

tarsius commented Mar 18, 2015

It is often impossible to use a -form inside a --form. This is the case when -form expands to --form or --some-other-form.

In the following example -first expands to --first (whose expansion contains --each-while which binds it),

(progn
  (--first (--first (message "--:-- %s %s (user error)" it it)
                    '("--inner"))
           '("--outer"))

  (--first (-first (lambda (-inner)
                     (message "--:-  %s %s (failure)" it -inner))
                   '(" -inner"))
           '("--outer"))

  (-first (lambda (-outer)
            (--first (message " -:-- %s %s (success)" -outer it)
                     '("--inner")))
          '(" -outer"))
  )

and produces

--:-- --inner --inner (user error)
--:-   -inner  -inner (failure)
 -:--  -outer --inner (success)
  • The first variant obviously cannot work, we refer to it twice expecting it to magically be different each time, i.e. it's a user error.
  • The third variant works correctly, it properly refers to the variable of the inner --filter. (Almost impossible to get that wrong).
  • The second variant should also work - but does not. its value should be "--outer" but is "-inner".

That is because -filter expands to --filter which uses --each-while which binds it around code which may contain uses of it which are intended to refer to a binding established outside of -filter (in this case the outer --filter.

This is a very serious flaw. Users cannot just use a -form inside a --form because -form may expand to --other-from which shadows the it from --form. This problem does not exist when -form does not expand to --other-form, or when -inner (or --inner even), is used in a place where the it of --outer is not in effect (E.g. inside COND of an outer --when-let).

Edit: in the second "safe" case, if the it let-binding is established carelessly, i.e. around code which is not actually supposed to have access to it, then this would fail too. I have a vague memory of that having happened to me, but don't remember what form it was.

Users should not have to know the implementation details to know which forms from dash nest and which do not. -map for example can be used as an inner form, because it just calls mapcar instead of expanding to --map (otherwise I would have used that as example :-).

Fortunately there is an easy fix - turn on lexical-binding in dash.el too. (Okay maybe that doesn't fix every such problem but the majority of such bugs would go away instantly).

Of course you could also refactor dash.el to fix each incarnation of this bug individually but the code would get quite a bit uglier and you would likely miss an incarnation or two. Forcing everyone to finally update to 24.1 is the lesser evil than having users wonder what the hell they are doing wrong, when infact the issue is a bug in dash.el not their understanding of how these forms are supposed to work. I for one have been wondering this quite a few times by now, but only now took the time to properly investitgate, I just didn't expect such a grave bug to linger for so long in such a popular library.

@Fuco1
Copy link
Collaborator

Fuco1 commented Mar 18, 2015

See also #56 (comment)

I'm ok with lexical binding, though it would require some changes to api (for example, -compare-fn is sometimes passed dynamically). But that should simply be refactored into -by version which takes the function as argument anyway (see the OP of post linked above)

@magnars
Copy link
Owner

magnars commented Mar 18, 2015

Yeah, sounds like a good reason to justify abandoning support for Emacs 23.

@occidens
Copy link
Collaborator

In addition to addressing the problem that @tarsius identifies, enabling lexical binding will also open up the range of possibilities for addressing #43, since closures could simplify iterating over non-list sequences (see also #80). Of course thats not the only way to solve it, but at least we'd have all of Emacs Lisp's current tools at our disposal.

That said, does anyone have a sense of how many people at this point would be affected by this change? I suppose the only ones who would be really caught off guard would be those who are using Emacs 23 and getting dash.el using package.el.

@magnars
Copy link
Owner

magnars commented Mar 19, 2015

It was @purcell who convinced me to keep supporting E23 in the first place. Any thoughts, Steve?

@purcell
Copy link

purcell commented Mar 19, 2015

I feel like requiring Emacs 24 is a very big step, since possibly hundreds of packages directly or transitively depend on dash, and all of those will cease to be installable on Emacs 23.

(I have no opinion on the specifics of this issue, other than that anaphoric macros are the work of the devil. ;)

@rejeep
Copy link
Contributor

rejeep commented Mar 19, 2015

Sometimes the only way to get people to upgrade is to break their stuff. I say drop Emacs 23 support!

@tarsius
Copy link
Contributor Author

tarsius commented Apr 5, 2015

24.1 was released three years ago, see https://www.gnu.org/software/emacs/history.html. It's laudable that we don't needlessly make it harder for users, but at some point it has to stop. After all "installing Emacs manually" is a skill worth having.

@purcell Yes, it is a big step, but it's about time we take it. You instructing new elisp authors to preserve backward compatibility when they submit their first creation, doesn't exactly make it easier for us maintainers of popular libraries/packages. Not saying it was wrong of you to do that, just that it has downsides too: what could have been a smooth transition now is something that will affect many users at once, when the big players finally start moving.

I think it would be a good idea for us more active maintainers of third-party extensions to take this step together - so that the blame doesn't fall on which ever popular package takes it first.

Magit's next release, 2.1.0, will require Emacs >= 24.2 (and Git >= 1.9.4 (sadly still)). So you could just sit it out, I suppose :-)

I have no opinion on the specifics of this issue, other than that anaphoric macros are the work of the devil. ;)

https://www.youtube.com/watch?v=ZRXGsPBUV5g

@purcell
Copy link

purcell commented Apr 5, 2015

You instructing new elisp authors to preserve backward compatibility when they submit their first creation, doesn't exactly make it easier for us maintainers of popular libraries/packages. Not saying it was wrong of you to do that, just that it has downsides too: what could have been a smooth transition now is something that will affect many users at once, when the big players finally start moving.

I generally tell authors who are using minor bits of new functionality (setq-local, declaring lexical-binding etc) that they either need to depend on (emacs "24") or make compatibility changes, then leave the decision up to them. I actually don't care what they do, as long as the declared dependencies are accurate.

And I'm not saying that dash shouldn't be updated to require Emacs 24: just warning that it's going to affect a lot of stuff. It would be relatively straightforward to find the github usernames of authors whose MELPA packages depend directly or transitively on dash, and CC them on this github issue, so perhaps that would be a helpful warning and step along the way. The archive.json or the standard archive-contents file provides this information, in conjunction with recipes.json.

@tarsius
Copy link
Contributor Author

tarsius commented Oct 21, 2015

Please consider turning on lexical-binding now, even though dash still supports older Emacsen with don't know about lexical-binding and will just ignore it. That way this bug is fixed for users of recent Emacsen and maintainers of packages which depend on dash can additionally depend on Emacs v24.1 if they want to be sure their package is not affected by this issue.

I'm ok with lexical binding, though it would require some changes to api (for example, -compare-fn is sometimes passed dynamically).

That's not a problem because this variable is properly defined using defvar and that causes it to have dynamic scope, even when lexical-binding is t.

@magnars
Copy link
Owner

magnars commented Oct 21, 2015

If we can fix a bug by adding the lexical binding pragma, without breaking older Emacsen, I'm certainly all for it. Is that all it takes, @tarsius?

What I'd really like is to move dash-functional into dash core as part of a breaking change 3.0 release, which would then drop support for E23. Do you have any statistics about E23 usage from Melpa, @purcell?

@purcell
Copy link

purcell commented Oct 21, 2015

If we can fix a bug by adding the lexical binding pragma, without breaking older Emacsen, I'm certainly all for it. Is that all it takes, @tarsius?

Yes. Just be careful that you don't actually start relying on lexical binding behaviour before you're ready to declare a dependency on Emacs 24.

What I'd really like is to move dash-functional into dash core as part of a breaking change 2.0 release, which would then drop support for E23. Do you have any statistics about E23 usage from Melpa, @purcell?

No, we have no way to tell how many people are using E23. It'd probably be worth figuring out the intersection of the packages which depend directly or transitively on dash and the packages which don't already depend directly or transitively on E24 - they would be the ones affected.

magnars added a commit that referenced this issue Oct 21, 2015
**Please note** The lexical binding in this file is not utilised at the
moment. We will take full advantage of lexical binding in an upcoming 2.0
release of Dash. In the meantime, we've added the pragma to avoid a bug that
you can read more about in issue #130
@magnars
Copy link
Owner

magnars commented Oct 21, 2015

I have added the lexical binding pragma to dash.el and released 2.12.1 with this fix. Thanks for the insight, @purcell and @tarsius.

@tarsius
Copy link
Contributor Author

tarsius commented Oct 21, 2015

😋 Thanks!

We will take full advantage of lexical binding in an upcoming 2.0

2.0?! That should be 3.0 right?

@tarsius
Copy link
Contributor Author

tarsius commented Oct 21, 2015

2.0

Ah, I see you already noticed that typo.

@Fuco1
Copy link
Collaborator

Fuco1 commented Oct 21, 2015

Awesome stuff, thanks for moving this forward :)

tarsius added a commit to magit/magit that referenced this issue Oct 22, 2015
Most importantly depend on a snapshot of `dash', because *any* snapshot
is larger than 2.MM.S but we really do need v2.8 at the very least (for
`-let').  Actually depend on a snapshot corresponding to v2.12.1 because
that fixes magnars/dash.el#130.
npostavs added a commit to npostavs/emacs.d that referenced this issue Oct 23, 2015
el-get: useful warnings for stale recipes
magit: new buffer name format system
dash: 2.12.1, see magnars/dash.el#130
ulm added a commit to gentoo/gentoo that referenced this issue Oct 27, 2015
Update HOMEPAGE and LICENSE. Depend on >=app-emacs/dash-2.12.1,
see <magnars/dash.el#130>.
Thanks to Jonas Bernoulli for pointing this out.

Use defaults of elisp.eclass for all phase functions.
Require Emacs 24 at least (should really be 24.4).

Package-Manager: portage-2.2.23
@CestDiego
Copy link

Thank you for keeping maintaining this :)

@Fuco1
Copy link
Collaborator

Fuco1 commented May 1, 2016

Anyone has something more to say, or can we close this? Lexical binding has been on for half a year and no problems were reported so far.

@tarsius
Copy link
Contributor Author

tarsius commented May 1, 2016

Hasn't caused any issues for me. Thanks, by the way!

@magnars
Copy link
Owner

magnars commented May 1, 2016

👍

@magnars magnars closed this as completed May 1, 2016
tarsius added a commit to magit/magit-popup that referenced this issue Nov 20, 2017
Most importantly depend on a snapshot of `dash', because *any* snapshot
is larger than 2.MM.S but we really do need v2.8 at the very least (for
`-let').  Actually depend on a snapshot corresponding to v2.12.1 because
that fixes magnars/dash.el#130.
@basil-conto
Copy link
Collaborator

What I'd really like is to move dash-functional into dash core as part of a breaking change 3.0 release, which would then drop support for E23.

Emacs 23 support was dropped in version 2.14.0 over a year ago (e9919f6), so how do people feel about merging dash and dash-functional now?

@magnars
Copy link
Owner

magnars commented Aug 17, 2019 via email

@basil-conto
Copy link
Collaborator

How does it break on E23?

How does what break?

@magnars
Copy link
Owner

magnars commented Aug 17, 2019 via email

@Fuco1
Copy link
Collaborator

Fuco1 commented Aug 17, 2019

As far as I understand neither package works with Emacs older than 24. So merging them will change nothing. If someone uses them on E23 they probably vendor the libraries already since Melpa does not allow installing older versions and is by far the most used channel. Actually I'm not even sure if you can use Melpa (package.el) on E23.

@purcell
Copy link

purcell commented Aug 18, 2019

As far as I understand neither package works with Emacs older than 24. So merging them will change nothing. If someone uses them on E23 they probably vendor the libraries already since Melpa does not allow installing older versions and is by far the most used channel. Actually I'm not even sure if you can use Melpa (package.el) on E23.

Agreed, I think you can safely assume that nobody is trying to use any subset of MELPA on E23.

@magnars
Copy link
Owner

magnars commented Aug 18, 2019 via email

@basil-conto
Copy link
Collaborator

So what can we make better with dropped E23-support without breaking people's code on higher Emacs versions?

My original question was whether merging dash and dash-functional would be desirable now that both assume lexical-binding. Previously, when dash still supported E23, merging the two was not possible. The benefit of merging them now is increased consistency and simplicity at the package level.

The complication in merging them is how to signal that the new merged dash supplants the old standalone dash-functional. I would hope that ELPA provides a way to specify such a conflict between specific versions of different packages.

@magnars
Copy link
Owner

magnars commented Aug 18, 2019 via email

@basil-conto
Copy link
Collaborator

That’s not a good enough reason to break lots of code that we have no control over.

How would merging the two packages break existing code?

I suggest we close this issue

Actually, if this discussion is going to continue at all, it deserves its own new issue.

and accept that we have two packages.

I'm sure everyone's already accepted this, but that doesn't mean things can't evolve. ;) The whole point of a separate package AIUI was due to lexical-binding compatibility. Now that this is no longer an issue, why not revisit dash packaging with an eye towards improvement? (No-one has suggested an explicitly backward-incompatible change so far.)

@magnars
Copy link
Owner

magnars commented Aug 18, 2019 via email

@Fuco1
Copy link
Collaborator

Fuco1 commented Aug 18, 2019

If both packages implement same functions than what gets used depends entirely on the random order in which Emacs loads these packages. So we need to fix this by reliably only running the functions from the main package.

What we could do is release a new version of dash-functional which would just require dash thus being essentialy a no-op. This would require people updating both at the same time though.

Also we could require dash-functional from newer dash and then (since it loads before dash loads) it would just overwrite the functions with the ones from itself.

I'm not sure if this works how I imagine it, though I'm pretty sure it does and therefore it could actually work in a BC manner. Someone ought to test first though.

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

8 participants