April 29, 2012
On Saturday, 28 April 2012 at 21:12:39 UTC, SomeDude wrote:
> […] And this opinion doesn't come just out of thin air, I speak from my own professional experience.

Regardless of the fact that I tend to agree with you on this one, isn't »my own professional experience« usually a synonym for »thin air«? ;)

David
April 29, 2012
On Saturday, 28 April 2012 at 18:48:18 UTC, Walter Bright wrote:
> Andrei and I had a fun discussion last night about this question. The idea was which features in D are redundant and/or do not add significant value?
>
> A couple already agreed upon ones are typedef and the cfloat, cdouble and creal types.
>
> What's your list?

garbage collector
*duck and run*

The point I'm trying to make is... normally I would use around 4-5 different languages depending on _what_ problem I'm currently solving... occasionally even languages I'm not particularly proficient with, just because a language might have an edge in a certain domain... however 'D' basically is good enough at "everything"... with some few exceptions.

So what one person considers redundant, is integral to someone else with a different background... no, D doesn't have too many features.

April 29, 2012
"Peter Alexander" <peter.alexander.au@gmail.com> wrote in message news:nvvuxboigxxfdqfhyftw@forum.dlang.org...
>
> To be honest, I don't like the idea of member functions at all. Having two syntaxes for calling a function is the core problem, and UFCS is just an extra complication on top of it to try and mitigate the original problem.
>
> f(x) ---> x.f() is not progress in language design.
[...]
> I'm serious. I don't like overloaded syntax.  foo.bar shouldn't also mean (*foo).bar -- it causes confusion and introduces ambiguities when either could work. Combine this with opDispatch, UFCS and function overloading and your in for some nasty headaches.
[...]
>
> Glancing at that code, it looks like foo has two member variables. It is also not clear that each access involves a hash-table lookup.

It sounds like you just simply don't like abstractions. I can understand that (although I don't agree with it), but it always puzzles me why such people even try to use high-level langauges at all instead of just binary machine code.

And for the record, I've *never* seen anyone confused by foo.bar syntax being used on reference types.


April 29, 2012
On 04/29/2012 11:31 AM, foobar wrote:
> On Sunday, 29 April 2012 at 08:58:24 UTC, Timon Gehr wrote:
>>> [...]
>>> Indeed but I'd go even further by integrating it with ranges so that
>>> ranges would provide an opApply like method e.g.
>>> auto r = BinaryTree!T.preOrder(); // returns range
>>> r.each( (T elem) { ...use elem...}); // each method a-la Ruby
>>>
>>
>> Well, I don't think this is better than built-in foreach (with full
>> break and continue and goto even for user-defined opApply!)
>
> I think we reached a matter of taste here.

Certainly, and this applies to the other issues as well.

> How often do you use these features anyway in your regular code?

Not too often, but it is awesome that it actually works. ;)

> I prefer a more functional style
> with higher order functions (map/reduce/filter/etc..) so for me foreach
> is about applying something to all elements and doesn't entail usage of
> break/continue/etc..

Some algorithms are better expressed in functional terms, some algorithms are better expressed in imperative terms. I think a combination of the two usually is the best choice.

> I'll use these constructs in a for loop but not a foreach loop.
>

break can be used as an optimisation to stop execution of a loop that performs a 'reduce' if the result cannot change after a certain point. I use continue mostly for 'filter'-ing out elements from consideration.

Usually there is not a huge difference between imperative style and functional style loops.

>>
>>>>
>>>>> * enum - enum should be completely redesigned to only implement
>>>>> what it's named after: enumerations.
>>>>>
>>>>
>>>> What is the benefit?
>>>
>>> On the one hand the current enum for manifest constants is a hack due to
>>> weaknesses of the toolchain
>>
>> I think that is actually not true. It might have been the original
>> motivation, but it has gone beyond that. Which weaknesses in
>> particular? I don't think that the toolchain can be improved in any
>> way in this regard.
>
> The weakness as far as I know is about link time optimization of constants.
> But regardless, my ideal implementation of so called "compile-time"
> features, including compile time constants, would be very different anyway.
>

