October 12, 2021

On Tuesday, 12 October 2021 at 17:48:04 UTC, Paul Backus wrote:

>

On Tuesday, 12 October 2021 at 17:25:52 UTC, max haughton wrote:

>

[...]

You can technically do this already with foreach and opApply, although the result ends up looking kind of goofy:

foreach (b; a)
foreach (c; b)
foreach (d; c)
e;

Desugars to:

a.opApply(b =>
    b.opApply(c =>
        c.opApply(d => e)));

...which is almost the same thing as Haskell's do notation, if you replace >>= with opApply and x <- y with foreach (x, y).

This similarity is perhaps why Scala uses the for keyword for its version of do notation.

I hate this and I hate you!

Nice trick though, wouldn't have thought to do that.

October 12, 2021

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

October 12, 2021
On Mon, Oct 11, 2021 at 03:59:10PM +0000, Atila Neves via Digitalmars-d 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

>From languages I know:

- C:
   - Implicit decay of array to pointer.
   - Null-terminated strings (corruption of your string data == buffer
     overflow exploit waiting to happen).
   - Pointer syntax: the `*` grammatically attaches to the identifier
     rather than the type name, which causes all sorts of totally
     needless syntactic convolutions like the unreadable function
     pointer syntax. (Yes, I know how it works, I've been writing them
     for 20+ years and I know them like the back of my hand; but it is
     nevertheless a Bad Idea(tm).)  Not to mention how easily beginners
     mess it up.

