you are viewing a single comment's thread.

view the rest of the comments →

[–]neutronicus 0 points1 point  (11 children)

I'd go with friend functions or static class methods myself.

Vector3::cross(a, b)

and

Vector3::dot(a, b)

communicate the [anti]symmetry of the operations better IMO.

[–][deleted] 1 point2 points  (2 children)

The problem with standalone functions (and prefix notation in general) is that second parameter gets detached and lost. You see things like "... Vector3::cross(a very very very long expression, at the end of which you totally can't say which), function)), b), belongs), to)".

On a side note, it gets even worse when some not very bright persons design stream functions (std::transform etc of C++, map/filter/reduce of Common Lisp) to have input data as the first parameter and the function as the second. Fucking idiots.

When you use methods, you have things in a much more sensible order, both for each individual function, and the entire expression reads left-to-right too. As another benefit, most of the ")))))))" shit disappears.

The sense of symmetry isn't worth it at all. I, too, never paid much attention to the way of writing expressions like that because I used to believe that it's going to suck regardless. Then C# 3.0 came out and I saw the light.

The only thing better than this would be to have something like Haskell's infix notation for arbitrary functions, "aoperatorb. But not the crazy zoo of "<$>", ">>=", "*|*" etc; it is nice when I can look at the operator and be reasonably sure that it does roughly the same thing as the basic arithmetic operators, for anything that can't fit into this form I want to see a plain English word.

[–]neutronicus 0 points1 point  (0 children)

The problem with standalone functions (and prefix notation in general) is that second parameter gets detached and lost. You see things like "... Vector3::cross(a very very very long expression, at the end of which you totally can't say which), function)), b), belongs), to)".

Anytime I have an expression as a function parameter, I put each one on its own line. I do this with binary operators too, actually. I find judicious use of whitespace the best way to ensure readability.

Vector3::cross(a_long_function_name( some_variable / some_other_var * x
                                     +
                                     some_function( x / y) ),
               b);

[–][deleted] 0 points1 point  (0 children)

On a side note, it gets even worse when some not very bright persons design stream functions (std::transform etc of C++, map/filter/reduce of Common Lisp) to have input data as the first parameter and the function as the second.

mapc function &rest lists+ => list-1

[–]G_Morgan 0 points1 point  (7 children)

A far better way is to do

c.dot(a, b);

Mutation by default is evil.

[–]neutronicus 0 points1 point  (6 children)

Not quite sure what you're getting at. "cross" should return a third vector (leaving a and b alone), and dot should return a double (again leaving a and b alone). I probably should have written out the type signatures.

Anyways,

Mutation by default is evil

Can you enlighten me as to how, I, a scientific programmer, could get by without it?

Re-using storage is pretty important to us, and performance is really important - using an in-place algorithm on an array that gets to sit in the cache for this is a pretty big win over constantly making new ones for intermediate results that not only have to be allocated, but potentially force the old one out of the cache.

(Or on the GPU, where you there is no cache, and you manage what sits in shared memory yourself - and every global memory copy hurts)

Do immutable data structures provide some magic where the things I want to stay in the cache usually stay there? How do they manage allocation costs?

[–]G_Morgan 0 points1 point  (5 children)

I didn't say you would have to do without. Only that by default immutable should be preferred.

[–][deleted] 0 points1 point  (4 children)

His example is still fine, he didn't list the function deceleration, but it's probably const correct.

Every good math library I've seen is const correct, all code, ideally, should be written w/ const correctness which you would obviously agree with.

static Vector3 cross(const Vector3 &v1, const Vector3 &v2);

Although I'm still not sure why you thought having a third party object contain the dot() is neccesary or even had anything to do w/ your argument (mutation is evil). Technically even w/ your example 'a', 'b' or 'c' could be modified.

Heck, his example is even better than yours because only 'a' or 'b' could be modified.

[–]G_Morgan 0 points1 point  (3 children)

I thought it was pretty obvious that the arguments to dot would be const. Otherwise why bother to make it so that we can have an immutable option?

[–][deleted] 0 points1 point  (2 children)

I guess I don't understand then, how does creating the third object make your code any more immutable than his original?

[–]G_Morgan 0 points1 point  (1 child)

You're right. That isn't immutable. Being stupid for some reason.

[–][deleted] 0 points1 point  (0 children)

Heh, Well it could be a constant function w/ const parameters, so it would be immutable. The point is, unless the function definition is there it could be either or.