Well, you never elaborate on these things. BTW, what is your stance on template haskell?

>>
>>> and on the other hand it doesn't provide
>>> properly encapsulated enums
>>
>> Those could in theory be added without removing the manifest constant
>> usage.
>>
>>> such as for instance the Java 5.0 ones or
>>> the functional kind.
>>>
>>
>> An algebraic data type is not an 'enumeration', so this is a moot point.
>
> I disagree. They are a generalization of the concept. In fact,
> functional languages such as ML implement c style enums as an algebraic
> data type.
>

The current way enums can be used as manifest constants is a generalization as well. The generalization takes place on the static semantics level instead of on the conceptual level though.


>>> [...]
>>>
>>> I should be able to use a *very* minimalistic system to write completely
>>> _regular_ D code and run it at different times.
>>
>> Examples in concrete syntax? How would you replace eg. string mixin
>> functionality?
>>

?

>>
>>> This is a simple matter
>>> of separation of concerns: what we want to execute (what code) is
>>> separate to the concern of when we want to execute it.
>>>
>>
>> It is not. For example, code that is only executed during CTFE does
>> never have to behave gracefully if the input is ill-formed.
>
> I disagree - you should make sure the input is valid or all sorts of bad
> things could potentially happen such as a compiler can get stuck in an
> infinite loop.

It could fail in a number of other ways. I don't think that this example can be used to invalidate the statement.

> If you only use a batch mode compiler you can simply kill
> the process which btw applies just the same to your user program.

Maybe the user program should not be killed. See your IDE example.

> However, if you use an integrated compiler in your IDE that could cause
> me to lose part of my work if the IDE crashes.

Why would the IDE crash?
April 29, 2012
On 04/29/2012 08:35 AM, Nick Sabalausky wrote:
> "Timon Gehr"<timon.gehr@gmx.ch>  wrote in message
> news:jnhpvv$ih4$1@digitalmars.com...
>> On 04/28/2012 11:04 PM, Dmitry Olshansky wrote:
>>>
>>> But how about:
>>> alias thing = runSomeCtfe();
>>>
>>
>> That would work in certain cases, where the initializer is not a single
>> symbol. But then, I kinda like the way 'enum' is generalized in D:
>>
>> enum Foo{
>>      member1,
>>      member2,
>>      member3,
>> }
>>
>> =>  (allow non-integral enumerations)
>>
>> enum Foo{
>>      member1 = "1",
>>      member2 = "2",
>>      member3 = "3",
>> }
>>
>
> Those are good. They are essentially enumerations.
>
>> =>  (anonymous enums)
>>
>> enum{
>>      member1 = "1",
>>      member2 = "2",
>>      member3 = "3",
>> }
>>
>
> I don't think "anonymous enum" makes any sense at all. It's *not* an
> enumeration by any stretch of the term,

enum{
    member1,
    member2,
    member3,
}

static assert(member1+1==member2 && member2+1==member3);


> it's just a series of manifest
> constants. The fact that they're grouped doesn't even have any semantic
> consequence, as far as I'm aware.

The only differences are that they don't occupy their own namespace and they don't define their own type. But non-anonymous enums are _just a bunch of manifest constants_ as well! Therefore there are ways in which the generalisation makes sense.


>
>> =>  (a single member is okay)
>>
>> enum{
>>      member1 = "1",
>> }
>>
>> =>  (syntactic sugar)
>>
>> enum member1 = "1";
>>
>
> Just simpler examples of the above, which isn't any form of enumeration at
> all.
>

