www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Replacing built-in complex? What's this about?

reply Stewart Gordon <smjg_1998 yahoo.com> writes:
The plan to remove the built-in complex and imaginary types has finally 
come to my attention.  I seem to have somehow blinked and missed the 
discussions earlier this year about it.

The motives seem to be along the lines of reducing compiler complexity 
(not sure if any pun has been intended) and freeing up keywords.  But is 
it really worth it?

I've noticed that std.complex is still heavily under construction. 
Which would explain why it doesn't yet have pure imaginary types, among 
other things.

My thoughts are:

- Built-in complex types can't be that hard to implement.  That Walter 
is thinking about removing them sounds to me like a sign that D is 
losing its simplicity and trying to cut corners to get it back.

- If the point is to cut D's number of keywords, this can be done 
without throwing the whole feature out of the window.  One way, inspired 
by the const/invariant notation, is

     complex(real)
     imaginary(double)

That said, the existing c* and i* keywords STM no big deal, considering 
that they aren't standard English words, and so not that likely that 
somebody will want to use them for his/her own purposes.

- The current built-in complex is relatively simple.  Libraries have 
only a few complex types to deal with.  The added complexity of 
std.complex means that library programmers will have to consider 
supporting both cartesian and polar representation, among possible other 
things.  Using templates just to cover all possible cases would prevent 
the functions from being virtual.

- Link compatibility with C has been given as an important feature of D. 
  Since C has complex and imaginary types, can this work fully if D 
doesn't?  It might get even trickier when we consider link compatibility 
with C++, which must support function overloading.  Even if C++ doesn't 
yet have complex/imaginary, it will probably get them soon, and FAIK 
some C++ compilers probably already have it as an extension.

- The imaginary literal notation and resulting concise notation for 
complex numbers are nice ideas.  It would be sad to lose these.

- D has the potential to supersede Fortran as a scientific programming 
language.  Indeed, "Numerical programmers" is one entry in the "Who D is 
For" list.  So if Fortran has an excuse for built-in complex, why not D?

To conclude, while library complex types are a nice idea, there's no 
need for them to replace the built-in complex types.  IMO it would work 
well to keep the built-in complex types, and have std.complex available 
as an alternative for when you want more power than that provided by the 
built-in types.  What's more, std.complex should provide conversions 
between its complex types and the built-in ones.

Thoughts?

Stewart.
Dec 27 2008
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Stewart Gordon wrote:
 The plan to remove the built-in complex and imaginary types has finally 
 come to my attention.  I seem to have somehow blinked and missed the 
 discussions earlier this year about it.
 
 The motives seem to be along the lines of reducing compiler complexity 
 (not sure if any pun has been intended) and freeing up keywords.  But is 
 it really worth it?
 
 I've noticed that std.complex is still heavily under construction. Which 
 would explain why it doesn't yet have pure imaginary types, among other 
 things.
 
 My thoughts are:
 
 - Built-in complex types can't be that hard to implement.  That Walter 
 is thinking about removing them sounds to me like a sign that D is 
 losing its simplicity and trying to cut corners to get it back.

I think that the justification should go the other way. A feature's existence must be justified, not its removal. Justification of complex as a built-in was always rather thin, and Walter is willing to implement the few peephole optimizations that obviate it.
 - If the point is to cut D's number of keywords, this can be done 
 without throwing the whole feature out of the window.  One way, inspired 
 by the const/invariant notation, is
 
     complex(real)
     imaginary(double)
 
 That said, the existing c* and i* keywords STM no big deal, considering 
 that they aren't standard English words, and so not that likely that 
 somebody will want to use them for his/her own purposes.

The point was not to reduce the number of keywords.
 - The current built-in complex is relatively simple.  Libraries have 
 only a few complex types to deal with.  The added complexity of 
 std.complex means that library programmers will have to consider 
 supporting both cartesian and polar representation, among possible other 
 things.  Using templates just to cover all possible cases would prevent 
 the functions from being virtual.

Cartesian vs. polar is an opt-in, not a must. All operations work with both representations, obviously with different performance tradeoffs. A library can stick with the representation that is most fit for it, or use templates if it is implementation-indifferent. Does this put more pressure on the library user to choose the best representation in a given situation? Sure. But compare with the previous situation, in which polar representation-friendly situations and applications were consistently the loser.
 - Link compatibility with C has been given as an important feature of D. 
  Since C has complex and imaginary types, can this work fully if D 
 doesn't?  It might get even trickier when we consider link compatibility 
 with C++, which must support function overloading.  Even if C++ doesn't 
 yet have complex/imaginary, it will probably get them soon, and FAIK 
 some C++ compilers probably already have it as an extension.