- C++:
   - Template syntax (using `<>` for template parameters/arguments has
     got to be the worst syntax choice made in the last 30 years, making
     C++ unlexible until it's first parsed).
   - Null-terminated strings (one of the worst things to inherit from
     C).
   - Free-for-all, wild-wild-west operator overloading that lets you
     define, e.g., <, <=, >, >= in completely inconsistent, incompatible
     ways. Not to mention the amount of boilerplate necessary to
     implement these operators in a consistent way, leading to more
     opportunities for human error.

- Perl:
   - Sigils: $x, @x, %x refer to different things. Worse yet, $x{y}
     dereferences %x, not $x, and $x[y] dereferences @x rather than $x.
     (Argh...)
     (I actually *like* the rationale behind sigils, but the way they're
     implemented is just ... argh...)

- JS:
   - Dynamic typing.  Leads to all kinds of bugs and runtime exceptions
     that eventually compel you to write checks, that ultimately boil
     down to reinventing static typing in an ad hoc way.  As someone
     once said, dynamic typing is just static typing with only a single
     type.
   - `===` vs `==`.
   - WAT-eliciting behavior of built-in operators: like `[] + []`
     producing an empty string, `[] + {}` producing `[object Object]`
     (which is a *string*, not an actual object!) and `{} + []`
     producing 0.


> * Worst features (in your opinion) in D

- The integer promotion/casting mess. I understand that C compatibility
  is very important, but things have seriously gone off the rails when
  you get things like:

	ubyte x;
	x = -x; // compile error

- Autodecoding.

- Half-baked features like `shared`. It kinda-sorta works, its behaviour
  is by design, casts are intentional, etc.. But it leaves you with the
  feeling of incompleteness.  It promises thread safety (or at least to
  help towards that goal), but when you use it you feel like you're
  given parts of some IKEA furniture without nails, screws, or bolts,
  which you have to provide on your own (and of course, assembly is also
  DIY, and if it falls apart it's your fault, not ours).

- In the same vein, @property.  It's been how many years now, when are
  we going to get a comprehensive solution (or, at the very least, when
  will this sad creature with missing limbs finally be euthanized for
  mercy's sake)?


> * Features you'd like to see in D
[...]

- C++ concepts-like feature of specifying the expected interface of a
  template parameter.
   - Yes, sig constraints fit the bill, kinda-sorta.  But it leads to
     poor, unhelpful error messages.  Yes, this can be fixed by using
     static if, pragma(msg), etc., but this requires a lot of extra work
     on the part of the user which should have been handled by the
     language.
   - Yes, there's a dub package that kinda-sorta does this. But again,
     this is something that should have been done by the language.

- More procedural type manipulations.  `static foreach` is great; it
  eliminated a bunch of cases where you'd need to use recursive
  templates. It'd be nice to have procedural manipulations of
  types/aliases/etc. as well.  Kinda like Stefan's type functions, but
  more integrated into the language and streamlined with other D
  features.  Basically, get rid of recursive templates except where
  they're absolutely, unavoidably, necessary.

- More complete outworking of features that require deep language
  support, like `shared` or @property or [insert your favorite
  half-baked D feature here].  Provide a comprehensive package that
  actually enables you to get stuff done without having to buy your own
  nails, your own hammer, and assemble things yourself, and that provide
  some solid guarantees that doesn't just leave you holding the pieces
  when things break.

- Extend `if (auto x = initializer())` to apply to complex conditions as
  well, e.g.:

  	if (someClause && (auto x = initializer()) && (auto y =
		initializer2())) {
		/* use x and y here */
	}


T

-- 
Turning your clock 15 minutes ahead won't cure lateness---you're just making time go faster!
October 12, 2021

On Tuesday, 12 October 2021 at 00:13:45 UTC, jfondren wrote:

>

On Monday, 11 October 2021 at 21:41:45 UTC, monkyyy wrote:

>
  • nontrivial namespace collisions when importing std

Are these still around? This compiles now:

unittest {
    import std;

    assert(!std.ascii.isAlpha('5'));
}

unittest {
    import std;
    import std.ascii : isAlpha;

    assert(isAlpha('X'));
    assert(std.uni.isAlpha('X'));
}

The one that annoyed me was stdio and file, write

October 12, 2021
On Tuesday, 12 October 2021 at 19:41:57 UTC, H. S. Teoh wrote:
> - C++ concepts-like feature of specifying the expected interface of a
>   template parameter.
>    - Yes, sig constraints fit the bill, kinda-sorta.  But it leads to
>      poor, unhelpful error messages.  Yes, this can be fixed by using
>      static if, pragma(msg), etc., but this requires a lot of extra work
>      on the part of the user which should have been handled by the
>      language.
>    - Yes, there's a dub package that kinda-sorta does this. But again,
>      this is something that should have been done by the language.

You can get about 90% of the way to better constraint errors with existing language features:

    struct Check
    {
        bool passed;
        string message;

        T opCast(T : bool)() { return passed; }
    }

    enum hasFoo(T) =
        Check(
        	__traits(hasMember, T, "foo"),
            "`" ~ T.stringof ~ "` must have a member named `foo`"
        );

    struct Pass { int foo; }
    struct Fail { int bar; }

    // Can check with static assert
    static assert(hasFoo!Pass);
    static assert(!hasFoo!Fail);

    // Works in constraints
    auto getFoo(T)(T input)
        if (hasFoo!T)
    {
        return input.foo;
    }

    static assert(__traits(compiles, Pass().getFoo));
    static assert(!__traits(compiles, Fail().getFoo));

    // Can check error message
    pragma(msg, hasFoo!Fail.message);

The only thing missing is a way to get the compiler to print out your custom message, instead of (or in addition to) the default "must satisfy `hasFoo!T`" message.

I plan to write a DIP for this as soon as I am done with @nodiscard.
October 12, 2021
On Tuesday, 12 October 2021 at 19:41:57 UTC, H. S. Teoh wrote:
> [snip]
>> * Features you'd like to see in D
> [...]
>
> - C++ concepts-like feature of specifying the expected interface of a
>   template parameter.
>    - Yes, sig constraints fit the bill, kinda-sorta.  But it leads to
>      poor, unhelpful error messages.  Yes, this can be fixed by using
>      static if, pragma(msg), etc., but this requires a lot of extra work
>      on the part of the user which should have been handled by the
>      language.
>    - Yes, there's a dub package that kinda-sorta does this. But again,
>      this is something that should have been done by the language.
>
> 

I've become more sympathetic to this. The main argument against DIP 1023 (resolution of template alias parameters in template functions) boiled down to "why not use signature constraints." Something like Concepts could be an alternative solution to the problems addressed in that DIP (though that may not be the only way to resolve them).

Runtime & static polymorphism comes in different types and flavors. D has class, which is similar to how C++ and Java work (ignoring some details). So we can group these together in a run-time polymorphism. D's interface is similar to that, but doesn't do exactly the same thing and is different from Go's interface and Rust's traits system. Concepts as a form of static polymorphism are in a different category as well. But how do we group the categories in a way that makes sense?
October 12, 2021
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
- mixin identifiers
- __local, static break and continue (https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1010.md)
- template literals
- 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
- 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
- strong variable updates (probably not happening)
- non-lexical variable lifetimes (probably not happening)
- parametric polymorphism, in particular for type qualifiers (probably not happening)
- some amount of dependent types (dreaming now)
October 12, 2021
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`?
October 13, 2021
On 10/12/21 11:50 PM, 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`?

Uninitialized variables are not an option, so default initialization and/or some flow analysis unfortunately seem to be necessary given the limited nature of D's lexical scoping rules. The issue is that every user-defined type is by default assumed to have a default state. It's null pointers with extra steps. I'm not a big fan of the possibility of accidentally hiding a type's `init` using a user-defined field, but this is a lesser issue and at least this way it can be @disabled, though I am not absolutely sure that this completely denies access.
October 13, 2021
On 10/12/21 11:38 PM, Timon Gehr wrote:
> 
>> * Worst features (in your opinion) in D 

- local imports that implicitly hide symbols from outer scopes in current module