'enum' declares manifest constants whether or not it is anonymous. Afaik go uses 'const' for enumerations and manifest constants. 'const' means a different thing in D. (arguably, 'readonly' would be a better fit) I don't think that the exact keywords matter a huge deal here.
April 29, 2012
On Sunday, 29 April 2012 at 11:23:17 UTC, Timon Gehr wrote:
> On 04/29/2012 11:31 AM, foobar wrote:
>> On Sunday, 29 April 2012 at 08:58:24 UTC, Timon Gehr wrote:
>>>> [...]
>>>> Indeed but I'd go even further by integrating it with ranges so that
>>>> ranges would provide an opApply like method e.g.
>>>> auto r = BinaryTree!T.preOrder(); // returns range
>>>> r.each( (T elem) { ...use elem...}); // each method a-la Ruby
>>>>
>>>
>>> Well, I don't think this is better than built-in foreach (with full
>>> break and continue and goto even for user-defined opApply!)
>>
>> I think we reached a matter of taste here.
>
> Certainly, and this applies to the other issues as well.
>
>> How often do you use these features anyway in your regular code?
>
> Not too often, but it is awesome that it actually works. ;)
>
>> I prefer a more functional style
>> with higher order functions (map/reduce/filter/etc..) so for me foreach
>> is about applying something to all elements and doesn't entail usage of
>> break/continue/etc..
>
> Some algorithms are better expressed in functional terms, some algorithms are better expressed in imperative terms. I think a combination of the two usually is the best choice.

I agree and indeed I haven't argued to remove break/continue from the language. Imperative style loops are already expressible with for/while/etc where break/continue work as advertizes. IMO a foreach loop is a higher level concept more suitable for functional style loops.
In any case, break/continue is implemented via opApply's return values and as such doesn't require anything special from the compiler to implement a library based foreach.


>
>> I'll use these constructs in a for loop but not a foreach loop.
>>
>
> break can be used as an optimisation to stop execution of a loop that performs a 'reduce' if the result cannot change after a certain point. I use continue mostly for 'filter'-ing out elements from consideration.
>

Well, I'll use a filter to filter out elements.... :)

> Usually there is not a huge difference between imperative style and functional style loops.
>
>>>
>>>>>
>>>>>> * enum - enum should be completely redesigned to only implement
>>>>>> what it's named after: enumerations.
>>>>>>
>>>>>
>>>>> What is the benefit?
>>>>
>>>> On the one hand the current enum for manifest constants is a hack due to
>>>> weaknesses of the toolchain
>>>
>>> I think that is actually not true. It might have been the original
>>> motivation, but it has gone beyond that. Which weaknesses in
>>> particular? I don't think that the toolchain can be improved in any
>>> way in this regard.
>>
>> The weakness as far as I know is about link time optimization of constants.
>> But regardless, my ideal implementation of so called "compile-time"
>> features, including compile time constants, would be very different anyway.
>>
>
> Well, you never elaborate on these things. BTW, what is your stance on template haskell?

I discussed this many times in the past...
I don't really know haskell. But I do like ML.

>
>>>
>>>> and on the other hand it doesn't provide
>>>> properly encapsulated enums
>>>
>>> Those could in theory be added without removing the manifest constant
>>> usage.
>>>
>>>> such as for instance the Java 5.0 ones or
>>>> the functional kind.
>>>>
>>>
>>> An algebraic data type is not an 'enumeration', so this is a moot point.
>>
>> I disagree. They are a generalization of the concept. In fact,
>> functional languages such as ML implement c style enums as an algebraic
>> data type.
>>
>
> The current way enums can be used as manifest constants is a generalization as well. The generalization takes place on the static semantics level instead of on the conceptual level though.
>

A language is the interface between a human programmer and a computer and should IMO provide clear conceptual level abstractions for the benefit of the human. I realize that using enum for manifest constants makes sense on the implementation level but I feel the compiler should work for me and not the other way around.

>>>> [...]
>>>>
>>>> I should be able to use a *very* minimalistic system to write completely
>>>> _regular_ D code and run it at different times.
>>>
>>> Examples in concrete syntax? How would you replace eg. string mixin
>>> functionality?
>>>
>
> ?

macro testMacro() {
 std.writeln("Hello world!");
 <| std.writeln("Hello world!"); |>
}

macro is a syntactic sugar on top of a regular function. You can call it just like you call a regular function. The first line is executed regularly and the second one is mixed-in [returned token stream from the macro]
since the macro is evaluated by the compiler, the first line would generate compile-time output. the second line would be part of the generated code and would be thus executed during run-time of my code.

Regarding syntax, the main difference is that it's a token stream and not text but otherwise pretty much the same as current CTFE. The important difference here is the execution model which is different from CTFE.

