November 10, 2021
On Wednesday, 10 November 2021 at 17:05:20 UTC, H. S. Teoh wrote:
> On Wed, Nov 10, 2021 at 02:17:28PM +0000, Sebastiaan Koppe via Digitalmars-d wrote:
>> On Wednesday, 10 November 2021 at 14:15:58 UTC, Stefan Koch wrote:
>> > On Wednesday, 10 November 2021 at 07:09:34 UTC, Timon Gehr wrote:
>> > > Specifically, I would like it to influence scoping.
>> > > 
>> > > ```d
>> > > int x=2;
>> > > int y=move(x);
>> > > int z=x; // error: undefined identifier x (was moved away)
>> > > 
>> > > int y=move(y); // ok
>> > > ```
>> > 
>> > That would be a good start. I would support that.
>> 
>> Yeah, that would be lovely.
>
> That would allow a better implementation of ownership tracking.
>
>
> T

Other way around.
It requires better compiler ownership tracking.
November 10, 2021
On Wed, Nov 10, 2021 at 05:55:47PM +0000, Stefan Koch via Digitalmars-d wrote:
> On Wednesday, 10 November 2021 at 17:05:20 UTC, H. S. Teoh wrote:
> > On Wed, Nov 10, 2021 at 02:17:28PM +0000, Sebastiaan Koppe via Digitalmars-d wrote:
> > > On Wednesday, 10 November 2021 at 14:15:58 UTC, Stefan Koch wrote:
> > > > On Wednesday, 10 November 2021 at 07:09:34 UTC, Timon Gehr >
> > > wrote:
> > > > > Specifically, I would like it to influence scoping.
> > > > > > > ```d
> > > > > int x=2;
> > > > > int y=move(x);
> > > > > int z=x; // error: undefined identifier x (was moved away)
> > > > > > > int y=move(y); // ok
> > > > > ```
> > > > > That would be a good start. I would support that.
> > > 
> > > Yeah, that would be lovely.
> > 
> > That would allow a better implementation of ownership tracking.
[...]
> Other way around.
> It requires better compiler ownership tracking.

That's what I meant. :D  If the compiler has this tracking, then it becomes possible to implement a simple ownership-tracking pointer wrapper type in user code.


T

-- 
Stop staring at me like that! It's offens... no, you'll hurt your eyes!
November 11, 2021
On 11/5/21 6:02 PM, Atila Neves wrote:
> On Tuesday, 12 October 2021 at 21:38:48 UTC, Timon Gehr wrote:
>> On 10/11/21 5:59 PM, Atila Neves wrote:
>>> ...
>> ...

Sorry, just noticed I never answered this...

>> This is likely to be incomplete or overly abstract, but here's my approximate take:
>>
>>> * Worst features implemented in a non-toy language
>>
>> - Turing-complete constructors
> 
> Care to explain?
> ...

The goal of construction is to create an instance that usually should satisfy a certain invariant. (I don't necessarily mean invariant contracts, often invariants are left implicit.)

Therefore, to construct an instance, all that needs to happen is input validation and moving the constructor arguments into the fields of the instance.

Right now, constructors first get you some instance _that does not yet satisfy the invariant_ and basically allows you to do anything you want with it. That's quite a drag on type safety. (E.g., good luck adding non-null pointers.)

```d
struct S{
    immutable int x;
    this(int x){
        foo();
        this.x=1;
        foo();
    }
    void foo(){ writeln(x); }
}

```

There just should not be a way to obtain instances that violate (type system) invariants.

I think Walter agrees that the constructor design is not optimal, especially regarding exception safety.

>> - @safe `void` initialization
> 
> For what types? It doesn't compile for pointers, for instance, and I don't see why void initialising an int would be unsafe.
> ...

The backends may treat accessing it as UB. I'd expect both ldc and gdc to think accessing a void-initialized value is UB.

The worst thing is @safe void initialization for user-defined types.

>> - @safe extern(C) function prototypes
> 
> Instead of @trusted, you mean?
> ...

Yes, at least with @trusted you can find it with grep. Slapping @safe on something should never be dangerous.

>> - .init
> 
> Because?
> ...

It's Hoare's billion dollar mistake, extended to all types. Having a default instance is often antithetical to strong invariants. At least it can be hidden I guess.

>> - separation of templates and CTFE
> 
> What would it look like if they weren't separated?
> ...

Stefan's type functions come close. (But I'd like to have dependent types as well.) E.g., manipulating arrays of types in CTFE functions.

