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

Syntax design bug: [a:b] #8950

Closed
xianrenb opened this issue Nov 9, 2014 · 23 comments
Closed

Syntax design bug: [a:b] #8950

xianrenb opened this issue Nov 9, 2014 · 23 comments

Comments

@xianrenb
Copy link

xianrenb commented Nov 9, 2014

Currently "[1:5]" represents "[1, 2, 3, 4, 5]".
But ranges are not the same as arrays!
"[1:5]" should not equal "[1, 2, 3, 4, 5]"!

I believe we need to be able to create array of ranges in a concise way. For example, "[1:5, 1:3]" should be an array of two ranges: "1 to 5" and "1 to 3".
Range(s) between "[" and "]" should still be range(s).

We could convert range to array: "convert(Array, 1:5)" would give "[1, 2, 3, 4, 5]".
It is clear that it is not a must-do for "[1:5]" representing "[1, 2, 3, 4, 5]".
So, why not consider "a..b" ?

Related issue: #8949

@StefanKarpinski
Copy link
Member

Please stop opening duplicate issues.

@xianrenb
Copy link
Author

xianrenb commented Nov 9, 2014

I believe this is a new bug report.
#3737 is about concatenation of arrays "[a, b]".
This issue is to report that the design for current syntax of "[a:b]" is wrong!
"[a:b]" should be an array with one element, and that element is a range from a to b.

Yes, they are closely related, but not really duplicate.

@johnmyleswhite
Copy link
Member

This is definitely not a bug report. It's a complaint about existing syntax that is completely addressed by other issues.

@xianrenb
Copy link
Author

xianrenb commented Nov 9, 2014

This is a design bug.
Currently, one could write "convert(Array, a:b)" instead of the wrong "[a:b]", but one could not use "[a:b]" to represent an array of single range element.

@StefanKarpinski
Copy link
Member

It is quite clear from existing issues that everyone here is well aware of the issues surrounding current concatenation syntax and that the consensus is that it needs to be improved but that consensus hasn't yet been reached on the best way to fix it. It would be appropriate to make a comment on the most relevant existing issue rather than opening multiple new issues just to voice your specific suggestion. We've heard you: you don't like the fact that [1:5] makes an array of five integers instead of a array of a single range. You think that a..b is a good syntax. Great. You've been heard. However, we already have other tentative plans for the a..b syntax, so I suspect that won't happen. The fact that you don't like the way [1:5] currently behaves does not make it "wrong". Asserting that it is "wrong" just makes you come across as obnoxious and overbearing. Perhaps this is a non-native language issue. Either way, please tone it down and stop opening new issues when a comment on an existing one will do.

@xianrenb
Copy link
Author

xianrenb commented Nov 9, 2014

Sorry for my language.

My view is that if one puts some things between "[" and "]", those things would be elements of an array.
"[1:5]" is clearly not consistent to the above.

I opened this issue, because it is very likely that the solutions for other related issues would not change what "[a:b]" means as it is assumed that "[a:b]" is not a bug.
In other words, this is a different issue.

@timholy
Copy link
Member

timholy commented Nov 9, 2014

You can use UnitRange[a:b] to make a vector of Ranges.

I think the disconnect here is that you're not realizing that the current behavior is because of concatenation. For example,

julia> [1:3, 5:7]
6-element Array{Int64,1}:
 1
 2
 3
 5
 6
 7

concatenates the AbstractVector (really UnitRange{Int}s) into a single output. Since Julia functions should be type-stable, the return type cannot depend upon how many AbstractVectors you are concatenating. Consequently, [1:3] has to return a Vector{Int}.

So @StefanKarpinski is entirely correct.

@xianrenb
Copy link
Author

I believe most of us don't realize the problem:

julia> r = 1:5
1:5

julia> [1:5]
5-element Array{Int32,1}:
1
2
3
4
5

julia> [r]
5-element Array{Int32,1}:
1
2
3
4
5

timholy just assumed that the type of material between "[" and "]" must be known.
One would expect putting anything between "[" and "]" would give a "simple version" array of something. But the above just showed that this is not the case.
If someone is writing a program manipulating arrays without knowing the elements' type, he may have to carefully consider whether the elements are in fact ranges for every line of code.

@xianrenb
Copy link
Author

julia> sort([1:2, 2:3])
4-element Array{Int32,1}:
1
2
2
3

julia> r1 = 1:2
1:2

julia> r2 = 2:3
2:3

julia> sort([r1, r2])
4-element Array{Int32,1}:
1
2
2
3

One would expect the outputs are arrays with two elements.

@StefanKarpinski
Copy link
Member

Yes, this is a completely known behavior. I don't like it either.

@ntessore
Copy link

Like others have already said, you can get what you want by telling Julia to create an array of two ranges, instead of concatenating them:

julia> import Base.isless

julia> isless(a::UnitRange{Int64}, b::UnitRange{Int64}) = a.start < b.start
isless (generic function with 29 methods)

julia> sort(UnitRange[1:2, 2:3])
2-element Array{UnitRange{T<:Real},1}:
 1:2
 2:3

julia> r1 = 1:2
1:2

julia> r2 = 2:3
2:3

julia> sort(UnitRange[r1, r2])
2-element Array{UnitRange{T<:Real},1}:
 1:2
 2:3

It's not a "syntax bug", at the moment the expressions you use just don't mean what you want them to mean.

@timholy
Copy link
Member

timholy commented Nov 10, 2014

Right, @xianrenb, there's nothing you're pointing out that hasn't been known for ages. It's just that no one has fixed it yet. Please, by all means, be the one to do so.

@xianrenb
Copy link
Author

julia> function push_stack(A::Array, item::Any)
return [A, item]
end
push_stack (generic function with 1 method)

julia> function query_stack(A::Array)
return A[end]
end
query_stack (generic function with 1 method)

julia> a = []
0-element Array{None,1}

julia> a = push_stack(a, 3)
1-element Array{Int32,1}:
3

julia> a = push_stack(a, 4)
2-element Array{Int32,1}:
3
4

julia> query_stack(a)
4

julia> a = []
0-element Array{None,1}

julia> a = push_stack(a, 1:2)
2-element Array{Int32,1}:
1
2

julia> a = push_stack(a, 2:3)
4-element Array{Int32,1}:
1
2
2
3

julia> query_stack(a)
3

So I push a range but get an integer?

@timholy
Copy link
Member

timholy commented Nov 10, 2014

julia> a = UnitRange{Int}[]
0-element Array{UnitRange{Int64},1}

julia> push!(a, 1:2)
1-element Array{UnitRange{Int64},1}:
 1:2

julia> push!(a, 2:3)
2-element Array{UnitRange{Int64},1}:
 1:2
 2:3

julia> a[end]
2:3

@xianrenb
Copy link
Author

What I want to show is that, with current design/implementation, programmers have to think carefully how to manipulate arrays without knowing the elements' type, or if the type is Any.
And, programmers have to add additional code for handling ranges for their own programs.
In my example, the related code may be a single line, but in other programmers' code, there may be several thousand occurrences.

@timholy
Copy link
Member

timholy commented Nov 10, 2014

Please fix it.

@xianrenb
Copy link
Author

julia> function init_with_single_item(item::Any)
return [item]
end
init_with_single_item (generic function with 1 method)

julia> size(init_with_single_item(1), 1)
1

julia> size(init_with_single_item(2:3), 1)
2

@nolta
Copy link
Member

nolta commented Nov 10, 2014

@timholy There already is a PR to fix this: #8599

@lindahua
Copy link
Contributor

@xianrenb Admittedly, the current semantics, namely expanding arrays and performing concatenation within square brackets, is not ideal and sometimes causes confusion.

However, it seems that it hasn't caused big problems in practice. I haven't seen lots of functions that have to introduce additional codes to deal with this. If your purpose is to write a function that can wrap an input item into an vector, no matter whether it is a number, a vector, or a range, you can already accomplish your goal with the status quo:

# The function `singleton` wraps an item x to a vector containing exactly a single element `x`
singleton(x) = typeof(x)[x]

julia> singleton(1)
1-element Array{Int64,1}:
 1

julia> singleton(2:3)
1-element Array{UnitRange{Int64},1}:
 2:3

You can see that I don't have to introduce any branch statements to deal with the case where x is a range.

@xianrenb
Copy link
Author

What about:
function init_with_single_item(item::Any)
doing_something_with(item)
return [item]
end

singleton() or any other Julia function may not provide what a programmer wants.
What could a programmer do if doing_something_with() is written by others?
The only way to make it work correctly is to inspect code defining doing_something_with() and modify it if it does not handle ranges correctly.

I believe changing line 497~521 of julia/range.jl is a good start, but this may change many things.

Julia developers are creating a new computer language, and one of the targets should be easy to use.

@JeffBezanson
Copy link
Member

You don't need to keep repeating your point. In fact all of these things
have been discussed already in the issues others have linked to. All of
this is perfectly obvious. Furthermore, we agree and I hope this change
makes it into 0.4.

@johnmyleswhite
Copy link
Member

Perhaps this thread should be locked? It's clear that we're all in agreement.

@timholy
Copy link
Member

timholy commented Nov 10, 2014

Thanks, @nolta. Haven't had time to follow everything that's been happening.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants