November 29, 2022
On Wed, Nov 30, 2022 at 02:23:20AM +0000, zjh via Digitalmars-d wrote:
> On Wednesday, 30 November 2022 at 01:09:03 UTC, H. S. Teoh wrote:
> > This includes containers that behave like built-in arrays, ARC'd types that behave like pointers, etc..  Solve this at the core, and it will enable progress on many other fronts.
> 
> How can we improve the `core`?
[...]

By extending the language in such a way that a user type can accomplish what currently can only be done by a built-in type.

To randomly pick a recent example: const(T)[] can be implicitly cast to
const(T[]), but UserContainer!(const(T)) cannot be implicitly cast to
const(UserContainer!T).  There are various ways of dealing with this:

One way is to ignore it ("the language does not support this"), which is the current status quo -- the result is that library container types will always be 2nd class, and they can never fully emulate the behaviour of built-in arrays.

Another way is to introduce a new built-in type that emulates some or
all of the functionality of UserContainer. It solves this particular
problem (just replace UserContainer with the new built-in type, or some
adaptation of the new built-in type), but the fundamental problem
(Template!(const T) is incompatible with const(Template!T)) remains
unsolved.  Next time, somebody comes along and complains that
AnotherContainer!(const T) is not compatible with
const(AnotherContainer!T), so we're back to square 1 again.

One way to actually solve this problem is to let user types declare the equivalence between their const forms.  From a discussion a few years ago I had here on the forum, we could introduce an opImplicitCast method/template that the compiler looks up whenever user code has an instance of Container!(const T) but needs an instance of const(Container!T).  Then there would be some way for this method/template to cast the container or create a proxy or something, to produce the needed instance of const(Container!T).  This makes the language extensible: it doesn't need to invent special rules for converting between various forms of const in relation to Container, the definition of Container itself provides the instructions of what to do to achieve this conversion (or prohibit it, as the case may be).  The language remains agnostic of how const should interact with user types, but provides the library author the tools to tell it what to do for each particular case. (BTW I'm not pushing for this particular solution, just using it as an illustration of what I'm talking about.)

IOW, empower the user instead of trying to make the language omniscient about every last imaginable use case.


T

-- 
It is not the employer who pays the wages. Employers only handle the money. It is the customer who pays the wages. -- Henry Ford
November 29, 2022
https://issues.dlang.org/show_bug.cgi?id=6019
https://issues.dlang.org/show_bug.cgi?id=22367
https://issues.dlang.org/show_bug.cgi?id=9816
https://issues.dlang.org/show_bug.cgi?id=4071

:-)
November 29, 2022
On 11/29/2022 3:56 PM, Vladimir Panteleev wrote:
> On Monday, 28 November 2022 at 09:27:11 UTC, Walter Bright wrote:
>> I've written most of a DIP for one. Should I:
> 
> I really wish that instead of adding more language features, we would improve the language so that things like this are implementable *in* the language.

There is std.sumtype

https://dlang.org/phobos/std_sumtype.html


> I'm not sure about the argument that not everyone might use the same type if it's implemented as a custom type. Even if one were to argue that not everyone uses Phobos, the implementation can go into Druntime, which is an extension of the compiler, which solves this problem.

In general you're correct. But sometimes, it's a big win to put things into the language. Two examples: ddoc and unittest. The conventional wisdom at the time was these certainly should not be part of the core language.


> The example in the DIP about null pointers is wrong. It can still be useful to distinguish between non-null pointers, null pointers, and some third state such as "no pointer". `Nullable!(Nullable!(Nullable!int)) foo` is perfectly valid.

True, but the draft design doesn't prevent doing that.


> We don't even need a standardized `NonNull` type or trait or whatever. Leverage existing D features instead:
> 
> ```d
> template nullIsValid(T)
> {
>      static if (is(T == struct))
>          enum nullIsValid = {
>              try
>              {
>                  union U
>                  {
>                      ubyte[T.sizeof] bytes = 0;
>                      T t;
>                  }
>                  U u;
>                  assert(u.t); // call invariant
>                  return true; // no exception was thrown - null is a valid state
>              }
>              catch (Throwable e)
>                  return false; // exception was thrown - null is an INVALID state
>          }();
>      else
>          enum nullIsValid = true;
> }
> ```
> 
> or something like that. Currently it doesn't work because you can't call struct invariants directly, and can't catch `assert(false)` in CTFE. But, these are fixable and examples of things that would improve metaprogramming at a fundamental, building-block level and enable building more cool high-level types and data structures, not just sum types.

True, but I wouldn't have a null pointer violation be anything but a fatal error, not a catchable exception.

November 30, 2022

On Wednesday, 30 November 2022 at 02:49:44 UTC, H. S. Teoh wrote:

>

On Wed, Nov 30, 2022 at 02:23:20AM +0000, zjh via Digitalmars-d wrote:

>

[...]
[...]

By extending the language in such a way that a user type can accomplish what currently can only be done by a built-in type.

[...]

In the end, it will come down to allowing the programmer to manipulate the AST directly in order to perform all the operations that the compiler can perform... And we know AST macros ain't happening

😢

November 30, 2022

On Wednesday, 30 November 2022 at 03:45:13 UTC, Tejas wrote:

>

On Wednesday, 30 November 2022 at 02:49:44 UTC, H. S. Teoh wrote:

>

On Wed, Nov 30, 2022 at 02:23:20AM +0000, zjh via Digitalmars-d wrote:

>

[...]
[...]

By extending the language in such a way that a user type can accomplish what currently can only be done by a built-in type.

[...]