>> - interaction of type qualifiers with reference types
> 
> I'd love to know what you mean by this.
> 
> 

The most obvious one is that there is no way to declare a tail-mutable class reference.

There's also this:

```d
import std.stdio;
class C{
    static x = [1,2,3];
    this(){ x[0]=2; }
    this()immutable{}
}

void main(){
    auto a=new immutable(C)();
    writeln(a.x); // [1,2,3]
    auto b=new C();
    writeln(a.x); // [2,2,3]
}
```
November 11, 2021
On 09.11.21 18:13, Atila Neves wrote:
> On Tuesday, 9 November 2021 at 16:37:37 UTC, Timon Gehr wrote:
>> On 08.11.21 15:04, Atila Neves wrote:
>>> On Friday, 5 November 2021 at 21:22:12 UTC, victoroak wrote:
>>>> [...]
>>>
>>> Interesting. Adding an invariant causes compilation to fail:
>>>
>>> ```
>>> foo.d(27): Error: variable `foo.main.x` `void` initializers for structs with invariants are not allowed in safe functions
>>> ```
>>>
>>>
>>
>> Well, that makes some sense, but a struct can have an invariant without actually having it spelled out explicitly in the source code. Furthermore, adding contracts actually makes code less safe in -release mode.
> 
> IMHO -release mode really shouldn't be used, but since contracts are supposed to prevent bugs from occurring, hopefully testing "proved" that "none" exist.
> 
> In this case specifically, even with -release one gets a compiler error, which is enough of a reason to add one methinks.

"Add an empty invariant to your types for maximum type safety", "we have constructors, but your types have to cope with arbitrary bit patterns anyway" or "this thing is @safe but some compiler backends may treat it as UB anyway" are The Last Thing D Needs.
November 11, 2021

On Thursday, 11 November 2021 at 00:26:06 UTC, Timon Gehr wrote:

>

On 11/5/21 6:02 PM, Atila Neves wrote:

>

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

>
  • .init

Because?
...

It's Hoare's billion dollar mistake, extended to all types. Having a default instance is often antithetical to strong invariants. At least it can be hidden I guess.

I don't see how .init is related to null as long as you can disable default construction?

That said, I find .init annoying and want to see it adjusted (zero default across the board for fast array initialization + user provided default constructor).

November 14, 2021

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

November 14, 2021
On Monday, 11 October 2021 at 20:51:13 UTC, Adam D Ruppe wrote:
> On Monday, 11 October 2021 at 15:59:10 UTC, Atila Neves wrote:
>> [...]
>
> Well not a feature per se, but the idea that number of keywords or number of features really matters.
>
> People argue you should remove things just to remove things. But this often just moves the inherent difficulty of programming from the language to the code.
>
> You don't wanna go too far with it, of course, but there is simple metric here.
>
>> [...]
>
> The whole int promotion and casting mess. Two ideas that sound fine in isolation but make things just nearly unusable in real life when combined.
>
>> [...]
>
> Explicit implicit construction. Minimally on return values, but also on function arguments would be nice.
>
> Note the constructor called implicitly must be explicitly labeled implicit.

could you give an example on how those features would be like?
November 14, 2021

On Tuesday, 12 October 2021 at 18:59:29 UTC, Imperatorn wrote:

>

On Monday, 11 October 2021 at 15:59:10 UTC, Atila Neves wrote:

>