>
>>>
>>>> This is a simple matter
>>>> of separation of concerns: what we want to execute (what code) is
>>>> separate to the concern of when we want to execute it.
>>>>
>>>
>>> It is not. For example, code that is only executed during CTFE does
>>> never have to behave gracefully if the input is ill-formed.
>>
>> I disagree - you should make sure the input is valid or all sorts of bad
>> things could potentially happen such as a compiler can get stuck in an
>> infinite loop.
>
> It could fail in a number of other ways. I don't think that this example can be used to invalidate the statement.
>
>> If you only use a batch mode compiler you can simply kill
>> the process which btw applies just the same to your user program.
>
> Maybe the user program should not be killed. See your IDE example.
>
>> However, if you use an integrated compiler in your IDE that could cause
>> me to lose part of my work if the IDE crashes.
>
> Why would the IDE crash?

My example illustrates that the same considerations should be given to "compile-time" code as well as to the client application and it all depends on what you're trying to achieve.

April 29, 2012
On Saturday, 28 April 2012 at 18:48:18 UTC, Walter Bright wrote:
> What's your list?

My personal list of features I could easily live without – some of these might be controversial, but yes, I have written non-trivial amounts of code in both D1 and D2:

 - Anonymous nested classes: They might be useful in Java, particularly in older incarnations, but not so much in D – never used them.

 - Comma operator: Kill it with extreme prejudice, it is an anti-feature (allow it in for loop expressions if you really want to, but I think there are better solutions).

 - Typesafe variadics: They look nice on paper, but I haven't used them once. Often, you either explicitly need C-style variadics, or you want to accept e.g. multiple ranges of the same element type, where you need template variadics anyway.

 - Unsigned right shift, but I can see how it can be useful (simply underused?).

 - HexString, DelimitedString, r""-WysiwigString, TokenString: I didn't ever use the former two (for including binary data in the program, ImportExpression is imho much easier than generating a source file containing a HexString). As for r"", every time I actually need WysiwigString, I use backticks, because such strings often contain quotes anyway. Regarding TokenString(i.e. q{}) – it is certainly a very nice idea, especially regarding syntax highlighting, and I occasionally use them for CTFE code generation. But without any kind of support for string interpolation, I typically find myself using normal strings for everything except small self-contained portions of code (where mixin templates would probably be cleaner). The problem is that can't just »interrupt« q{}s strings to do something like »q{…} ~ identifierName ~ q{…}«, because there will most likely be unmatched braces – but this is needed in assembling mixin strings all the time…

 - Concatenation of adjacent strings: Also an anti-feature, imho.

 - Floating point comparison operators like !<>= (yes, that _is_ valid D code): I must admit that I seldom write code relying on the finer details of IEEE-754 semantics, but can't they just be »lowered« to a combination of the more common ones?

 - »Short« floating point literals like .4 instead of 0.4 and 4. instead of 4.0 – the saved characters are not worth the syntax special cases, especially w.r.t. UFCS.

 - new/delete issues have been discussed many times

 - Built-in arrays and AAs: They are convenient to use, but as far as I can see the single biggest GC dependency in the language. Why not lower array and AA literals to expression tuples (or whatever) to make the convenient syntax usable with custom (possibly non-GC safe) containers as well? A GC'd default implementation could then be provided in druntime, just like today's arrays and AAs.

 - shared: TLS by default is great, but only __gshared is really usable right now. IMHO, shared had better been reserved for a comprehensive take on the subject, rather than the half-baked implementation we have right now.

David
April 29, 2012
On 29.04.2012 10:42, Jonathan M Davis wrote:
> On Sunday, April 29, 2012 09:53:15 Dmitry Olshansky wrote:
>> On 29.04.2012 4:31, Jonathan M Davis wrote:
>>> For better or worse, the solution for smart pointers in D would be to use
>>> opDispatch,
>>
>> *cough* alias this *cough*
>
> That's not necessarily a good idea, depending on how it's used. You want to
> avoid having the smart pointer implicitly convert to what it holds such that a
> reference to it leaks. If you're dealing with a pointer to a struct, and alias
> this aliases to the struct (rather than the pointer), then you're okay. But if
> you're dealing with a class, you don't have that option. So, alias this ends
> up leaking a reference to the class, which defeats the purpose of the smart
> pointer. You have the same problem if alias this aliases to the pointer rather
> than what's pointed to.

