Range operator too smart... or not?

I have a project where in a filter I do range[2],<number>,[1] to produce for instance 2 3 4 if number is 4. If number is 1, I intent to have no output. But I get 2 1, incrementing by -1 whereas I specified 1 as increment (without specifying this increment, the doc is clear that 2 1 is the output I shall get).

I think this would be better fixed, wouldn’t it?

It’s not possible to change that behaviour in a backwards compatible way.

The range syntax is: [range[<begin>],[<end>],[<step>]] So your <number> variable defined the end of the range. The behaviour for default values and user defined values have to be the same. So it doesn’t matter if you define the <step> as 1 or you don’t define it. We can’t have 2 different behaviours.

You may request a completely new operator or a new operator flag. [range:allow-empty[1],[1],[1]] or something similar.

IMO at the moment the range-operator is designed to always have an output.

What about

range:upwards[2],[1]

producing no output or

range:downwards[2],[1]

producing [[2]] [[1]] ? The default flag would be any, producing the current situation?

hmmm, may be :

range:inc[2],[1] ... for incrementing and 
range:dec[2],[1] ... for decrementing

We will need some additional well document rules eg:

  • flag inc will create an empty list if <start> >= <end> … no matter what <step> says
  • flag dec will create an empty list if <start> <= <end> … no matter what <step> says
  • If a flag is used IMO steps must be positive values.
    • Everything else will result in confusion
range:inc[2],[1],[1] ... empty list
range:dec[2],[2],[1]  ... empty list

There will still need to be more thoughts put into that, but I think that would make a proposal already.

@pmario

inc and dec are fine names for flags. Your rules are OK too.

However, I think that

range:inc[2],[2]

should produce [[2]]. This is the equivalent of for j := 2 to 2.

I think you should make an “feature request” issue at github. You may mix and match the rules for edge cases so the whole thing should be a consistent experience for new users.

It seems to me there are plenty of ways to handle the logic you seek that is possibly more meaningful than over loading the range operator.

Perhaps I don’t follow exactly but would this not work as expected and provide more visibility of what the filter is designed to do?

<$let number=7>
{{{ [<number>match[1]then[]] :else[range[2],<number>] }}}
</$let>
  • but then depending on the possible values of number you may ask different questions.
  • This approach is about “testing” or “conditioning” the input eg <<number>> before applying the range operator.
    • For example you may also want to exclude negative numbers
1 Like

@TW_Tones Your code is really complex. Also note that match is not enough. number may be zero or less so a compare operator is needed.

But mostly, the intent is not as obvious.

Yet I thank you for your post, as I had not realized that there was a “else” filter run prefix. I would hav coded your suggestion so:

<$let number = 7
  rg = {{{ [range<number>join[ ]] }}}
  range = {{{ [<number>compare:integer:gteq[2]then<rg>] }}}
>

the join is just here because we’re in a let and not in a set widget.

with :else I should avoid some computation for nothing. Good point!

@jypre

I must admit, perhaps there is something I don’t understand.

  • Perhaps I do now, the step value is an absolute value (nether +ve or -ve) and the range determines the “direction” to move from the first value to the second.
    • This has great utility in most cases, by assuming the direction intended, from the number provided.
    • If the sign on the step made a difference then this {{{ [range[10],[1],[2]] }}} would return nothing or an infinite list, because it would be starting with 10, add 2 until it reached 1?
  • I have left my notes below, written before this discovery in case it of any value. To you or others.


Given my confidence with filters, if I am missing something, I may be able to provide some ideas, otherwise if it is of no importance, to you or anyone, there is no need to proceed with the discussion :slightly_smiling_face:

Do I miss understand? you say;

Is this what you mean by complex?

  • If your requirement, as I read it, was to return nothing when number=1 there may be few if any further simplifications possible.
  • I would argue for the desired outcome it would appear necessary and in some ways reads more “true”.

But as I said;

I genuinely wish to understand, but so far given your question;

  • my friendly opinion would be no, because as I said;

However as you say, I observe if we do not provide a step value it assumes 1,

  • But the range operator also detects when the first number is greater than the second, and in the absence of a step number, defaults it to -1.
  • That is, it assumes “the intention is for a decrementing range”.

The following examples illustrate the behaviour.

{{{ [range[10],[1]] }}} vs {{{ [range[1],[10]] }}}

{{{ [range[10],[1],[-1]] }}} vs {{{ [range[10],[1],[1]] }}}

{{{ [range[10],[1],[-2]] }}} vs {{{ [range[10],[1],[2]] }}}

Thanks for raising the Issue.

@TW_Tones
By complex, I mean that it had more code than an extended range operator, and that the syntax for such an extended range operator was nor complex nor out of tone with other operator.

Also, tw has a way to tell range which is not common to other languages. take pascal:

for j:= 17 downto 3 do … end

the downo intead of to marks an explicit wish for -1 step which is akin to the extended range I propose.

Note that our change does not break previous code.

The current problem is that

{{{ [range[10],[1],[1]] }}}

produces

10
9
8
7
6
5
4
3
2
1

instead of an empty output. If it did, it would be fine enough for me to let things like that.