Re: [rust-dev] std::num::pow() is inadequate / language concepts

2014-07-25 Thread Gregor Cramer
> I disagree with that: " I never accept compromises, this is
> the way how to develop magnificient software"
> Because it's not. Unless you use magnificient only in academic context.

? I'm not doing academic things.

> It's not so much about wether or not overloading could be used in rust
> without causing really painful issues. The question is if overlaoding
> fits into the language's design principles.

I agree, if overloading does not fit at all, then it should not be done.

> Overloading is not necessary. It's just one of many ways that lead to Rome.

Yes, many ways are leading to Rome. One of the ways is easy to go,
and is a joy. Another way is tedious or cumbersome.

> A language is about more than just "what you consider beautiful,
> etc."... It's about wether it allows agile, fast paced development
> across diverse teams and average programmers can produce code anyone
> else can read without getting eye cancer.

Fast-paced development without generic programming? And overloading
is supporting generic programming.

> > I cannot see that overloading is horrible or complicated. It's another
> 
> No, but it might be unnecessary.

Possibly I'm wrong that overloading is neccessary in Rust, that's why
I'm talking, I'm not a master in Rust programming. But fact is: as I
stumbled over std::num::pow() I could see problems if not
having overloading. And I repeat: std::num::pow() is only an
example for a general problem.___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] std::num::pow() is inadequate / language concepts

2014-07-25 Thread Gregor Cramer
> And of course it's possible to change something to a trait after the
> fact without breaking API compatibility.

How you are doing this? I'm in fact a newbie in Rust, and it's interesting
that this can be done. std::num::pow() is a good example, I think.
Suppose I already have a program which is using std::num::pow() with a
self defined integer type. Now you are changing std::num::pow(), moving
the functionality into a trait. And my program will still compile and work
as before?___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] std::num::pow() is inadequate / language concepts

2014-07-25 Thread Gregor Cramer
> > I gave up at all. (I'm doing software design and implementation since
> > more than 30 years, and I never accept compromises, this is the way
> > how to develop magnificient software).
> 
> Hum, I would almost strongly disagree  I would even go as far as
> saying that you won't develop any kind ...

How can you disagree about what I'm doing?

> Well, I guess you did nothing but C++ in the last 30 years then?
> Because I can't recall many languages that would allow this sort of
> thing. How would C# and Java's Math::Pow() would work out in this
> case? How would it work out in C? How would it work out in Python,
> JavaScript, etc... the list is ... quite long.

I don't care about the capabilities of other languages, I don't use a
language if it is not appropriate.

> The question is always about compromise. Shall rust include a language
> feature to make some things easier for the sake of introducing tons of
> problems as well?

No. Everyone is talking about tons of problems, but which ones?
The most problematic language, with tons of problems, is C++.
But even in C++ not overloading is the problem - and I have about
20 years experience with C++ - it is for example, to name just one,
the implicit casting, because this makes overloading a bit problematic.

> Java is about the least expressive language we have at the time
> (appears a bit like the greatest common denominator of all imperative
> languages) and I would say only few people are out there who would say
> that you can't do proper software design with it.

This depends on how your are doing software design. Impossible
for me to use Java.

> It might not be a
> concise and pleasing as "GOOD C++ design is", but then again "GOOD C++
> design" is very hard to archieve and thus begs the questions if it is
> even worth it to make a language that complicated so that magnificient
> (academic) design is possible at the cost of making the average
> (industrial) design horrible.

I cannot see that overloading is horrible or complicated. It's another
point that C++ is horrible and complicated. We have 2014, as I started
with C++ it was the superior language, but software design has evolved,
nowadays object oriented design is obscure, and that's in fact
my own experience. But C++ already supported one ingenious feature:
generic programming (but very low level).

> > pub fn pow>(mut base: T, mut exp: uint) -> T;
> 
> I agree this definition appears to be very strange to me. In more than
> one way. First it implies that the existing implementation works by
> somehow multiplying types with the expontential trick " a * a = b, b *
> b = c, c * c = a^6" etc...
> This is an unacceptable restriction for me, as this kind of evaluation
> might not be the best in many cases and we are talking about a
> standard library function after all. It should always allow the BEST
> implementation, not just some implementation.
>
> Here we clearly need a better concept. And this concept needs to be
> designed & defined. And you could start by doing this, instead of just
> giving up ;).