I'm brainstorming about what I'll talk about at DConf, and during a conversation with Walter I thought it might be cool to talk about:

  • Worst features implemented in a non-toy language
  • Worst features (in your opinion) in D
  • Features you'd like to see in D

Ideas? Examples?

Thanks!

foreach_reverse - worst "feature" mankind has produced

why is it that bad? lol

November 14, 2021
On Tuesday, 12 October 2021 at 21:38:48 UTC, Timon Gehr wrote:
> On 10/11/21 5:59 PM, Atila Neves wrote:
>> ...
>
> This is likely to be incomplete or overly abstract, but here's my approximate take:
>
>> * Worst features implemented in a non-toy language
>
> - unsound type system
> - undefined behavior for categories of errors other than memory corruption
> - inappropriately nondeterministic semantics
> - mutable aliasing by default (mutability is fine, aliasing is the issue)
> - pointer arithmetic without appropriate type system support
> - imprecise type system (>40 years behind state of the art)
> - non-orthogonal language definition
> - null pointers without appropriate type system support
> - Turing-complete constructors
>
>> * Worst features (in your opinion) in D
>
> - undefined behavior for categories of errors other than memory corruption, e.g. 'assert'
> - @safe `void` initialization
> - @safe extern(C) function prototypes
> - inout
> - nondeterministic floating-point semantics
> - forward reference errors/ambiguities (arising from underspecification of compile-time introspection)
> - .init
> - separation of templates and CTFE
> - null pointers
> - Turing-complete constructors
> - Object
> - interaction of type qualifiers with reference types
> - underspecification of type/function qualifiers
> - template syntax is inconsistent between declaration and call site
> - alias this (it uses lookup rules different from import)
> - slicing and indexing of sequences is magic, there is no way to slice a library tuple without decaying into an auto-expanding sequence
>
>> * Features you'd like to see in D
>
> - built-in tuples/products with standard syntax
> - built-in tagged unions/sums, nullable, algebraic data types
> - pattern matching
> - real support for linear/affine typing
> - named parameters with support for perfect forwarding and compile-time introspection
> - built-in duck-typed record types playing nice with named parameters
> - 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
> - uniform syntax for function definition, in particular `int add(int a,int b)=>a+b;` (-preview=shortenedMethods)
> - `let Statement in Expression` expression
> - `Expression where Statement` expression

could you give example what those would be like?

> - mixin identifiers
example?
> - __local, static break and continue
what would __local do?

> (https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1010.md)
> - template literals
example?
> - static opIndex/opSlice
> - AST introspection
> - consistent treatment of operators for built-in types, e.g. 1.opBinary!"+"(2) should work.
> - operator overloading should combine orthogonally with UFCS
> - better support for subtyping (expose the concept to user-defined types, e.g., Range!(const(T)) could be a subtype of const(Range!T)) and any other features required to support a good container library
> - async/await (or similar)
> - yield (or similar)
> - range literals
what's that?
> - more lightweight coroutines
> - ProtoObject
> - special treatment of qualified types, in particular qualified reference types (e.g. `immutable` classes should allow conversion of references from `immutable` to mutable)
> - non-deterministic semantics, with as little undefined behavior as possible, in particular for type/function qualifiers
> - compile-time fixed-point support (e.g., with struct S(T){ T* next; }, fix!S gives a singly linked list).
> - flow-sensitive typing
what's that?
> - strong variable updates (probably not happening)
what's that?
> - non-lexical variable lifetimes (probably not happening)
> - parametric polymorphism, in particular for type qualifiers (probably not happening)
> - some amount of dependent types (dreaming now)

you got very nice features, are you making DIPs for those?

November 15, 2021
On 14.11.21 22:54, Dr Machine Code wrote:
> On Tuesday, 12 October 2021 at 21:38:48 UTC, Timon Gehr wrote:
>> On 10/11/21 5:59 PM, Atila Neves wrote:
>>> ...
>> ...
>>
>>> * Features you'd like to see in D
>>
>> - built-in tuples/products with standard syntax

auto (a,b) = (1,2);

