I think the primary argument against inarg is that it is incredibly weak. It is essentially only a self-imposed compile-time warning.
I'm not opposed to being able to prevent modification of an object, but I think that is more useful as a requirement imposed by the caller, rather than self imposed. I thought some about adding a lock on array modification via the high bit of the array size. This would make the additional check very low cost. A module-level flag that forces all locals to be const by default (ala Swift) sounds cool to me. As a general use language that is not primarily targeted towards computer scientists, I expect that making the const option a default would be a bit harder to explain to non-CS users. I'm working on adding a Ref-type implementation to my jn/ccall3 pull request (based on an implementation in use in Gtk.jl), because for C-interop you often do need such a thing. Being able to add const annotations to type fields would be beneficial, especially when a constant-propagation pass is added to the function inference code. I imagine that a pull-request to add such a feature would be accepted. On Wednesday, August 20, 2014, <[email protected]> wrote: > Adam, > > I was the originator of this thread, and in the end I conceded that the > current way that Julia handles immutable makes sense. Indeed, I authored a > paragraph about the definition of immutable composite types for the 0.4.0 > version of the Julia manual in which I put Stefan's explanation dated Aug 5 > into my own words. > > I think that adding ref arg and value arg types to Julia would make the > type system too confusing. For example, what if you pass an Array of > Arrays as a value arg? (Are the inner arrays also copied over?) Can you > designate the elements of an Array to be ref/value? And what about > multiple dispatch in the case that two methods have similar signatures > except for a difference in ref and value args? > > On a related note, I have proposed that Julia should have a way to > designate that a mutable arg to function is an inarg (i.e., not > modifiable). My proposal is in a different thread in this newsgroup. The > consensus seems to be that the proposal works fine with existing Julia, but > some respondents questioned whether the proposal would get buy-in. > > -- Steve Vavasis > > > > On Tuesday, August 5, 2014 5:38:17 PM UTC-4, [email protected] wrote: >> >> Dear Julia users, >> >> It seems to me that Julia's distinction between a 'type' and an >> 'immutable' conflates two independent properties; the consequence of this >> conflation is a needless loss of performance. In more detail, the >> differences between a 'type' struct and 'immutable' struct in Julia are: >> >> 1. Assignment of 'type' struct copies a pointer; assignment of an >> 'immutable' struct copies the data. >> >> 2. An array of type structs is an array of pointers, while an array of >> immutables is an array of data. >> >> 3. Type structs are refcounted, whereas immutables are not. (This is not >> documented; it is my conjecture.) >> >> 4. Fields in type structs can be modified, but fields in immutables >> cannot. >> >> Clearly #1-#3 are related concepts. As far as I can see, #4 is >> completely independent from #1-#3, and there is no obvious reason why it is >> forbidden to modify fields in immutables. There is no analogous >> restriction in C/C++. >> >> This conflation causes a performance hit. Consider: >> >> type floatbool >> a::Float64 >> b:Bool >> end >> >> If t is of type Array{floatbool,1} and I want to update the flag b in >> t[10] to 'true', I say 't[10].b=true' (call this 'fast'update). But if >> instead of 'type floatbool' I had said 'immutable floatbool', then to set >> flag b in t[10] I need the more complex code t[10] = >> floatbool(t[10].a,true) (call this 'slow' update). >> >> To document the performance hit, I wrote five functions below. The first >> three use 'type' and either no update, fast update, or slow update; the >> last two use 'immutable' and either no update or slow update. You can see >> a HUGE hit on performance between slow and fast update for `type'; for >> immutable there would presumably also be a difference, although apparently >> smaller. (Obviously, I can't test fast update for immutable; this is the >> point of my message!) >> >> So why does Julia impose this apparently needless restriction on >> immutable? >> >> -- Steve Vavasis >> >> >> julia> @time testimmut.type_upd_none() >> @time testimmut.type_upd_none() >> elapsed time: 0.141462422 seconds (48445152 bytes allocated) >> >> julia> @time testimmut.type_upd_fast() >> @time testimmut.type_upd_fast() >> elapsed time: 0.618769232 seconds (48247072 bytes allocated) >> >> julia> @time testimmut.type_upd_slow() >> @time testimmut.type_upd_slow() >> elapsed time: 4.511306586 seconds (4048268640 bytes allocated) >> >> julia> @time testimmut.immut_upd_none() >> @time testimmut.immut_upd_none() >> elapsed time: 0.04480173 seconds (32229468 bytes allocated) >> >> julia> @time testimmut.immut_upd_slow() >> @time testimmut.immut_upd_slow() >> elapsed time: 0.351634871 seconds (32000096 bytes allocated) >> >> module testimmut >> >> type xytype >> x::Int >> y::Float64 >> z::Float64 >> summed::Bool >> end >> >> immutable xyimmut >> x::Int >> y::Float64 >> z::Float64 >> summed::Bool >> end >> >> myfun(x) = x * (x + 1) * (x + 2) >> >> function type_upd_none() >> n = 1000000 >> a = Array(xytype, n) >> for i = 1 : n >> a[i] = xytype(div(i,2), 0.0, 0.0, false) >> end >> numtri = 100 >> for tri = 1 : numtri >> sum = 0 >> for i = 1 : n >> @inbounds x = a[i].x >> sum += myfun(x) >> end >> end >> end >> >> >> function type_upd_fast() >> n = 1000000 >> a = Array(xytype, n) >> for i = 1 : n >> a[i] = xytype(div(i,2), 0.0, 0.0, false) >> end >> numtri = 100 >> for tri = 1 : numtri >> sum = 0 >> for i = 1 : n >> @inbounds x = a[i].x >> sum += myfun(x) >> @inbounds a[i].summed = true >> end >> end >> end >> >> function type_upd_slow() >> n = 1000000 >> a = Array(xytype, n) >> for i = 1 : n >> a[i] = xytype(div(i,2), 0.0, 0.0, false) >> end >> numtri = 100 >> for tri = 1 : numtri >> sum = 0 >> for i = 1 : n >> @inbounds x = a[i].x >> sum += myfun(x) >> @inbounds a[i] = xytype(a[i].x, a[i].y, a[i].z, true) >> end >> end >> end >> >> >> function immut_upd_none() >> n = 1000000 >> a = Array(xyimmut, n) >> for i = 1 : n >> a[i] = xyimmut(div(i,2), 0.0, 0.0, false) >> end >> numtri = 100 >> for tri = 1 : numtri >> sum = 0 >> for i = 1 : n >> @inbounds x = a[i].x >> sum += myfun(x) >> end >> end >> end >> >> function immut_upd_slow() >> n = 1000000 >> a = Array(xyimmut, n) >> for i = 1 : n >> a[i] = xyimmut(div(i,2), 0.0, 0.0, false) >> end >> numtri = 100 >> for tri = 1 : numtri >> sum = 0 >> for i = 1 : n >> @inbounds x = a[i].x >> sum += myfun(x) >> @inbounds a[i] = xyimmut(a[i].x, a[i].y, a[i].z, true) >> end >> end >> end >> >> end >> >> >> >> >