This means that I have to design at a lower level, before I start to implement
the big number library. Probably I'll try it, I don't know yet. I don't know
yet whether I will really use Rust. (BTW: "I gave up at all" does not mean
forever, please be aware that I'm not a native English speaker.)
In fact I'm looking for an alternative to C++, and Rust is still the most
promising one, but Rust is not yet elaborated (I know that Rust is still
pre-alpha).

> And overlaoding is not a great concept in general, IMO.
> What Rust could do is copy template specialization. So that I can say:
>
> pub fn pow>(mut base: T, mut exp: uint) -> T; //
> uses the exponential trick
>
> pub fn pow(mut base: i64, mut exp: uint) -> i64; // uses some
> cool processor features if available
>
> pub fn pow(mut base: &BigInt, mut exp: uint) -> BigInt; //
> uses some mighty algorithm that is not naive 

Yes, that would possibly be one solution for overloading, Unfortunately
the problem with the signature remains. It's absolutely clear for me
that an overloading feature should not cause problems, this means
that a design is required which suits perfectly with the principle design
of Rust.___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] std::num::pow() is inadequate / language concepts

2014-07-25 Thread Gregor Cramer
> Did I miss a point in this thread where using a typeclass/trait to
> implement exponentiation was dismissed?
>
> This function could be changed to:
>
>   fn pow(base: T, exp: uint) -> T { base.pow(exp) }
>
> trait HasPow {
>   fn pow(self: Self, exp: uint) -> Self
> }
>
> Or, just use HasPow in your code.

Yes, you missed a point, I've already pointed out in my initial mail that
moving pow() into a trait (that's what your code is finally doing) is solving
this special problem, but it is not solving a general problem with (other)
functions. A new module may cause that an older function (which you
cannot overload) is inadequate. This makes software instable. In the past
(with some older programming languages) you did not have solutions for
this, but Rust is 2014, programming and compiler techniques have evolved.___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] std::num::pow() is inadequate / language concepts

2014-07-25 Thread Gregor Cramer
Hi Marijn,

> Firstly, blanket statements like "This makes generic programming
> impossible" and "it does not allow proper software design" are
> unneccesary hyperbole, and do not help the discussion in any way.

You're not right, my statement wasn't blanket, it was my result
after I tried to overwork the big integer library, and I have mentioned this:
I gave up at all. (I'm doing software design and implementation since
more than 30 years, and I never accept compromises, this is the way
how to develop magnificient software).

> Traits provide a more well-defined, easier to reason about alternative
> to overloading. They do require the author of an algorithm to decide
> ahead of time whether this algorithm needs to be specializeable, which
> I guess C++-style overloading does not.

Yes, the traits are great, I'm impressed, as I said before, and in fact Rust
is really great, despite a few facts, otherwise I wouldn't subscribe to
this mailing list. And my goal is to be constructive, don't worry if I'm
a bit euphoric, such things happens. Nethertheless, it gave up to overwork
the big integer libary because I cannot specialize std::num::pow(). There is
no way to proceed with a proper design.

> Whether that is a good or a
> bad thing is debatable, but it is not true that Rust lacks a feature
> for specialization.

There is a lack in the current language concept, std::num::pow()
is inadequate due to this language concept, and std::num::pow() is
only one example for this fact.

I will repeat the problem with signatures. Currently pow() is declared
as following:

pub fn pow>(mut base: T, mut exp: uint) -> T;

That't 100% ok. The user will call this function in this way:

pow(a) // a is i32

Perfect. Now I need a specialized function for BigInt:

