November 29, 2021

On Monday, 29 November 2021 at 12:47:35 UTC, Nick Treleaven wrote:

>

On Monday, 29 November 2021 at 11:34:41 UTC, bauss wrote:

>

Pretty much anything can be solved in a library, except for things that requires AST/macros.

There is no way to implicitly convert from other types (particularly built-in types) to a struct, and I think Walter is opposed to introducing that. This means no null -> Slice or null -> AA and other implicit conversions. So any library solution would be more awkward to use compared to the current language equivalents.

Yeah, I'm not really supportive of everything having to be a library.

There are far more advantages of having some things built-in, rather than added as a library solution.

November 29, 2021

On Monday, 29 November 2021 at 13:11:12 UTC, user1234 wrote:

>

If there must be a special path then it's already a failed library solution, not to say a lie. You make think to the user that classes is a library thing but it's not.

No lies. Compiler optimizations that focus on standard library constructs are not uncommon and makes a lot of sense: these constructs occur frequently.

It is still a library solution. Change the library implementation, it will still work, but the optimizer may not recognize it.

Nothing unusual about this.

November 29, 2021

On Monday, 29 November 2021 at 14:16:56 UTC, bauss wrote:

>

There are far more advantages of having some things built-in, rather than added as a library solution.

What advantages?

Keep the language small and make it possible to implement a bug-free compiler.

November 29, 2021

On Monday, 29 November 2021 at 13:22:34 UTC, Nick Treleaven wrote:

>

On Monday, 29 November 2021 at 13:11:47 UTC, Alexandru Ermicioi wrote:

>

But isn't a slice basically a struct with pointer and length?
Therefore you can consider that a slice is never null, but rather internal ptr field is, while length is 0.
The assoc array could follow same logic, i.e. point to null node.

For slices we could just stop supporting null and require [] instead, but I expect it would break quite a lot of code. For AAs, null is part of its design as a reference type. I'm not sure how you would get around that elegantly.

Yes, that would make a lot of sense.

Or you could make the current behaviour more general and add support for "dependent typing" of null-pointers.

So, this would work like this: you can specify that an operator is "value dependent", meaning there are two or more implementations that will be selected based on the value of the argument.

Is this sufficient to cover the current implementation?

November 29, 2021

On Monday, 29 November 2021 at 14:57:04 UTC, Ola Fosheim Grøstad wrote:

>

Or you could make the current behaviour more general and add support for "dependent typing" of null-pointers.

Maybe overkill?

One step would be to make this optionally explicit in overloading functions such as opIndexAssign.

So you could specify opIndexAssign(ref … this, …) and modify this if it is a null pointer.

Anyway, seems like such issues could be solved with minor adjustments.

November 29, 2021

On Monday, 29 November 2021 at 13:56:08 UTC, Petar Kirov [ZombineDev] wrote:

>
  1. Type qualifiers. I really like D's type qualifiers, including shared, but after using TypeScript at work for the past 2-3 years, I feel their approach is more flexible - simply offer head-only type qualifiers and build transitive ones on

Yes, it is a powerful concept, but TypeScript is "reinterpreting" the underlying types. If done in D it might be the same as refining types with constraints.

A long time ago I suggested using reqular expressions for expressing constness, which perhaps is what TypeScript is doing. Just with a verbose syntax.

It could be done in a more concise fashion by borrowing ideas from query languages for tree-datastructures, such as XQuery or CSS selectors.

Using the more verbose syntax of TypeScript only makes sense if you also do your point 3:

>
  1. Replace interface types and ad hoc Design-by-Introspection patterns like isInputRange, hasMember!(S1, "member") with TypeScript's object types: https://www.typescriptlang.org/docs/handbook/2/objects.html which offer a very powerful, yet elegant and concise way of describing object shapes.

Concepts/traits in C++ and Rust are making the lack of this feature more apparent, I guess.

>
  1. Replace ad hoc DIP1000 parameter storage class design with affine types. I haven't given it much thought, but ideally we should be able to write an Affine type constructor in library code and simply use it like as any other type constructor (e.g. tuple, sum type, etc.)

By "affine types" in this context, do you mean "use once or never" types?

