This will also let us make the memory layout for composites and arrays of composites the same as C structs and arrays of structs.
> This will also let us make the memory layout for composites and
> arrays of composites the same as C structs and arrays of
> structs.
>
> This *the* prime motivation: compact storage of arrays of composite
> values. This is really huge for big data stuff and something that
> no languages but C/C++ manage. You
That would be very nice indeed. It makes array operations a lot more
useful to be able to have efficient arrays of composites.
> As a side-effect, it may be really good for concurrency, but
> honestly that's actually not our prime motivation (unlike Clojure
> where that seems to be the main driver for wanting all data
> structures to be immutable).
In Clojure the main motivation is indeed concurrency, but immutable
data structures also make for more robust code in non-parallel
applications once they reach a certain size. I wonder if anyone has
ever done statistics on the causes of hard-to-find bugs. In my
personal experience, whenever it took me more than a day to track down
a bug, the cause was almost always some piece of data that was
modified in an unexpected way.
Konrad.
> As a side-effect, it may be really good for concurrency, but> honestly that's actually not our prime motivation (unlike ClojureIn Clojure the main motivation is indeed concurrency, but immutable
> where that seems to be the main driver for wanting all data
> structures to be immutable).
data structures also make for more robust code in non-parallel
applications once they reach a certain size. I wonder if anyone has
ever done statistics on the causes of hard-to-find bugs. In my
personal experience, whenever it took me more than a day to track down
a bug, the cause was almost always some piece of data that was
modified in an unexpected way.
Mostly for my own curiosity, could you outline the technicalities ofhow the compact arrays of immutable types would work?
Let's call a type "closed" if it is not abstract and all its fields
(if any) are declared to be of a closed type. Then it is known exactly
how large the whole data structure is, and like C structs, an array of
a closed type can be efficiently packed inline in memory. As someone
who doesn't know the inner workings of Julia, I would have thought
this would be the way to implement tight packing. As a user, I would
be very happy with such a feature - whenever I have a lot of data to
store, I would declare all the fields to be of a non-abstract type,
and get the same performance as with C structs.
julia> v = [1//2][1//2]julia> q = v[1]1//2julia> v[1].den = 33
julia> v[1//3]julia> q1//3
julia> v = [1//2][1//2]julia> q = v[1]1//2julia> v[1] = 1//3[1//3] # see issue #544julia> v[1//3]julia> q1//2
> Yes, this is very true. That's part of why strings are immutable in
> Julia. Also, there's an important psychological distinction between
> things we think of as mutable versus immutable: values that are
> *defined* by their values should be immutable, whereas values that
> *contain* other values but have an independent identity can be
> mutable.
Right. Nevertheless, immutable container types are of interest as
well, implemented by what the functional programming community calls
"persisten data structures": there are no operations to change those
data structures, but there are operations that produce modified
versions that share most of the memory storage with the original
version, which continues to exist.
The reason for having immutable containers is again robustness, in
particular, but not exclusively, with concurrency. I find myself
agreeing more and more with Rich Hickey, the inventor of Clojure, who
claims that "immutability is the right default". We do need mutable
containers, and in particular mutable arrays, for doing many things
efficiently, but it's usually best to start with immutable containers
and switch to mutable when required for efficiency.
> about the actual memory buffer that represents a string. In a
> higher level language, it's really, really surprising when a string
> changes value because you passed it to some other function. Some
> of the nastiest bugs I've ever had to find were due to this.
Me too!
Konrad.
> Building a persistent collections library in Julia is certainly a
> cool project.
Once there are immutable composites, I don't see why this couldn't be
done.
> The basic built-in arrays have to be mutable, however.
Indeed. No one has yet come up with a good immutable substitute for arrays.
Konrad.
Congratulations for this new programming language. It's very clear and easy to learn. I like it!
But... be careful with a purist functional (or object oriented or...) approach like:
- Immutable data is the right thing or you are wrong.
- Magic abstractions (you loss the idea about what you code really does)
- Only arrays, vectors or matrix need to be immutable.
- Functions are good, objects are bad.
- Think as a language designer, not as a language user.
An example, I'm working since some time with data analysis and my code is full of mutable composites like this:
template<typename ValueType>
class variance {
public:
typedef ValueType value_type;
variance()
: _mean(0), _var(0) {}
variance(const variance& v)
: _mean(v._mean), _var(v._var), _count(v._count) {}
void operator()(value_type x) {
_count();
value_type delta = x-_mean;
_mean += delta/_count;
value_type delta2 = delta*(x-_mean);
_var += (delta2-_var)/_count;
}
operator value_type() const { return _var; }
value_type sum() const { return _mean*_count; }
value_type mean() const { return _mean; }
std::size_t count() const { return _count; }
private:
value_type _mean, _var;
counter _count;
};
The are hundreds of function objects (accumulators), composed in other composites and in a long chain that compiler joins in a big expression to calculate each sample of the data in one loop (for each thread). So yes, I'm coding in a functional approach. But, however there are many (light) objects and 90% have a mutable state. But this is not all the answer because 90% of the time parts of the program access those composites as immutable values. The member function operator() modify its values but all the rest can not. All references are marked as const, for example:
const variance& operator[](size_type i) const { // const functon retreive a const variance from an array
return _items[i]; // array is mutable for not const member function of its owner
}
It's not a good idea to destroy and construct each piece of all the superstructure in each sample iteration. And you need the state because you want to know the full snapshot incrementally. There aren't immutable composites but there are one modifier function and many accesses that trait data as immutable.
C++ is impure, magnet of the details, neither functional nor object oriented and with a limited type abstraction... but maybe for those reasons I've found that is the best to get my work done and... very fast! Be careful with fall in love with a particular paradigm or programming hype.
"There are more things in earth and heaven, Horatio, that are dreamt of in your philosophy."
Regards,
Guillermo Ruiz Troyano.