(int a, string b) = fetch!((x)=>(1,text(x)));

[(1,2),(3,4)].map!((a,b)=>a+b).each!writeln;

>> - built-in tagged unions/sums,

Not sure about detailed syntax, maybe something like:

(int+string) x = inj(0,1);
(int+string) y = inj(1,"hi");

> nullable,

Maybe:

int*? x=null;
writeln(*x); // error

if(x) writeln(*x); // ok

C? x = ...;
D? = x?.method();

> algebraic data types

Maybe:

enum Result(T){
    Ok(T),
    Error(string)
}

>> - pattern matching

auto x = match(r){
    Ok(0) => 3;
    Ok(x) => x+1;
    Error(_): assert(0);
}

int toInt((int+string) x)
   match(x){
       (0, x): return x;
       (1, s): return to!int(s);
   }
}

>> - real support for linear/affine typing

auto x = S();
auto y = move(x);
auto z = x; // error: undefined identifier x
auto y = move(y); // ok

// error: y was not explicitly moved away or destroyed

>> - named parameters with support for perfect forwarding and compile-time introspection

void foo(int x=0,int y=0,int z=0;

foo(x: 100, z: 200);

auto apply(alias f,T...)(T args){ return f(args); }

apply!foo(x: 100, z: 200);

>> - built-in duck-typed record types playing nice with named parameters

auto r = (x: 100, z: 200);
foo(r);

>> - 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);
}

>> - uniform syntax for function definition, in particular `int add(int a,int b)=>a+b;` (-preview=shortenedMethods)

int add(int a,int b)=>a+b;

>> - `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);
}

> could you give example what those would be like?
> ...

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

>> - __local, static break and continue
> what would __local do?
> ...

static foreach(i;0..100){
    __local alias X=Foo!(T[i]); // local to static foreach, does not clash between iterations
    mixin Bar!X;
}

>> (https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1010.md)

Also see the explanation in the DIP.

>> - template literals
> example?

enum sizes = staticMap!((B)!=>B.sizeof,Types);

>> - static opIndex/opSlice
>> - AST introspection
>> - consistent treatment of operators for built-in types, e.g. 1.opBinary!"+"(2) should work.
>> - operator overloading should combine orthogonally with UFCS
>> - better support for subtyping (expose the concept to user-defined types, e.g., Range!(const(T)) could be a subtype of const(Range!T)) and any other features required to support a good container library
>> - async/await (or similar)
>> - yield (or similar)
>> - 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));

>> - more lightweight coroutines
>> - ProtoObject
>> - special treatment of qualified types, in particular qualified reference types (e.g. `immutable` classes should allow conversion of references from `immutable` to mutable)
>> - non-deterministic semantics, with as little undefined behavior as possible, in particular for type/function qualifiers
>> - compile-time fixed-point support (e.g., with struct S(T){ T* next; }, fix!S gives a singly linked list).
>> - flow-sensitive typing
> what's that?

class A{ void foo(){ ... } }
class B:A{ void bar(){ ... } }

void foo(A a){
    if(a is B){
        B b=a; // ok
        a.bar(); // ok
    }
}

>> - strong variable updates (probably not happening)
> what's that?

int x=2;
x="123";
static assert(is(typeof(x)==string));

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

>> - non-lexical variable lifetimes (probably not happening)
>> - parametric polymorphism, in particular for type qualifiers (probably not happening)
>> - some amount of dependent types (dreaming now)
> 
> you got very nice features, are you making DIPs for those?
> 

I started this:
https://github.com/tgehr/DIPs/blob/tuple-syntax/DIPs/DIP1xxx-tg.md
https://github.com/tgehr/dmd/commits/tuple-syntax

Unfortunately, I was too busy at the time to finish the implementation, and I am not fully satisfied with the proposal. Probably I'd want to add at least static opIndex and static opSlice to it. Another issue is that it slightly expands `alias this` and I am not sure whether that's a direction Walter is willing to invest in at this point.