>
  1. Introduce some concept of compile-time dependency resolution / context abstraction (see https://docs.scala-lang.org/scala3/reference/contextual.html) so that things like the current allocator, scheduler, request/response context, etc., can be implicitly provided to improve the ergonomics of such APIs. For example, one could specify that in one scope the current implementation of associative arrays is a GC-managed hashmap, while in other a unique_ptr to red-black tree value container, like std::map, all while using the same K[V] x = [key1: value1] syntax.

So you would make all functions "templated"? Or, how else would this work?

November 29, 2021

On Monday, 29 November 2021 at 16:05:46 UTC, Ola Fosheim Grøstad wrote:

>

[snip]

>
  1. Replace interface types and ad hoc Design-by-Introspection patterns like isInputRange, hasMember!(S1, "member") with TypeScript's object types: https://www.typescriptlang.org/docs/handbook/2/objects.html which offer a very powerful, yet elegant and concise way of describing object shapes.

Concepts/traits in C++ and Rust are making the lack of this feature more apparent, I guess.
[snip]

Typescript's object types look to me like Go's interfaces (which are like Rust's traits), but with a bit more flexibility. They look more runtime-oriented than C++'s concepts.

November 29, 2021

On Monday, 29 November 2021 at 16:14:29 UTC, jmh530 wrote:

>

Typescript's object types look to me like Go's interfaces (which are like Rust's traits), but with a bit more flexibility. They look more runtime-oriented than C++'s concepts.

Hm. I think I understand what you mean, but TypeScripts type system does not exist at runtime at all, so it is static and for compile time only (or mostly). Runtime type checks are limited to JavaScript. Go is more dynamic in nature and therefore not fully static, so some typing errors will be discovered at runtime in Go.

TypeScripts types are however weak, in the sense that they are just painted over JavaScript objects that may have content that contradicts the types TypeScript paints over them. Kinda like how D can paint incompatible types over data-structures received from C code.

In that context maybe it would be interesting to have something similar, and allow programmers to «paint constraints» over both D and C data-structures to catch more bugs at compile time.

November 30, 2021

On Sunday, 28 November 2021 at 20:47:28 UTC, Ola Fosheim Grøstad wrote:

>

If there was a majority in favour of D3, with breaking changes, and a strong focus on meta-programming, then it would make a lot of sense to streamline the language.

I just don't understand the premise of a "D3".
We don't need "D3" to have breaking changes, we now have a better system with -preview/-revert.

Considering D2 approximately halved the userbase, talks about "D3" should really be talks about new -preview flags. The idea that a magical breaking change fixes the langage without a nice upgrade path is magical thinking, because it would be strictly worse than what we have today.

November 30, 2021

On Tuesday, 30 November 2021 at 13:49:12 UTC, Guillaume Piolat wrote:

>

On Sunday, 28 November 2021 at 20:47:28 UTC, Ola Fosheim Grøstad wrote:

>

If there was a majority in favour of D3, with breaking changes, and a strong focus on meta-programming, then it would make a lot of sense to streamline the language.

I just don't understand the premise of a "D3".
We don't need "D3" to have breaking changes, we now have a better system with -preview/-revert.

Considering D2 approximately halved the userbase, talks about "D3" should really be talks about new -preview flags. The idea that a magical breaking change fixes the langage without a nice upgrade path is magical thinking, because it would be strictly worse than what we have today.

There should absolutely be an upgrade path, which is why "equally expressive" would be very much a requirement. I believe to a large extent that one could do it this way:

For files ending in ".d" emulate most of the current behaviour. For files ending in ".d3" provide the full cleanup. Then you can combine ".d" and ".d3" files. Much like how you combine ".c", ".cpp", ".mm" files in X-Code for C, C++, Objective-C.

One possible way to arrive at this path would be to create a high level IR (HIR) for the current DMD compiler, then a new D3 compiler would emit the same high level IR and you would combine the HIR output using a high level linker (basically a compiler). You could do the same for C too.

The problem right now is that there is neither LTS or a compiler code base that is suitable for further evolution, so with no change on the technological level you effectively "halve the population" anyway by slow evolution. There are other languages quite close to D in semantics, so without "D3" there are other languages that take that role regardless.

It also makes absolutely no sense to write a new compiler codebase that emulate the quirks of dmd?

The better strategy is to have:

  1. DMD -> HIR
  2. D3 -> HIR
  3. HIR -> LLVM

That allows you to give DMD D2 long term support and also improve on compilation speed and backend support (ARC and borrowing) for both.