So basically, it’s a magical operator that does whatever you want it to do. This is what I mean by your proposal lacking coherence. Make a SINGLE proposal that has merit; don’t try to make five or six proposals in one.
To be clearer for those not familiar with that: For example 'a'..'zz' produces the strings “a”, “b”, “c”, …, “x”, “y”, “z”, “aa”, “ab”, …, “zy”, “zz”. That’s done via the succ method returning the “successor” of a value, for example "ez".succ gives "fa". In Python, that could be a __succ__ method, so that x..y with arbitrary objects gives an iterator yielding x, then x.__succ__(), etc, repeatedly calling the method until y is reached.
You mentioned this previously why mention it again. I already said that for this post there will be focus on one operator .. and it’s functionality. The Operators in Python can have multiple functionalities depending on the types of the operands given.
But there’s currently no operator that checks the operand types and does different things depending on that, is there?
What about the step? Can one do “a”…“z”.step(2) in ruby?
With parentheses, yes: ("a"..."z").step(2). (Though I think that computes all strings of the range and yields only every second, doesn’t just compute every second.)
You can do things such as 1.1+2 which has different types (float and int) values. The proposal can be considered similar to this for object type.
But it’s not the operator checking types and choosing what it does. That calls (1.1).__add__(2), and that method determines what happens. The operator doesn’t know or care.
Yes inside that .__add__ method, or .__transform__ for this proposal, we can check what’s passed to that method and then proceed to the result value.
Do you suggest that we create another operator for pipe functionality (input the current value into the next function) rather than replying on .. operator. My idea is to reuse the same operator relying on input types rather than needing another operator for this.
No, I was rather just nitpicking about your description of the operator checking the types. Yes, the __transform__ method could check the type and act accordingly, for example int.__transform__(a, b) could return a range if b is an int or call b(a) and return its result if b is a callable.
So I guess step is simply a method of iterator.
I quite like how ranges work in Ruby. class Range - Documentation for Ruby 3.2
Mostly that it uses the same object for both ranges and slices and also allows for infinite ranges.
It would be possible to adapt it:
- Implement infinite ranges
- Support ranges as an alternative to
slicein__getitem__. - Slice syntax in
__getitem__then made into generic operator, which works outside of it and producesrangeobjects - Deprecate
slice
Then, could build on top of range as appropriate, implement it for strings, dates, etc…
Or calling b(a) could be the default fallback in case the transform method isn’t implemented (at all or for the type(s)). Like with the __radd__ fallback.
I am having troubles understanding what would be the use of it. I.e.: why 1..int is better than int(1)?
If there is a need to seperate the range and pipe functionality, we can use the operator .. for ranges while :: for pipe functionality.
a=(1..10) # list of values from 1 to 10
a_str=a::str # str(a)
Thank you
I don’t know. That’s no range, so it falls under my “those other usages you showed just look odd/bad. Why would you want to write those instead of the normal way?” ![]()
Given that slices currently use ::, it would be more natural to use it for ranges as well. Same as matlab does. Also, if implemented as ternary, it would support step too.
It can be used for flexibility purpose. It’s more prominent in flexibility if you need to nest multiple functions: a..map..filter that filter(map(a)).
Yes, I prefer this as well for ranges to use :: operator. Currently object methods are the ones using . so it’s more suitable to use for pipe functionality as well.
I would rather see functools.pipe. This way, a reusable pipe could be constructed flexibly. E.g.:
func = pipe(partial(add, 1), str, partial(add, '!'))
func(2) # "3!"
func(3) # "4!"
a..map..filter just doesn’t add anything. It is less flexible than filter(map(a)) and can not compose functions.