Point taken.

>
> But regardless of whether you use alias this or opDispatch, you have the same
> problem with regards to ->. In C++, . would be used to call the smart
> pointer's functions, and ->  would be used to call functions on the object
> pointed to. In D, the two aren't distinguished - both use . - so you can't
> have any functions on the type pointed to which conflict with the smart
> pointer's functions, or you won't be able to call them (unless another way to
> call them is provided somehow). So, it's definitely something that C++ does
> better with as far as that goes.


So you just need not to have any _member_ functions on smart pointer? Just use free functions that take SmartPointer!T. UFCS may also lend a hand if T and SmartPointer do not have ambiguous funcs.

-- 
Dmitry Olshansky
April 29, 2012
On Sunday, April 29, 2012 16:28:23 Dmitry Olshansky wrote:
> On 29.04.2012 10:42, Jonathan M Davis wrote:
> > But regardless of whether you use alias this or opDispatch, you have the same problem with regards to ->. In C++, . would be used to call the smart pointer's functions, and ->  would be used to call functions on the object pointed to. In D, the two aren't distinguished - both use . - so you can't have any functions on the type pointed to which conflict with the smart pointer's functions, or you won't be able to call them (unless another way to call them is provided somehow). So, it's definitely something that C++ does better with as far as that goes.
> 
> So you just need not to have any _member_ functions on smart pointer? Just use free functions that take SmartPointer!T. UFCS may also lend a hand if T and SmartPointer do not have ambiguous funcs.

That could work (though they'd obviously have to be in the same module as the smart pointer - not exactly an onerous requirement - in order to be able to access its private members).

- Jonathan M Davis
April 29, 2012
Am 29.04.2012 10:42, schrieb Timon Gehr:
> On 04/29/2012 08:31 AM, Paulo Pinto wrote:
>> Am 28.04.2012 20:47, schrieb Walter Bright:
>>> Andrei and I had a fun discussion last night about this question. The
>>> idea was which features in D are redundant and/or do not add significant
>>> value?
>>>
>>> A couple already agreed upon ones are typedef and the cfloat, cdouble
>>> and creal types.
>>>
>>> What's your list?
>>
>> - two different ways of creating function pointers is confusing
>> (function and delegate)
>>
>> I understand the reasoning, but makes one think all the time when
>> to use what.
>>
>
> 'delegate' is more powerful, 'function' is more efficient. If you don't
> want to think about it, just use 'delegate'. I'd rather see 'function'
> implicitly convert to 'delegate' than to have it gone. D can be used for
> systems programming after all!

That is what I mean. The compiler could make the distinction between function and delegate itself.

I am not arguing to remove the feature, rather to have the compiler check it for me. Surely it can see if I am passing the delegate to D code or extern C/C++ code and act accordingly.

>
>> - sometimes D code looks like template and mixins gone mad
>> While I do appreciate the power, it can be quite confusing to try
>> to understand what the code does. Specially with the lack of support
>> in mixin's debugging
>>
>
> pragma(msg, ...) ?

Too low level?

>
>>
>> - misuse of enum to declare constants
>> I prefer that the use of const would be possible
>>
>
> const infects the type and const-qualified data can exist at runtime, so
> it is not possible.

Yeah, but it brings me back bad memories from the early days, when C++ compilers did not fully support C++98, and we had to resort to the enum trick to create constants.

>
>> - conditional compilation is hard to follow without syntax highlighting
>> Other languages with conditional compilation make it easier to follow
>> what is what. e.g. Turbo Pascal/Delphi, C#, Modula-3, Ada
>>
>
> That is not a language issue.

How come?

In the languages mentioned above, the conditional compilation stands out in clear text that is doing something at compile time.

In D you need to be aware which statements are compile time and which are not.

Not a big deal to argue about, but easy trap for D newbies.

--
Paulo