November 15, 2021

On Monday, 15 November 2021 at 14:09:53 UTC, Paul Backus wrote:

>

On Monday, 15 November 2021 at 14:02:42 UTC, Imperatorn wrote:

>

What's wrong about retro? Too retro :D

std.range.retro is honestly just a bad name. It should have been reversed, or maybe reverser.

That reverser thing could have been a builtin property e.g .reverser, problem is that the expression that gives a foreach range (https://dlang.org/spec/statement.html#foreach-range-statement) is a bit degenerated for now:

LwrExpression .. UprExpression

is actually not an expression at all thus

(LwrExpression ..UprExpression).reverser

cant work. But Ideally D could get rid of that foreach_reverse keyword with a property.

foreach (i; (0 .. length).reverser){}
foreach (e; elems.reverser){}

Off topic side node : the old DIP 58 shows that the idea to have the .. D operator is not new but apparently the case of simplifying foreach was not a goal.

November 15, 2021
On 15.11.21 15:02, Imperatorn wrote:
> On Monday, 15 November 2021 at 12:41:51 UTC, Timon Gehr wrote:
>> On 15.11.21 10:29, Imperatorn wrote:
>>> On Monday, 15 November 2021 at 00:49:17 UTC, tsbockman wrote:
>>>> [...]
>>>
>>> Well, obviously :)
>>>
>>> It was more of a joke, but it's still pretty cringe to see it
>>
>> The reason it exists is that reverse iteration over unsigned types is notoriously error prone. I think it's pretty nice to have this built in, knowing that it will be directly transformed into a simple for loop even in debug builds. It also allows easily reversing the order of iteration of any foreach loop, no matter which form. In particular, there is `static foreach_reverse`.
> 
> What's wrong about retro? Too retro :D

My post was literally a description of drawbacks of retro. There's nothing wrong with it in particular, but the main drawback is that in general you need to do more than slap retro on the aggregate in order to get the correct semantics.

Tuple!(int,int)[] a=...;
foreach(i,x;a){} // reverse this!

November 15, 2021
On Mon, Nov 15, 2021 at 02:23:03PM +0000, bauss via Digitalmars-d wrote: [...]
> > * Features you'd like to see in D
> 
> I'd like to see everything currently in D finished, instead of introducing new features.
> 
> Sometimes less is more.

+1.  We've a lot of unfinished/incomplete things: shared, std.experimental.allocator, etc..

It's more fun to implement new things, but *somebody* has to do the grunt work of polishing what's already there.


T

-- 
Once bitten, twice cry...
November 15, 2021
  • Worst features implemented in a non-toy language?
  • Worst features (in your opinion) in D
  • Features you'd like to see in D

Undisputed first place: Reference types (ptrs, function ptrs, arrays, classes, etc.) are nullable by default in Java and many others, including D. D could have had int* and int*?, int function() and int function?(), int[] and int[]?, Object and Object?.

std.typecons.Nullable is basically a type constructor, and faking one via templates only works half-way (for example, const(int)? and const(int?) are the same type). Distinguishing nullable types is very useful documentation-wise. If you do this, enable nullable for every type, even if int? is implemented by a plain bool-int pair.

For me, second place for bad D feature: function types. Not function pointer types but mere function types. They aren't even documented, so it's technically a bug that they exist.


A few other nuisances that are just unnecessary to work around and have absolutely no justification existing:

  1. Cannot initialize an empty AA by [ ]. Especially a nuisances when wanting to pass an empty AA as an argument to a function.
  2. The new keyword is needed to allocate class objects. Makes meta-programming annoying, and one cannot have a literal pointer like new int(1) for, say, Object.
  3. One cannot express every type (not counting encapsulation, i.e. Voldemort types) directly: Notably, one cannot express a function pointer returning by reference in a function argument list.
November 15, 2021

On Monday, 15 November 2021 at 14:19:00 UTC, Steven Schveighoffer wrote:

>

I think I can count on one finger (and you can guess which one) the times I've used foreach_reverse.

Pinkie?

November 15, 2021
On Monday, 15 November 2021 at 14:19:00 UTC, Steven Schveighoffer wrote:
> I think I can count on one finger (and you can guess which one) the times I've used `foreach_reverse`.

~/arsd$ grep foreach_reverse *.d | wc
     34

it p useful actually.
November 15, 2021

On Sunday, 14 November 2021 at 18:41:10 UTC, Dr Machine Code wrote:

>

On Monday, 11 October 2021 at 18:27:29 UTC, russhy wrote:

[...]

> >

Features you'd like to see in D

- tagged union (built in, not as std/lib)
- interface / signature for structs (built in, not as std/lib)
    - alternatively, reintroduce the * for classes
