tools icon indicating copy to clipboard operation
tools copied to clipboard

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

Open wilzbach opened this issue 7 years ago • 21 comments

tl;dr: @WebDrake recently changed the rdmd to automatically add semicolons for the last statement without effect (https://github.com/dlang/tools/pull/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

wilzbach avatar Feb 13 '18 04:02 wilzbach

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.

dlang-bot avatar Feb 13 '18 04:02 dlang-bot

This is nice if we manage to make it unambiguous.

andralex avatar Feb 13 '18 16:02 andralex

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?

marler8997 avatar Feb 13 '18 16:02 marler8997

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 avatar Feb 13 '18 17:02 wilzbach

Let's at least partially revert #303, s.t. we aren't limited by it for the future direction of this PR (-> https://github.com/dlang/tools/pull/318).

wilzbach avatar Feb 13 '18 17:02 wilzbach

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 b08b87e88bd94569b6e88376d79aeb6753b11fff (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 avatar Feb 14 '18 21:02 WebDrake

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.

WebDrake avatar Feb 14 '18 23:02 WebDrake

Could you provide examples you would find surprising?

marler8997 avatar Feb 14 '18 23:02 marler8997

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.

WebDrake avatar Feb 14 '18 23:02 WebDrake

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

marler8997 avatar Feb 14 '18 23:02 marler8997

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.

WebDrake avatar Feb 14 '18 23:02 WebDrake

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?

marler8997 avatar Feb 14 '18 23:02 marler8997

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.

WebDrake avatar Feb 15 '18 00:02 WebDrake

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.

marler8997 avatar Feb 15 '18 00:02 marler8997

Folks, I'd like to ask that we put the focus on the questions I asked above: https://github.com/dlang/tools/pull/317#issuecomment-365777561

WebDrake avatar Feb 15 '18 01:02 WebDrake

@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...

wilzbach avatar Feb 15 '18 01:02 wilzbach

@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.

WebDrake avatar Feb 15 '18 01:02 WebDrake

I'd like to suggest an alternative https://github.com/dlang/tools/pull/320 that IMO is better

timotheecour avatar Feb 15 '18 11:02 timotheecour

just make a --print flag

Superstar64 avatar Mar 26 '18 04:03 Superstar64

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

wilzbach avatar Mar 26 '18 07:03 wilzbach

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.

WebDrake avatar Mar 26 '18 19:03 WebDrake