[#overload]
pub fn pow(base: &BigInt, mut exp: uint) -> T;

There's a problem (beside the missing overloading feature): the
specialized version requires a reference. Same problem if I'm
calling this function:

pow(&a) // a is BigInt

The user has to know how to call a function, depending on the type.
But a proper function specialization would be:

[#overload]
pub fn pow(base: BigInt, mut exp: uint) -> T;

And so the function call is as expected, like with other numeric types:

pow(a) // a is BigInt

But there is now a problem in this function definition, BigInt is given as
a copy, and this is a software design issue (superfluous memory allocation).
And this currently happens if the user is calling std::num::pow() with a
numeric type like BigInt (apart from other performance penalties in pow()).

That's what I've mentioned that the compiler should decide whether an
argument is given by reference or by value. In this way the latter approach
works. And in the case that a function willl modify an argument (in-out
value), for example:

fn mul_vec(acc : &mut [BigDigit], base: &mut [BigDigit], mut exp:uint)

the call of this function would be:

mul_vec(&a, &b, exp)

This concept will not change, because here it has to be clear that an argument
will be changed (furthermore the compiler should give a warning if a function
is not changing a mutable argument). I think that this approach is even
superior to the 'const' concept of C++, and it fit's with the great overall
concept of Rust (especially with the owner/borrower concept).

I try to show the problems if function specialization (overloading) is not
supported. A stable software design is problematic. Adding a new module,
which will use existing function declarations, is impossible in some cases.
Currently I cannot implement a specialized version of pow() for BigInt, adding
a new function for a different numeric type is only a hack, and moving this
function into a trait is not solving the general problem, because pow() is
only one example. (Beside: it's not my decision to move pow() into a trait.)

Cheers,
Gregor

___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] std::num::pow() is inadequate / language concepts

2014-07-25 Thread Gregor Cramer
Hi Patrick,

> If the signature is wrong and we mistakenly freeze it, we can just introduce
> a new function with a different name.

But this is a severe design issue, to introduce new function names. This makes
generic programming impossible. Now the user has to distinguish between
the types, but this is the task of the compiler.

> Overloading only helps some simple cases, and adds more complexity than it's
> worth (IMO).

Overloading is the only way to specialize functions, and this is the only way
to allow generic programming. Without specializing we are back to the bad
old days, where the user has to call the appropriate function for a specific
object, but in a modern programming language the compiler is doing these
things.

> The problem with C++ isn't that it doesn't have enough features. Rust is
> deliberately omitting some features from C++ that don't pull their weight.
> Overloading is one of them.

I think that some weights are unavoidable. And I cannot see serious drawbacks
with function overloading, but I see serious drawbacks without:

As I saw Rust the first time, I was impressed, and I decided to overwork the
big integer module (I've already written a big integer library in C), because
the current impementation is much too slow, it suffers from:

1. too many memory allocations
2. some algorithms are a bit naive.

And at first I tried to specialize std::num::pow(), but I gave up immediately,
because I cannot specialize. And without specializing this function I cannot
realize a proper implementation and design, and I'm never doing half-baken
things. So I gave up at all.

The current design in Rust does not allow:

1. Generic programming, in current design of Rust the user has to know,
which function to call for a specific object, and has to use switch (or match)
statements to call it (and if he forget the right functions and uses
std::num::pow(), his program will suffer). This is a programming style 30 years
ago, as I started to write programs.

2. Uniform function signatures, currently the user has to decide about using a
reference or not, but the compiler should decide. If the compiler is deciding,
whether an argument is given by value or by reference, then the problem with
the signature will vanish. And the compiler is better to decide than the user.
One more advantage: the user must not know whether to use a reference
or not when calling a function/method. One exception: a mutable argument, in
this case a reference will be used explicitely by the user, when specifiying
the signature, and when calling the function.

One more drawbacks without overloading: The user defines two  print methods:

pub fn print(line : string) -> bool;
pub fn print(line : string, max_line_length : uint) -> bool;

Not possible, he has to use different names. An alternative definition would be:

pub fn print(line : string) -> bool;
pub fn print_with_maxlen(line : string, len : uint) -> bool;

30 years ago this was the normal way, but nowadays, it's a No-Go.

The current status of Rust is: it does not allow proper software design. And
that's bad, because a successor for C++ is needed. Of course, a successor
of C++ does not mean: a better C++. It means, a completely new language
conecept, like Rust. And it does not mean: avoid the good things of C++,
like specialization of functions.

Cheers,
Gregor___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


[rust-dev] std::num::pow() is inadequate / language concepts

2014-07-24 Thread Gregor Cramer
Hello Rust folk!

I am new to Rust, and I have doubts concerning current language concepts.

One example: in module ::std::num function pow() is defined:

pub fn pow>(mut base: T, mut exp: uint) -> T {
if exp == 1 { base }
else {
let mut acc = one::();
while exp > 0 {
if (exp & 1) == 1 {
acc = acc * base;
}
base = base * base;
exp = exp >> 1;
}
acc
}
}

In general this implementation is ok, but not really usable with BigInt. Of
course, the call ':.std::num::pow(a, 1000)', 'a' is a BigInt, works. But this
implementation is not adequate for big integers. Firstly, too many memory
allocations during the computation (a specialized version can avoid these
memory allocations), secondly, for big integers a specialized function for
squaring (base * base) has to be used, because squaring can be done quite
more efficient than multiplication (with big integers). So this function is much
too slow and has to be overloaded, but:

1. Overloading is not supported (even the archaic C++ is providing this).

2. The footprint 'base: T' is not 100% suitable, for big integers the function
definition
 fn pow(base: &BigInt, mut exp: uint) -> BigInt
would be more appropriate, because the argument 'base' needs not to be
modified (or reassigned), and a call by reference (avoiding a superfluous
memory allocation) is more efficient in this case.

Of cource, a specialized version of pow() could be implemented in trait
BigInt, but this is only a workaround. And if a user only knows
::std::num::pow(), he will use an inappropriate implementation without
being aware of this.

Probably in this case it might be a solution  to move pow() into a trait, but
I'm speaking about a general problem. Rust 1.0 will be released, and someone
is developing a new module for version 1.1. But some of the functions in 1.0
are inadequate for the new module, how to solve this without changing the API
in 1.1? I think that function overloading may  help in some cases, but the
problem with inappropriate footprints remains. In my opinion this
thing with the footprints (reference or not if the real type is unknown -
that's why the concept with 'const' in C++ exists) is a conceptual design
issue, but probably I do not yet fully understand Rust.

BTW: the functions next_power_of_two(), and checked_next_power_of_two()
are only defined for primitives (trait Primitive), but should also be 
applicable for big integers, I think .

C heers,
Gregor___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev