Why do we need both Base.view and Base.@view?

There seems to be a big overlap in functionality of Base.view and Base.@view :

a = collect(1:9)
b = view(a, 3:6)
c = @view a[3:6]
b[1] = 0
c[1] = 3
b .= 7
c .= 1
sum(b)
sum(c)

Why do we need both?

so that your code looks cleaner

1 Like

Cleaner with which option? Why?

exactly, it depends on what that line looks like.

Canonical example in Julia I think is () vs. |>, clearly |> is “not necessary”.

1 Like

I see notational usefulness of having both () and |>. It wasn’t clear to me that the same applies here. Looking at the documentation (https://docs.julialang.org/en/v1.6-dev/base/arrays/#Base.view) it is not obvious that these two are just syntactic sugar for the same concept. The descriptions are different. The examples are the same, which may be a clue of semantic equivalence, but it is not obvious.

If I am just doing something simple on the line, it is easy to use @view. But suppose it is a long complicated expression, with multiple different array slices and whatever else, and the thing I want to create a view of is nested within it… now I use view(thatthing,...).

2 Likes

We don’t. You could use view for everything. However, if you have code that already using normal indexing syntax, it’s much easier to put @view on it than to change the syntax entirely. Also, if the indexing uses end then it’s much simpler to use @view than try to write it with view. Example:

julia> a = rand(5, 5)
5×5 Matrix{Float64}:
 0.472674   0.983691   0.689607  0.920273   0.179599
 0.0837699  0.141811   0.184759  0.572521   0.387751
 0.184719   0.761248   0.581414  0.0863993  0.24077
 0.213946   0.0351995  0.295844  0.790204   0.514396
 0.334912   0.415638   0.688737  0.31583    0.180898

julia> @view a[2:end, 1:end-1]
4×4 view(::Matrix{Float64}, 2:5, 1:4) with eltype Float64:
 0.0837699  0.141811   0.184759  0.572521
 0.184719   0.761248   0.581414  0.0863993
 0.213946   0.0351995  0.295844  0.790204
 0.334912   0.415638   0.688737  0.31583

julia> view(a, 2:size(a, 1), 1:size(a, 2)-1)
4×4 view(::Matrix{Float64}, 2:5, 1:4) with eltype Float64:
 0.0837699  0.141811   0.184759  0.572521
 0.184719   0.761248   0.581414  0.0863993
 0.213946   0.0351995  0.295844  0.790204
 0.334912   0.415638   0.688737  0.31583
13 Likes

I appreciate more compact option with @view a[2:end, 1:end-1]. The gist of my post is to make it easier for newbies to distinguish between syntactic sugar and semantically different forms. As I wrote above, the documentation gives an impression that there may be some semantic differences. I suspect there may be more cases similar to Base.view. It would help to group them together under a single function description or make explicit in some other way that the semantics of two (or more) forms is equivalent.

1 Like

Agreed. Besides f(g(x)), g(x) |> f, x |> g |> f (for callable x), there is also ∘(f, g)(x) and (f ∘ g)(x). All of them are “unnecessary”, but I had to use all of them in a project to avoid cluttered syntax.

The same applies to view and @view, you use the one that suits your syntax better.

1 Like

I may be wrong, but most of the cases, if there is a macro and a function of same name inside the same module, then the macro is just syntactic sugar for that function.

In fact, all macros are always “just” syntactic sugar, they never do anything that could not be done without them, by the programmer manually writing the macro expansion by hand instead of using the macro to write boilerplate code for them.

3 Likes

In my short experience with Julia I’ve seen exceptions to this rule, e.g.:

julia> a = collect(1:3)
3-element Array{Int64,1}:
 1
 2
 3

julia> @show a
a = [1, 2, 3]
3-element Array{Int64,1}:
 1
 2
 3

julia> show(a)
[1, 2, 3]

Similar, but not quite the same.

I have thought about show and @show when I wrote my comment, and they exactly prove my case. @show is nothing but syntactic sugar for the common idiom that you could write by hand yourself:

julia> print("a = "); show(a); println(); a
a = [1, 2, 3]
3-element Array{Int64,1}:
 1
 2
 3

I am not saying that f and @f do the exact same thing, but that @f is just something that write some boilerplate code around a f call for you, that you could have written yourself but it is a bother, for the most common uses of f.

1 Like

Actually, @show is a counter-example to your point. I challenge you to write a function that can capture the name of a variable, rather than just the value of a variable. In other words, @show can do this:

julia> x = 1; @show x;
x = 1

julia> asdf = 2; @show asdf;
asdf = 2

But it’s not possible to write a function that can do this:

julia> x = 1; foo(x)
x = 1

julia> asdf = 2; foo(asdf)
asdf = 2

because you can’t write a foo that captures the name of the variable that you supply.

1 Like

I know this, and I was very careful when I worded my post. I never said a function can always do what a macro does, I said that a programmer can always do what a macro does.

In this case, the programmer would need to write the name of the variable inside a string, this is what I call boilerplate code, and that the macro does for the programmer. But it is never impossible for the programmer to do it by hand. Q. E. D.

2 Likes