Skip to content

Loading…

consider consistently using ! for mutating functions #907

Closed
StefanKarpinski opened this Issue · 20 comments

6 participants

@StefanKarpinski
The Julia Language member

E.g. rename push => push!, etc. Discussion here.

@diegozea
The Julia Language member

I think the same, actually... I was thinking in open an issue like this!
I'm extending the dequeue function for my types, and the actual definitions looks awkward...
I'm think that push, del and others must be to be push!, del!, etc.
And returning the modified input in a function that modified the input looks awkward (redundant) and is inefficient (without not reason).

I'm opened this topic on julia-dev:
[https://groups.google.com/forum/?hl=es&fromgroups=#!topic/julia-dev/wKIlMaSj0YE]

@JeffBezanson
The Julia Language member

"returning the modified input...is inefficient": Prove it.

@StefanKarpinski
The Julia Language member

I'm not sure where the idea that returning the array is inefficient comes from, but that's just not the case.

@diegozea
The Julia Language member

I do a very simple test for push against push! in the mail list [ previous link ] yesterday at 3 AM. But, yes... Excuse me... Looks to be my error [ it's lees than a 1% better, probably only measurement error ]. Here a test for:

function enqueue!{T}(a::Array{T,1}, item)
    if is(T,None)
        error("[] cannot grow. Instead, initialize the array with \"T[]\".")
    end
    item = convert(T, item)
    ccall(:jl_array_grow_beg, Void, (Any, Uint), a, 1)
    a[1] = item
    nothing
end

And the results are:

        BenchmarkCategory BenchmarkName Iterations Fun_wins_to_Fun2 Fun2_wins_to_Fun    Ties  TotalWall AverageWall    MaxWall MinWall             Timestamp            JuliaVersion    JuliaHash CodeHash      OS
[1,]     "compare_return"        "with"      50220          4.60024          4.78505 90.6147 0.00282693  5.62909e-8   9.799e-5     0.0 "2013-01-06 19:51:44" "0.0.0+106484954.r5387" "53872cd2c9"       NA "Linux"
[2,]     "compare_return"     "without"      49780          4.60024          4.78505 90.6147 0.00268102  5.38573e-8 5.38826e-5     0.0 "2013-01-06 19:51:44" "0.0.0+106484954.r5387" "53872cd2c9"       NA "Linux"

But, I still think that the names need to be ending in ! for indicate modification of input data.

@StefanKarpinski
The Julia Language member

I'm proposing the following renames:

push    => push!
pop     => pop!
grow    => grow!
enqueue => unshift!
unshift => unshift!
insert  => insert!
del     => delete!
del_all => deleteall! (or clear!?)

As a transitional step, the old versions will be kept around with warnings.

@diegozea
The Julia Language member

Good!! :+1:

I think deleteall! is better than clear! [ clear! gives the idea of deletion of data but maintain the structure (type and size) ]

@JeffBezanson
The Julia Language member

Here are my notes from our discussion about !, especially how it relates to linear algebra.

! convention:
after f!(A), A should equal B in B=f(A)

constructor convention:
Type(A) takes ownership of A and can mutate it.

linear algebra functions that might throw errors should be designed as follows:
a low-level function/ctor CholeskyDense(A) that takes A, mutates it, and
returns something that can tell you whether A was posdef. Does NOT throw.

a high-level function chol(A) and/or chol!(A) that throws an error if there
is a problem. it might have to do CholeskyDense(copy(A)) internally.

@StefanKarpinski
The Julia Language member

How can CholeskyDense(A) indicate whether it worked or not? It's supposed to return an object of type CholeskyDense so it seems like there would have to be a notion of a CholeskyDense object that somehow encodes the fact that A wasn't positive definite?

@JeffBezanson
The Julia Language member

I was thinking CholeskyDense could have a field that tells you, but the important thing is some low-level function that doesn't throw an error so you have the option of more control.

@andreasnoack
The Julia Language member

@JeffBezanson wrote:
! convention:
after f!(A), A should equal B in B=f(A)

This is not the case in lapack.jl and I think it could be too restrictive. The LAPACK functions return tuples typically with a matrix and error information and for example syevr! just destroys A and returns eigenvalues. Otherwise many of the LAPACK functions should copy their input and have ! removed.

@StefanKarpinski CholeskyDense gives and error if the decomposition fails but LUDense and CholeskyDensePivoted don't. They just save the information variable returned from LAPACK. For the former it is used in the det method and the latter has a rank method but fails if the factorisation is used for solving a system of equations.

@StefanKarpinski StefanKarpinski added a commit that referenced this issue
@StefanKarpinski StefanKarpinski bang: consistently use ! for mutating functions [#907].
This deprecates the non-! versions and prints a warning when they
are used. It doesn't quite close out #907 but comes close. There
are still a few lingering mutators like del_each that we may want
to get rid of or rename somehow.
d34d330
@JeffBezanson JeffBezanson added a commit that referenced this issue
@JeffBezanson JeffBezanson rename copy_to to copy!
see #907
280e49f
@JeffBezanson
The Julia Language member

All done now, except for assign! which I did on a branch since it is very disruptive.

@JeffBezanson
The Julia Language member

How are we feeling about assign!?

@johnmyleswhite
The Julia Language member

I'm for it.

@StefanKarpinski
The Julia Language member

I think we should consider both ref and assign and make them match in some sense. Something like getindex and setindex!. Or maybe atindex and atindex!. Or perhaps we should go Pythonic on this with __index__ and __index__!. Generally, I'd say ick, but this is one of those cases where we really want to avoid collisions in a global sense.

@JeffBezanson
The Julia Language member

I like getindex and setindex! best so far. Remember we want to leave the door open for a non-mutating setindex; there are nifty implementations of vectors that allow that.

@StefanKarpinski
The Julia Language member

Oh, fair enough. Yeah, let's go with getindex and setindex! then, leaving setindex as the non-mutating variant on setindex!.

@JeffBezanson
The Julia Language member

This is going to be the most vicious renaming ever.

@kmsquire
The Julia Language member

Is there a way to do this so all external uses of ref and assign don't break immediately?

@JeffBezanson
The Julia Language member

We could use const ref = getindex. That is frictionless but won't give a warning.

@kmsquire
The Julia Language member

How about a special deprecate macro for ref and assign, and broadcasting really loudly (ALL CAPS) on the mailing list? ;-)

I think it's easy enough to find all uses of ref and assign in base and packages is pretty doable. It's user's private code that I'm concerned about. But I definitely support the chance, and if it's going to happen, the earlier, the better.

@JeffBezanson JeffBezanson added a commit that closed this issue
@JeffBezanson JeffBezanson rename ref to getindex, assign to setindex!
closes #1484, closes #907
there are aliases in deprecated.jl to ease the transition
f0e6204
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.