That's a good point.
 - The imaginary literal notation and resulting concise notation for 
 complex numbers are nice ideas.  It would be sad to lose these.

Please count the number of complex literals you use per 10,000 lines of code. This is an illusory advantage, and ironically one that fosters bad style. I was always infuriated when Walter mentioned it.
 - D has the potential to supersede Fortran as a scientific programming 
 language.  Indeed, "Numerical programmers" is one entry in the "Who D is 
 For" list.  So if Fortran has an excuse for built-in complex, why not D?

Because of advancement in compiler technology.
 To conclude, while library complex types are a nice idea, there's no 
 need for them to replace the built-in complex types.  IMO it would work 
 well to keep the built-in complex types, and have std.complex available 
 as an alternative for when you want more power than that provided by the 
 built-in types.  What's more, std.complex should provide conversions 
 between its complex types and the built-in ones.
 
 Thoughts?

I'd say, to paraphrase: "Although built-in complex types are a nice idea, there's no need for them to replace the library complex types." I took the liberty to replace "while" with "although" because that usage of "while" is considered bad style :o). Keeping both complex types around would further degrade justification for the built-in counterpart. Andrei
Dec 27 2008
next sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Andrei Alexandrescu wrote:
<snip>
 I think that the justification should go the other way. A feature's 
 existence must be justified, not its removal.

I disagree. It's important to weigh up the benefits against the costs of removing a feature. <snip>
 Cartesian vs. polar is an opt-in, not a must. All operations work
 with both representations, obviously with different performance
 tradeoffs.

But that's almost a vacuous truth at the moment, because no real operations have been implemented yet.
 - D has the potential to supersede Fortran as a scientific programming 
 language.  Indeed, "Numerical programmers" is one entry in the "Who D 
 is For" list.  So if Fortran has an excuse for built-in complex, why 
 not D?

Because of advancement in compiler technology.

So, is complex scheduled for removal from Fortran?
 To conclude, while library complex types are a nice idea, there's no 
 need for them to replace the built-in complex types.  IMO it would 
 work well to keep the built-in complex types, and have std.complex 
 available as an alternative for when you want more power than that 
 provided by the built-in types.  What's more, std.complex should 
 provide conversions between its complex types and the built-in ones.

 Thoughts?

I'd say, to paraphrase: "Although built-in complex types are a nice idea, there's no need for them to replace the library complex types." I took the liberty to replace "while" with "although" because that usage of "while" is considered bad style :o).

They cannot replace the library complex types, because these built-in types came first.
 Keeping both complex types around would further degrade justification 
 for the built-in counterpart.

You could just as well claim that library implementations of associative arrays, sorting and the like degrade justification for the built-in counterparts. But it's still useful to have the built-in counterparts. Stewart.
Dec 27 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Stewart Gordon wrote:
 Andrei Alexandrescu wrote:
 <snip>
 I think that the justification should go the other way. A feature's 
 existence must be justified, not its removal.

I disagree. It's important to weigh up the benefits against the costs of removing a feature.

Just saying it doesn't make it true. Wait, I did the same :o). My justification stems in the philosophy of preferring libraries over core features, all things being equal. There are many benefits to that philosophy, which makes it prevalent across today's languages. If you don't buy into that, we'll have to agree to disagree.
 <snip>
 Cartesian vs. polar is an opt-in, not a must. All operations work
 with both representations, obviously with different performance
 tradeoffs.

But that's almost a vacuous truth at the moment, because no real operations have been implemented yet.

I should have put the future tense in there. That elevates the statement from vacuous truth to intended design.
 - D has the potential to supersede Fortran as a scientific 
 programming language.  Indeed, "Numerical programmers" is one entry 
 in the "Who D is For" list.  So if Fortran has an excuse for built-in 
 complex, why not D?

Because of advancement in compiler technology.

So, is complex scheduled for removal from Fortran?

Fortran has too little support for abstraction to accommodate complex as a user-defined type gainfully. It also has a great deal of legacy code to worry about.
 To conclude, while library complex types are a nice idea, there's no 
 need for them to replace the built-in complex types.  IMO it would 
 work well to keep the built-in complex types, and have std.complex 
 available as an alternative for when you want more power than that 
 provided by the built-in types.  What's more, std.complex should 
 provide conversions between its complex types and the built-in ones.

 Thoughts?