In the end, it will come down to allowing the programmer to manipulate the AST directly in order to perform all the operations that the compiler can perform... And we know AST macros ain't happening

😢

Lack of AST macros means we can't implement custom syntax in library code (except via string mixins). It does not, in principle, place any hard limit on the semantics we can implement.

Fully-featured pattern matching requires new syntax, so it will have to be a built-in language feature, but sum types themselves are mostly about semantics, not syntax. With a handful of general-purpose language enhancements, we could in principle have a library sumtype that works just as well as a built-in one from a semantic perspective--and we could use the same enhancements to improve other library-defined types, like containers, ranges, etc.

November 30, 2022

On Wednesday, 30 November 2022 at 03:55:18 UTC, Paul Backus wrote:

>

On Wednesday, 30 November 2022 at 03:45:13 UTC, Tejas wrote:

>

On Wednesday, 30 November 2022 at 02:49:44 UTC, H. S. Teoh wrote:

>

[...]

In the end, it will come down to allowing the programmer to manipulate the AST directly in order to perform all the operations that the compiler can perform... And we know AST macros ain't happening

😢

Lack of AST macros means we can't implement custom syntax in library code (except via string mixins). It does not, in principle, place any hard limit on the semantics we can implement.

Fully-featured pattern matching requires new syntax, so it will have to be a built-in language feature, but sum types themselves are mostly about semantics, not syntax. With a handful of general-purpose language enhancements, we could in principle have a library sumtype that works just as well as a built-in one from a semantic perspective--and we could use the same enhancements to improve other library-defined types, like containers, ranges, etc.

Oh? What are these enhancements?

  • One is Deadalnix's "allow const type!int to convert to type!(const int)"

  • Another I know of is many folks asking for some kinda hook in the template type inference process

November 30, 2022

On Wednesday, 30 November 2022 at 02:49:44 UTC, H. S. Teoh wrote:

>

...

I think, It should be possible to improve the core language in the way of vim-like plug-in.
There should also be a list of important core related to do items.

Implicit cast is different from implicit cast const.

November 30, 2022
On 11/28/2022 6:46 PM, rikki cattermole wrote:
> Seriously, 2-3 days an actual dmd compiler dev could probably get ModuleInfo exportation on Windows working correctly and we could get shared library support in D starting to actually be decent.

I looked at https://issues.dlang.org/show_bug.cgi?id=22367 in your list, and the solution appears to be straightforward. I outlined it in the issue. Anyone want to have a go at it?

November 30, 2022
On Wednesday, 30 November 2022 at 01:09:03 UTC, H. S. Teoh wrote:
> On Wed, Nov 30, 2022 at 12:42:38AM +0000, deadalnix via Digitalmars-d wrote:
>> On Wednesday, 30 November 2022 at 00:10:47 UTC, H. S. Teoh wrote:
> [...]
>> > +1, I think this is a better approach.  If a library type can't do what a built-in type does, then extend the language until it can. As Andrei said in TDPL: "experience has shown time and again that offering many magic types that are unattainable to user code is a frustrating proposition and a sign of poor language design" [TDPL, 2010 ed, p.239].
> [...]
>> Damn, this Andrei guy, he knew one thing or two...
>
> And to elaborate a little, for those people who groan at "yet another library type": a common objection is that a library type has poorer error messages.  Well, it's just the same thing in different clothes: if a library type can't have good error messages, why is that?  Because the language cannot express certain things, or can't do it well, so the library author's hands are tied.  Giving up and pushing the type into the language is merely avoiding the problem (now the compiler writer has to solve the same problems, only in the compiler -- the amount of effort required is equivalent).  What actually addresses the problem is: fix the language so that the library author *can* provide good error messages.
>
> Because this isn't just about adding sumtype or whatever other magic type, this is about making it possible for *any* user type to provide the same level of functionality as built-in types.  It's about empowering the library writer so that he can replicate the power of a built-in type, integrated so well into the language that it feels like native type.  This includes containers that behave like built-in arrays, ARC'd types that behave like pointers, etc..  Solve this at the core, and it will enable progress on many other fronts.  Fail at this, and you're doomed to putting out individual fires one by one, ala whack-a-mole.  Today it's sumtypes, tomorrow it's ARC types, the next day it's head-immutable, it will never end.
>
>
> T

We already have C++ for that, beware of having wishes coming true.
November 30, 2022

On Wednesday, 30 November 2022 at 05:29:03 UTC, Tejas wrote:

>

On Wednesday, 30 November 2022 at 03:55:18 UTC, Paul Backus wrote:

>

Fully-featured pattern matching requires new syntax, so it will have to be a built-in language feature, but sum types themselves are mostly about semantics, not syntax. With a handful of general-purpose language enhancements, we could in principle have a library sumtype that works just as well as a built-in one from a semantic perspective--and we could use the same enhancements to improve other library-defined types, like containers, ranges, etc.

Oh? What are these enhancements?

  • One is Deadalnix's "allow const type!int to convert to type!(const int)"

  • Another I know of is many folks asking for some kinda hook in the template type inference process

Stuff like

  • User-defined implicit conversions.
  • Returning error messages from failed is(...) and __traits(compiles) checks instead of just false.
  • Allowing library code to display its own custom error messages, with the same control and flexibility the compiler has for built-in messages (currently we only have static assert).
  • User-defined GC scanning for library types (opGCScan?).
  • User-defined value ranges for aggregate fields (e.g., pointer that can't be null, integer that can't go above/below a certain limit).

All of these are things that built-in types have access to already, and all of these would have useful applications beyond just sum types.