- enum literals (being able to omit the enum name)
    ex:
        ```d
        enum MyEnum { A, B, C}
        MyEnum e = :A;
        my_function(:A);
        switch (e)
        {
        case :A: break;
        }
        // could be :, ., $, ! but that is not the point
        '''

This unscoped enums is one of the worst features in C/C++, imho. I loved when I switch to languages that the enum got its scopre but D you got both, you can also do:

enum A = 0;
enum B = 1;
// ...

if you dislike to set their initial values manually, I think you can write a template function to create enum values

It's not unscoped, it type checked, it just removes the need to be verbose and repetitive

MyAbility ability;

switch (ability)
{
 case MyAbility.SOMETHING_1: break;
 case MyAbility.SOMETHING_2: break;
 case MyAbility.SOMETHING_3: break;
 case MyAbility.SOMETHING_4: break;
 case MyAbility.SOMETHING_5: break;
}

What this repetition solves?

void use_ability(MyAbility ability){}


use_ability(MyAbility.SOMETHING_1);

Imagine if you had to prefix the type of everything

void use_my_int(int a) {}


use_my_int(int.4545846846);

Compiler can be smarter

Languages that allow removing the prefix for enums are popular it helps a lot with readability

November 15, 2021
On Mon, Nov 15, 2021 at 05:25:35PM +0000, russhy via Digitalmars-d wrote: [...]
> ```D
> MyAbility ability;
> 
> switch (ability)
> {
>  case MyAbility.SOMETHING_1: break;
>  case MyAbility.SOMETHING_2: break;
>  case MyAbility.SOMETHING_3: break;
>  case MyAbility.SOMETHING_4: break;
>  case MyAbility.SOMETHING_5: break;
> }
> ```
> 
> 
> What this repetition solves?
[...]

Why can't you just write:

	MyAbility ability;

	switch (ability) with(MyAbility)
	{
	 case SOMETHING_1: break;
	 case SOMETHING_2: break;
	 case SOMETHING_3: break;
	 case SOMETHING_4: break;
	 case SOMETHING_5: break;
	}

?  The `with` keyword was designed specifically for this purpose.


T

-- 
They pretend to pay us, and we pretend to work. -- Russian saying
November 15, 2021

On Tuesday, 12 October 2021 at 21:50:11 UTC, surlymoor wrote:

>

On Tuesday, 12 October 2021 at 21:38:48 UTC, Timon Gehr wrote:

>
  • .init

Is the issue default initialization or that the property is named init?

The fact that the default value of a type T is T.init and init isn't even a keyword leading to all the problems it has, when it could have been default(T) is really embarrassing.

November 15, 2021
On Sunday, 14 November 2021 at 23:05:03 UTC, Timon Gehr wrote:
...
>> nullable,
>
> Maybe:
>
> int*? x=null;
> writeln(*x); // error
>
> if(x) writeln(*x); // ok

Yes please.

>>> - type classes / some standard way to add UFCS methods to a type from another module that are then visible in an instantiated template from a third module
>
> import a: S;
> import b: rangeFun;
>
> auto ref front(ref S s){ ... }
> bool empty(ref S s){ ... }
> void popFront(){ ... }
>
> void main(){
>     S s;
>     rangeFun(s with(front, empty, popFront));
>     alias T=S with (front,empty,popFront);
>     T t=s;
>     rangeFun(t);
> }

Does the `s with...` expression have to keep the same source type? If not we could make a type constructor template `methodize!(S,.)` which wraps the source type but has methods which wrap all public functions in the given module that have a first parameter of the source type.

>>> - `let Statement in Expression` expression
>
> void main(){
>     auto z = {
>         int x=1;
>         int y=2;
>     } in x+y;
>     assert(z==3);
> }
>
>>> - `Expression where Statement` expression
>> ...
>
> void main(){
>     auto z = x+y where{
>         int x=1;
>         int y=2;
>     };
>     assert(z==3);
> }

Isn't this sufficient?:

auto z = {
    int x=1;
    int y=2;
    return x+y;
    }();

> See above. Unfortunately I only had time to give some contrived examples.
>
>>> - mixin identifiers
>> example?
>
> static foreach((name, value);[("x",1),("y",2),("z",3)]){
>     int mixin(name) = value;
> }
>
> writeln(x," ",y," ",z); // 1 2 3

Yes please.

>>> - template literals
>> example?
>
> enum sizes = staticMap!((B)!=>B.sizeof,Types);

I like 'alias template literal' syntax: `alias(B) => B.sizeof`

The only problem is currently `alias e = 5;` doesn't compile (at least on run.dlang.org). That is inconsistent as non-literal value expressions work.

>>> - consistent treatment of operators for built-in types, e.g. 1.opBinary!"+"(2) should work.

Seems we could add a few UFCS functions to object.d for that.

>>> - range literals
>> what's that?
>
> auto b = (fun(x) for x in iota(100) if bar(x));
>
> // equivalent to:
> // auto b = iota(100).filter!(x=>bar(x)).map!(x=>fun(x));

Maybe a template would make the current code a bit nicer:

auto b = iota(100).mapFilter!(x => bar(x) ? fun(x) : none);

>>> - strong variable updates (probably not happening)
>> what's that?
>
> int x=2;
> x="123";
> static assert(is(typeof(x)==string));

Seems bug prone unless it used special syntax to take a value of another type.

> auto f=File(x,"r");
> static assert(is(typeof(f)==File));
> f.close();
> static assert(is(typeof(f)==ClosedFile));

If we had affine typing, it would be simpler to just design `f.close` to invalidate any use of `f`, like your example calling `move` or `destroy`. (I suppose e.g. `f.closeForNow` would not invalidate `f` if needed).