I'd say, to paraphrase: "Although built-in complex types are a nice idea, there's no need for them to replace the library complex types." I took the liberty to replace "while" with "although" because that usage of "while" is considered bad style :o).

They cannot replace the library complex types, because these built-in types came first.

Yes, but in this case there's no right of the first-comer. Just because built-in complex was there first does not confer it extra rights beyond backwards compatibility, which for D2 is not a major concern.
 Keeping both complex types around would further degrade justification 
 for the built-in counterpart.

You could just as well claim that library implementations of associative arrays, sorting and the like degrade justification for the built-in counterparts. But it's still useful to have the built-in counterparts.

You see, built-in associative arrays have exactly two advantages: (a) The type name is very terse, e.g. uint[string] vs. Map!(string, uint). (b) The literal syntax is also very terse, e.g. [ "a" : 1, "b" : 2 ] vs. makeMap!("a", 1, "b", 2). In my opinion all other alleged advantages are illusory. In fact, having builtins wielding too much power is in fact a sign the language design didn't go that well: If a built-in type has too many advantages over a user-defined type, that only means user-defined types are robbed of that many possibilities. Andrei
Dec 27 2008
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Andrei Alexandrescu wrote:
 Stewart Gordon wrote:
 Andrei Alexandrescu wrote:
 <snip>
 I think that the justification should go the other way. A feature's 
 existence must be justified, not its removal.

I disagree. It's important to weigh up the benefits against the costs of removing a feature.

Just saying it doesn't make it true. Wait, I did the same :o). My justification stems in the philosophy of preferring libraries over core features, all things being equal. There are many benefits to that philosophy, which makes it prevalent across today's languages. If you don't buy into that, we'll have to agree to disagree.
 <snip>
 Cartesian vs. polar is an opt-in, not a must. All operations work
 with both representations, obviously with different performance
 tradeoffs.

But that's almost a vacuous truth at the moment, because no real operations have been implemented yet.

I should have put the future tense in there. That elevates the statement from vacuous truth to intended design.
 - D has the potential to supersede Fortran as a scientific 
 programming language.  Indeed, "Numerical programmers" is one entry 
 in the "Who D is For" list.  So if Fortran has an excuse for 
 built-in complex, why not D?

Because of advancement in compiler technology.

So, is complex scheduled for removal from Fortran?

Fortran has too little support for abstraction to accommodate complex as a user-defined type gainfully. It also has a great deal of legacy code to worry about.
 To conclude, while library complex types are a nice idea, there's no 
 need for them to replace the built-in complex types.  IMO it would 
 work well to keep the built-in complex types, and have std.complex 
 available as an alternative for when you want more power than that 
 provided by the built-in types.  What's more, std.complex should 
 provide conversions between its complex types and the built-in ones.

 Thoughts?

I'd say, to paraphrase: "Although built-in complex types are a nice idea, there's no need for them to replace the library complex types." I took the liberty to replace "while" with "although" because that usage of "while" is considered bad style :o).

They cannot replace the library complex types, because these built-in types came first.

Yes, but in this case there's no right of the first-comer. Just because built-in complex was there first does not confer it extra rights beyond backwards compatibility, which for D2 is not a major concern.
 Keeping both complex types around would further degrade justification 
 for the built-in counterpart.

You could just as well claim that library implementations of associative arrays, sorting and the like degrade justification for the built-in counterparts. But it's still useful to have the built-in counterparts.

You see, built-in associative arrays have exactly two advantages: (a) The type name is very terse, e.g. uint[string] vs. Map!(string, uint). (b) The literal syntax is also very terse, e.g. [ "a" : 1, "b" : 2 ] vs. makeMap!("a", 1, "b", 2). In my opinion all other alleged advantages are illusory. In fact, having builtins wielding too much power is in fact a sign the language design didn't go that well: If a built-in type has too many advantages over a user-defined type, that only means user-defined types are robbed of that many possibilities. Andrei

I meant makeMap("a", 1, "b", 2), and also meant to finish my message, continuing with: In the case of hashtables both (a) and (b) come about rather often. In the case of complex, it's just not so: (a) A battery of alias declarations take care of the naming issue; (b) Complex literals are rare, far between, and to be discouraged. Andrei
Dec 27 2008
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Bill Baxter wrote:
 On Sun, Dec 28, 2008 at 8:46 AM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Stewart Gordon wrote:
 So, is complex scheduled for removal from Fortran?

user-defined type gainfully. It also has a great deal of legacy code to worry about.

Additionally, if you're in Fortran's target demographic it means you're about 1000x more likely than the average programmer to actually need complex numbers.

A similar statement could be said about D's target demographic, though the figure would probably be a little smaller because of D's target demographic being a proper superset of Fortran's. Stewart.
Dec 28 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Sun, Dec 28, 2008 at 8:46 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Stewart Gordon wrote:
 So, is complex scheduled for removal from Fortran?

Fortran has too little support for abstraction to accommodate complex as a user-defined type gainfully. It also has a great deal of legacy code to worry about.

Additionally, if you're in Fortran's target demographic it means you're about 1000x more likely than the average programmer to actually need complex numbers.
 You could just as well claim that library implementations of associative
 arrays, sorting and the like degrade justification for the built-in
 counterparts.  But it's still useful to have the built-in counterparts.

You see, built-in associative arrays have exactly two advantages: (a) The type name is very terse, e.g. uint[string] vs. Map!(string, uint). (b) The literal syntax is also very terse, e.g. [ "a" : 1, "b" : 2 ] vs. makeMap!("a", 1, "b", 2). In my opinion all other alleged advantages are illusory.

Are you including the ability to use them at compile time among the illusory advantages?
 In fact, having
 builtins wielding too much power is in fact a sign the language design
 didn't go that well: If a built-in type has too many advantages over a
 user-defined type, that only means user-defined types are robbed of that
 many possibilities.

I agree and think the ability to do this-or-that at compile time is also in this category. In an ideal world, *any* D code that doesn't depend on dynamic data would be runnable at compile time. A library implementation of hash tables would then "just work" at compile time without any special considerations on the part of the developer. But I don't think the compiler is quite there yet, so till it is, there is an advantage to having a built-in hash-table that can run at compile time. --bb
Dec 27 2008
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Stewart Gordon wrote:
 - Link compatibility with C has been given as an important feature of D. 
  Since C has complex and imaginary types, can this work fully if D 
 doesn't?  It might get even trickier when we consider link compatibility 
 with C++, which must support function overloading.  Even if C++ doesn't 
 yet have complex/imaginary, it will probably get them soon, and FAIK 
 some C++ compilers probably already have it as an extension.

Andrei answered most of these points, and I want to deal with this one. C++ is never going to get native complex and imaginary, based on my discussions with people involved with the C++ standards committee. It won't for the same reasons Andrei mentions that it should be removed from D. For link compatibility, this is not the issue it seems to be because C doesn't have name mangling. Parameter passing/return sequences can easily be made compatible with a struct complex.
Dec 27 2008
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Walter Bright wrote:
[Re: link compatibility and library complex types]
 
 For link compatibility, this is not the issue it seems to be because C 
 doesn't have name mangling. Parameter passing/return sequences can 
 easily be made compatible with a struct complex.

Are you sure about that? Isn't there some calling convention where complexes and equivalent structs are treated differently? IIRC either the x86 C calling convention or the x86-64 one falls into this category for returning them from functions. I also seem to recall passing them as parameters is different on x86-64 when only one register is available -- passing the "native" version in one register and a stack slot while the struct is put entirely on the stack. Both of these are off the top of my head though, so I might be mistaken since I haven't looked at the specs recently and these weren't really the things I paid attention to when I did.
Dec 30 2008
prev sibling parent Don <nospam nospam.com> writes:
Stewart Gordon wrote:
 The plan to remove the built-in complex and imaginary types has finally 
 come to my attention.  I seem to have somehow blinked and missed the 
 discussions earlier this year about it.
 
 The motives seem to be along the lines of reducing compiler complexity 
 (not sure if any pun has been intended) and freeing up keywords.  But is 
 it really worth it?

The argument for built-in imaginary types is _extremely_ weak. The imaginary numbers are not closed under multiplication: idouble a, b; auto c = a * b; c is of type double! This is a horribly nasty thing to have in the core language. It introduces so many special cases, and for almost no benefit at all. You never want to use imaginary numbers, you _always_ want to convert them to reals. The only thing you want, really, is the imaginary literal 1i. The ireal, idouble, and ifloat types with their bizarre semantics are a hell of a price to pay for that one literal.
Dec 28 2008