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

rdmd: and with a last statement without effect should print it #317

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

wilzbach
Copy link
Member

@wilzbach wilzbach commented Feb 13, 2018

tl;dr: @WebDrake recently changed the rdmd to automatically add semicolons for the last statement without effect (#303), but I think we can do a bit better and make rdmd even more user-friendly.


At the moment we are in the unique position to introduce this without any breakage as until now rdmd didn't allow this:

> rdmd --eval='"hello"'
/tmp/.rdmd-1000/eval.CFD785092747553A81673B51A318D6AE.d(18): Error: "hello" has no effect

The new behavior will default to writeln if the last statement would have no effect:

> rdmd --eval="hello"
hello

A few more examples:

$(H4 Use rdmd as your calculator)

> rdmd --eval="2 + 2"
4
> rdmd --loop='line.splitter(",").take(1)'
16
> seq 5 | rdmd --loop='stdin.byLine.drop(2).joiner(newline)'
2
3
4
5

...


I'm aware that this is a "poor man's solution" and could be done better, but look at it this way:

  • it doesn't break any code
  • it's a 6 LoC change (imports and tests not counted)
  • it covers >95% of all uses
  • it increases the user-friendliness of rdmd and people who use it like perl's eval

Q: Oh come on, people can just type writeln themselves

Yes of course, but for CLI tools every keystroke matters and REPLs print to stdout.
Also it's a nice default for these two specific flags (in comparison to the hard error).

CC @CyberShadow @marler8997 @WebDrake @andralex

@dlang-bot
Copy link
Contributor

Thanks for your pull request, @wilzbach!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

@wilzbach wilzbach force-pushed the writeln-convenience branch 2 times, most recently from 77a3038 to b988788 Compare February 13, 2018 05:42
rdmd.d Outdated
if (code.length > 0 && code[$ - 1] != ';')
code ~= ';';
Copy link
Member Author

Choose a reason for hiding this comment

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

FYI: this change comes from #303 which hasn't been released yet.

@andralex
Copy link
Member

This is nice if we manage to make it unambiguous.

import std.typecons : tuple;
foreach (t; [tuple(`"eval_works"`, "eval_works"),
tuple("2 + 2", "4"),
tuple("2.write; 2 + 2", "24")])
Copy link
Member

Choose a reason for hiding this comment

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

Try a test with "if(0) {} 2 + 2" - that won't work

@marler8997
Copy link
Contributor

One thought comes to mind, would we need to support the case where a developer doesn't want the return value of their expression to be printed?

@wilzbach
Copy link
Member Author

One thought comes to mind, would we need to support the case where a developer doesn't want the return value of their expression to be printed?

Then he can add a semi-colon (as before).

Try a test with "if(0) {} 2 + 2" - that won't work

It would error too at the moment - but sure that's easy enough.
Well, at the moment the logic is super simple and there are these pendantic examples of e.g.

"foo" ~ ";" ~ "bar"

The point was that everything without an ending semi-color fails atm and this trick will do the sensible thing in the vast majority of cases.
If this isn't acceptable, I can also look into a minimal tokenizer for expressions, but that would require a lot more effort.

wilzbach added a commit to wilzbach/tools that referenced this pull request Feb 13, 2018
@wilzbach
Copy link
Member Author

Let's at least partially revert #303, s.t. we aren't limited by it for the future direction of this PR (-> #318).

dlang-bot added a commit that referenced this pull request Feb 13, 2018
Partially revert #303 until #317 is resolved
merged-on-behalf-of: Sebastian Wilzbach <sebi.wilzbach@gmail.com>
wilzbach added a commit that referenced this pull request Feb 13, 2018
dlang-bot added a commit that referenced this pull request Feb 13, 2018
Revert "Partially revert #303 until #317 is resolved"
merged-on-behalf-of: Vladimir Panteleev <github@thecybershadow.net>
@WebDrake
Copy link
Contributor

tl;dr: @WebDrake recently changed the rdmd to automatically add semicolons for the last statement without effect (#303)

For the record (we've already discussed this in other PRs): no, I didn't.

Automatically adding closing semicolons to code provided via --eval has been there since the flag was first introduced in commit b08b87e (back in 2009).

What #303 changed was to stop that closing semicolon being added if one was already there (to avoid empty statements at the end of the program, which some compilers object to).

@WebDrake
Copy link
Contributor

BTW, I would like to understand better the particular motivation behind this feature, other than that it seems cool. "95% of all uses" are mentioned above. What are those uses? Are they motivated by real, reported user needs? Or just imagination about what users might do?

"people who use it like perl's eval" are mentioned, but AFAICT, perl -e doesn't do anything like this. perl -e '"Hello World!\n"' gives you no output, for example; you have to explicitly use print.

I ask all this because it seems an odd thing to magically special case the behaviour of D code for this one specific use-case.

The good old principle of least surprise suggests that --eval should evaluate the D code you give it, no more, no less. I'm not sure it's wise to second-guess our users by magically giving them different behaviour from that, for a very narrow subset of the possible code that could be provided.

@marler8997
Copy link
Contributor

Could you provide examples you would find surprising?

@WebDrake
Copy link
Contributor

Could you provide examples you would find surprising?

If I ask for a piece of D code to be evaluated, I expect for that D code to be evaluated as it is. I do not expect for some extra magic to be done that prints out something I didn't ask to be printed.

I certainly do not expect the difference between nothing happening versus something happening to be whether or not I put a semicolon on the end of the code I provided. That's really weird special-casing.

In a case like this, the onus really has to be on the feature proposal to explain what is so good and valuable about it that we need to break with the intuitive and longstanding behaviour of --eval.

--eval is going to be easiest to understand and use if it just treats the D code it's given like any other D code. Which (apart from adding trailing semicolons, which is just about tolerable as a helping hand) is what it's always done.

@marler8997
Copy link
Contributor

Most of that was just repeating what you already said...

You said it would be surprising, can you provide surprising examples you were thinking of so the rest of us can evaluate it for ourselves? Surprise is subjective after all

@WebDrake
Copy link
Contributor

NO. This is fundamentally the wrong way to be thinking about this.

--eval is a feature which has been part of rdmd, virtually unchanged (apart from bugfixes) for almost 10 years now. Its behaviour is logical, simple to understand, and consistent with similar features of other languages (e.g. perl -e).

The proposed feature is a breaking change to one of the most widely used pieces of software in the D ecosystem. The onus is 100% on the feature proposal to provide significant justification for such a change.

Any other attitude is a fundamentally irresponsible attitude to maintaining a tool that LOTS of people rely on.

@marler8997
Copy link
Contributor

marler8997 commented Feb 14, 2018

I dont know why your assuming how I'm thinking about this. I'm just asking you to provide examples to backup one of your arguments.

Also, how would this be a breaking change?

@WebDrake
Copy link
Contributor

I dont know why your assuming how Im thinking about this.

I've defined my sense of "surprising" clearly: it's surprising if D code provided to --eval behaves differently from the same D code in other circumstances ... especially if that's a break to very long-standing previous behaviour.

Indeed, it's a pretty basic principle of maintaining widely used software that behavioural changes visible to the user should be carefully thought about and justified. From this point of view any such behavioural change is by definition surprising.

Asking questions about why that would be surprising strongly suggests a lack of understanding of that principle. Which is why I suggest the thinking is wrong-headed.

The focus needs to be on why the feature is good and important enough to justify the surprise.

Also, how would this be a breaking change?

Because rdmd --eval would produce different behaviour for the same input before and after this change. We do not know, and should not assume, what aspects of the long-standing existing behaviour people are relying on.

@marler8997
Copy link
Contributor

I was just asking questions to gather data. But every time I ask a question you start making assumptions about what Im thinking then proceed to rant and repeat yourself. I'm going to disengage from this, conversing with you is a time sink with no benefit.

@WebDrake
Copy link
Contributor

Folks, I'd like to ask that we put the focus on the questions I asked above: #317 (comment)

@wilzbach wilzbach added the PR.Blocked A PR blocker by another issue / PR, external to the PR (as opposed to WIP) label Feb 15, 2018
@wilzbach
Copy link
Member Author

@WebDrake no worries. This is moving nowhere unfortunately for now because the logic to insert a writeln by default is too simplistic.

FWIW I doubt that - if it works perfectly - could break much code, because --eval and --loop are only used for temporary scripts and we are talking only about the cause in which a semi-colon is already missing, but yes of course it's a concern.

I also get the point that printing writeln might not be expected by the user who just accidentally forgot a semi-colon.

Anyhow as long as we can't easily rewrite the last expression (or have a plan on how to do this), there's really no need to worry that this will be added.

-> setting this to BLOCKED for now. Sorry about this, it was a nice idea, but it seems it's not that easy in practice...

@WebDrake
Copy link
Contributor

@wilzbach OK, but I still think it would be good to be clear about the motivation and real use-cases if this is ever to be un-blocked.

"It's a nice idea" is a risky basis for behavioural change if it's not backed up by real, concrete user needs (which is not the same as "Well, then you could do THIS and THIS...").

There's a LOT of value in the simplicity and clarity that says, "--eval should just give you the exact same behaviour you would get from that D code in any other circumstances".

As I noted, perl -e seems to behave exactly like that, no magic added.

@timotheecour
Copy link
Contributor

I'd like to suggest an alternative #320 that IMO is better

@Superstar64
Copy link
Contributor

just make a --print flag

@wilzbach
Copy link
Member Author

wilzbach commented Mar 26, 2018

just make a --print flag

That would be --eval-p and --loop-p then which we wouldn't be a nice interface (only "power-user" would remember this) and (2) CLI flags come at a premium nowadays. I sincerely doubt that you could convince anyone to add two new flags for this.

Anyhow, I'm not really sure on the best way to solve this problem here:

  • the current approach has ambiguities
  • we could add a proper D lexer, but that would bloat rdmd heavily
  • the @ idea by @timotheecour is nice, but I'm not sure whether this feature is important enough for such a special case

I'm currently thinking about limiting this to a smaller subset, e.g. insert a "writeln(...);" if there's no semi-colon at the end and the beginning of the eval code can be reached by only seeing see a-Z_.()"!'+-=, but I'm not sure yet whether this would cover enough cases to be useful or maybe be even more confusing.
OTOH it would support this already: "myFile".readText.filter!(a => a == 'a').count

@WebDrake
Copy link
Contributor

I'm currently thinking about limiting this to a smaller subset, e.g. insert a "writeln(...);" if there's no semi-colon at the end and the beginning of the eval code can be reached by only seeing see a-Z_.()"!'+-=, but I'm not sure yet whether this would cover enough cases to be useful or maybe be even more confusing.

The core problem here is that whatever solution is taken, it involves creating some magical alternative language that looks like D but behaves differently in very subtle ways.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs rebase needs work PR.Blocked A PR blocker by another issue / PR, external to the PR (as opposed to WIP) stalled
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants