-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[BUG] The reduce operator and :reduce run prefix behave differently on empty input #5246
Comments
Having thought about it some more, option 4 is probably not viable. Sure, it's fine to say "Use the first value in the list as the initial accumulator value" when the operation you're doing acts directly on the list contents, such as in the In other words, we can't implement the functional-programming function called Another thought: option 3 would require adding the possibility of filter run prefix suffixes, the very thing I argued against in #5166 (comment). But because of the |
Alternate possibility: let filter run prefixes take a second parameter the way operators do, so that instead of |
@rmunn I pondered over this difference when writing the Greater consistency is always preferable if possible. Personally I would favour #1 or #4 of the proposed solutions, since I assume #3 is not an option based on previous discussions. Now it could be that some functionality just cannot be implemented with filter run prefixes. However even if we were to decide against the Thoughts @Jermolene ? I have a packed schedule today, but I'll try to follow the discussion as and when I can. |
Just to focus on one part of this: oddly, I don't have much problem with the complexity of the multiply example:
My reasoning is that the rationale for the reduce operator is to give us filters that hugely expand what is possible. It's also pretty complicated conceptually, the kind of thing that learners would find hard to understand until they reach the point where they've experienced the dead-ends that it is intended to resolve. We mitigate the complexity by making the operator closely resemble the traditional way that the functionality is exposed in languages like JavaScript, albeit mapped through to TiddlyWiki idioms. That gives us undeniably verbose examples like the one above, but the result is very readable. The problem with introducing shortcut syntaxes is that the result will be more cryptic, requiring knowledge of slightly more special rules in order to understand it. Other possibilities to improve the readability of the example above might be:
|
I'm currently busy writing the demo I'm going to use to present to my colleagues so I don't have time to implement this today, but I'll be happy to implement option 1 starting tomorrow if nobody else gets to it first. I'd prefer option 4, but as I mentioned earlier it's not really viable. There's really no way to make our So I'm 👍 on option 1 now. |
@rmunn I wont be able to work on this before the weekend at the earliest, so please feel free tomorrow. We should add the multiply example to the docs as well. As for the filter visualizer @Jermolene, I think we can get that ready for 5.1.24. I have some thoughts and leads to discuss once 5.1.23 is released and hopefully things calm down a bit on my end, the volunteering is tying up a fair bit of time at the moment. |
By the way, I support option 1. |
Fixes TiddlyWiki#5246. Now the reduce operator and :reduce filter run prefix will both return empty output when their input is empty, so that both can be chained together with the else operator or :else prefix.
PR: #5255 |
I've chosen to go with option 1 for the PR, as this brings |
@rmunn thank you. |
Great, thanks @rmunn @saqimtiaz |
Fixes #5246. Now the reduce operator and :reduce filter run prefix will both return empty output when their input is empty, so that both can be chained together with the else operator or :else prefix.
5.1.23 blocker, IMHO, though it shouldn't take too long to resolve.
Describe the bug
The
reduce
operator and:reduce
prefix behave differently on empty input. One or the other should be adjusted so they behave the same. The operator will always produce output, even if its input is empty (the output is the value of the accumulator). The filter run prefix will produce an empty output if its input is empty, and will only produce output if there was at least one input item. This is because of theif(results.length > 0)
check in the filter run prefix version, which is lacking in the operator version.To Reproduce
Steps to reproduce the behavior:
reduce
operator:reduce
filter run prefixelse
operator (or:else
prefix)Expected behavior
Both the operator and the filter run prefix should have exactly the same behavior with regard to empty input. There are four ways I can think of to make this happen:
reduce
and:reduce
: if no initial accumulator value given, use first list item for initial value. Use accumulator value only if given. (On empty input, return empty output, just like option 1).Of those four, I strongly prefer the first one. I had to write extra documentation for
reduce
to make it plain that it will always produce output even when its input is empty, and although my reasons for that were based on the fact that that's how it works in functional programming, I'm re-evaluating that opinion. The fact that I had to write extra documentation just goes to show that it's non-intuitive for most people that empty input would produce non-empty output, and the right way for thereduce
operator would have been for it to produce empty output when its input is empty, because that's what other operators do and that's what most people will expect. Thankfully, 5.1.23 isn't released yet so it's not too late to change this.Option two is possible, but means that the
:reduce
prefix can never return, say, 0 when asked to sum up an empty list of numbers. And the current:reduce
example (which uses:else[[0]]
to specify a default value for empty input) would then be impossible.Option three is a major syntax change, which I don't like so late in the release cycle. However, there's one good point in its favor. What would you expect to happen when you write
1 2 3 :reduce[multiply<accumulator>]
? You'd expect to get 6, right? But instead you get 0, because the initial value of the accumulator is the empty string, and math operators treat the empty string as 0. To get what you actually expect from:reduce[multiply<accumulator>]
, you have to specify an initial accumulator value of 1. That's possible with the operator, but impossible with the filter run prefix!Option four is not a major syntax change, but is a bit of a behavior change. But if we're going to do it, now's the time. Basically, right now
reduce
acts like the functional programming function calledfold
, not like the function calledreduce
! The rules forreduce
in functional programming are "first item in list is initial accumulator value, and it's an error to pass in an empty list". The function where you specify an initial accumulator value and an input list, and the return value on empty input is the initial accumulator value, that one is calledfold
in functional programming. I do not suggest creating afold
operator, as most people will have no idea what it does! But I do think that it would be good for thereduce
operator to be able to actually act likereduce
, so that themultiply<accumulator>
result will work the way people expect.So I suggest that either we make the small change and let the
reduce
operator also return empty input (option one), or we take option four and let thereduce
operator have two different behaviors, one for when no initial accumulator value is passed, and one for when an initial accumulator value is provided. Whichever one we do, we'd better do it soon, because right now there's an inconsistency that we don't want to allow into the release.The text was updated successfully, but these errors were encountered: