February 10, 2018
On Friday, 9 February 2018 at 18:31:18 UTC, H. S. Teoh wrote:
> 
> TBH, I'm not a fan of inout. Not because of how most people feel, that we shouldn't have it; IMO it doesn't go *far enough*.  For example, there's currently no way to express conveying the constness of a delegate argument's parameter to the return value, which would have been useful in some places in generic code.
>

So it's in your list of wanted stuff, not in your list of excess stuff. We're in agreement here.

>> What I would like to remove, is auto-decoding (popular opinion, I know)
>
> I would totally back up killing auto-decoding. With fire. And extreme prejudice. :-P
>
> barring some kind of workable (probably very long) deprecation cycle, I just don't see it going away anytime in the foreseeable future.

If we had something similar to c++ template lookup, it would, as I see it, finally solve that, along with many other problems. But bring some others, it's said... Better wrapper writing way than alias this would also solve it mostly and without the ADL problems, but there would still remain a problem with string and char literals.

>> __traits, is expression, typeof and std.meta templates should be invokable in a more UFCS-like manner
>
> As for is-expressions, I think either Walter or Andrei (possibly both) have acknowledged that the syntax is a mess.  But too much code already depends on the current syntax, and changing that now will be far too disruptive.

But UFCS style also allows the traditional way, there would be no breakage. So I quess that even here the existing thing is there for a reason after all.



After reading Manus list, I think I agree with his point 1.


February 10, 2018
On 10.02.2018 03:12, Nick Sabalausky wrote:
> On Saturday, 10 February 2018 at 01:24:55 UTC, Timon Gehr wrote:
>> The fundamental issue is that D's type system has no parametric polymorphism,
> 
> Pardon my ignorance, but isn't that what D's templated functions do? This sounds interesting but unclear exactly what you mean here and how it relates to inout and its problems.

TL;DR: Parametrically polymorphic functions have /runtime/ type parameters. inout can be interpreted as a dependent function of type "{type y | y.among(x, const(x), immutable(x)) } delegate(type x)" and an inout function can be thought of as a function that takes inout as an argument and produces the function as the return value. This formulation is more powerful than what the inout syntax can capture, and this is what causes problems with type safety. In particular, 'inout' does not support proper lexical scoping.

TS;NM: https://gist.github.com/tgehr/769ac267d76b74109a195334ddae01c3 (Some version of this was originally intended to go to the D blog, but I wanted to wait until inout has an obviously type safe definition. It also highlights other inout issues than just type unsafety and shows how all of them might be fixed in principle by adding polymorphism.)

---

I'll first explain parametric polymorphism, and then what the inout problem is.

The template

int foo(int x)(int y){
    return x + y;
}

relates to the function

int delegate(int) foo(int x){
    return (int y) => x + y;
}

just like the template

T foo(T)(T x){
    return x;
}

relates to the polymorphic function (fictional syntax):

T delegate(T) foo(type T){ // type is the type of types
    return (T x) => x;
}

Here, 'foo' takes a type T and produces an identity function for type T:

int delegate(int) idInt = foo(int);
int x = foo(int)(y);
writeln(foo(int)(2)); // 2

string delegate(string) idString = foo(string);
string y = foo(string)("b");
writeln(foo(string)("b")); // b


Alternatively, one might write

void id[T](T x){
    return x;
}

and then rely on implicit instantiation of the 'T' parameter:

int delegate(int) idInt = id![int]; // or something like this
int x = id(2);
writeln(id(2)); // 2

string delegate(string) idString = id![string];
string y = id("b");
writeln(id("2"));

As you will have noticed, all of this works just fine with templates, so what is the big difference?

For a polymorphic function, the type is a /runtime/ parameter. Some languages however enforce that polymorphic functions don't depend on the type T at runtime: just don't give any runtime methods or fields to the 'type' type).

The most obvious benefit of parametric polymorphism is that parametrically polymorphic functions exist at runtime (while templates only exist at compile time). For example, one can define a parametrically polymorphic delegate:

T delegate[T](T x) = [T](T x) => x;

Or, a parametrically polymorphic virtual function

class Base{
    bool pickFirst;
    abstract T pickOne[T](T a, T b);
}

class Honest: Base{
    bool pickFirst;
    override T pickOne[T](T a, T b){
        return pickFirst ? a : b;
    }
}

class Dishonest: Base{
    override T pickOne[T](T a, T b){
        return pickFirst ? b : a;
    }
}

class Confused: Base{
    override T pickOne[T](T a, T b){
        return uniform(0,2) ? a : b;
    }
}

So as expected, the difference is that for parametrically polymorphic functions, the type T /does not need to be known at compile time/.


Now, what is 'inout'? If it was a first-class entity, it might have the type:

alias Inout = { type y | y.among(x, const(x), immutable(x)) } delegate(type x);

I.e. it takes a type x and produces a type y such that y is either x, const(x) or immutable(x).

(This is not necessarily the most restricted possible type, depending on the details of the polymorphic type system. inout additionally enforces that the qualifier applied is the same for all argument types x.)

Consider the following 'inout' function:

inout(int*) id(inout(int*) x){
    return x;
}

This might be expressed as:

inout(int*) id[Inout inout](inout(int*) x){
    return x;
}

I.e., we can make 'inout' an explicit polymorphic parameter.


What is the problem with 'inout'? Let's look at the first counterexample to type safety:

@safe:
int a;
immutable(int) b=2;

inout(int)* delegate(inout(int)*) dg;
inout(int)* prepare(inout(int)* x){
    dg = y=>x;
    return x;
}
void main(){
    prepare(&b);
    int* y=dg(&a);
    assert(&b is y); // passes. ouch.
    *y=3;
    assert(b is *&b); // fails!
}

We will express this using explicit polymorphism and see where the type error occurs. (For readability, I have named the two versions of 'inout' differently, but this is not strictly necessary. The compiler knows that they are different, because they are associated to different declarations in the AST.)

inout1(int)* delegate[Inout inout1](inout1(int)*) dg;

inout2(int)* prepare[Inout inout2](inout2(int)* x){
    dg = [Inout inout1](inout1(int)* y)=>x;
    return x;
}

Here, the error would be:

Error: cannot implicitly convert '[Inout inout1](inout1(int)* y)=>x;' of type 'inout2(int)* delegate[Inout inout1](inout1(int)* y)' to 'inout1(int)* delegate[Inout inout1](inout1(int)*)'.

Note how the type error crucially depends on the fact that the type of the delegate contains _two incompatible functions_ of type Inout.

You can't have that if the only function of type Inout is the built-in inout!


Now let's look at the second counterexample to type safety:

@safe:
int a;
immutable(int) b=2;

inout(int)* delegate(inout(int)*)@safe delegate()@safe foo(inout(int)* y){
    inout(int)* bar(inout(int)* p){
        return y;
    }
    return ()=>&bar;
}
void main(){
    int* y=foo(&b)()(&a);
    *y=3;
    assert(&b is y); // passes. ouch.
    assert(b is *&b); // fails!
}

The problem is very similar. Can you spot it?

In summary, the issue is that there is only one 'inout' and therefore it is not properly lexically scoped. It is a bit like having a language where all variables are implicit function parameters and they all have the same, global, name. This sort of works fine until you want a function with two parameters or until you want to nest functions in a non-trivial way.

Also see: https://gist.github.com/tgehr/769ac267d76b74109a195334ddae01c3


February 10, 2018
On Fri, 09 Feb 2018 22:36:19 +0000, Ralph Doncaster wrote:

> Frankly, I think it is doomed to be a niche-use language.  While many more things were done right compared to C++, too many things were done wrong and there doesn't seem to be interest in breaking backward compatibility to excise them from D.

Yes.

If the current "let's get C++ programmers to like us" stuff continues, some of these problems will have to be fixed. I'm expecting C++20 to be a nice language for new projects, where you can ignore a lot of the blech (though not quite enough of it), and moving to C++20 will be easier than moving to D. The ROI just won't be there for most people (for many, it doesn't seem to be there today...).

I'm not sure that being a niche language is a bad thing; if we just say, "this is D; if you like it come and use it, come help make it better, but if it doesn't help you -- that's OK. There are other languages too" -- we may have more freedom to explore what D can do best. I've done compile- time stuff in D I would never have even considered attempting with C++ - and I haven't done nearly as much as others here. I don't think we've really explored the fullness of the language yet, and I wonder if that's only going to be possible if we quit comparing ourselves so much to C++.

My code still looks a lot like C and Python; it doesn't look like D yet. If we keep setting our sights on C++/Rust/whatever, we're going to artificially limit ourselves to their limitations, and that would be sad.
February 10, 2018
On Saturday, 10 February 2018 at 12:35:39 UTC, Timon Gehr wrote:
> So as expected, the difference is that for parametrically polymorphic functions, the type T /does not need to be known at compile time/.

According to this definition C++ doesn't support parametric polymorphism either, does it? Are there any C-style languages that allow that?

February 10, 2018
On Friday, 9 February 2018 at 22:36:19 UTC, Ralph Doncaster wrote:

> While many more things were done right compared to C++, too many things were done wrong and there doesn't seem to be interest in breaking backward compatibility to excise them from D.

I agree. Some users might shout because they recompiled a piece of code for the first time in eight years and got an error. The users that don't shout are the ones that try out the language, see rough edges that should have been removed, and move on.

Enterprise users that want guaranteed language stability should pay to support a compiler for that version of the language. Archive a version of the compiler every two years and paying companies can be guaranteed to use that compiler forever. There's no reason to shut down the evolution of the language to satisfy one (currently very small) group of users.
February 10, 2018
On Friday, 9 February 2018 at 23:01:44 UTC, Ola Fosheim Grøstad wrote:
> On Friday, 9 February 2018 at 20:49:24 UTC, Meta wrote:
>> was a complicated language, 99 of them would say no. If you ask 100 Python programmers, 99 would probably say yes.
>
> Yes, but objectively speaking I'd say modern Python is more complicated than C++ and D.
>
> What Python got right is that you don't have to deal with the complicated stuff unless you are hellbent on dealing with it. Python affords a very smooth incremental learning curve, but it is still a long learning curve...

^ This ...

I am seeing some responses that a language that is not loaded with all the options is not really real language. Yet, those languages are used for productivity all over the world.

Go is horrible limited and yet loved by many. And many really push the boundaries very far. But why do Python, PHP, Ruby, Go and others rank so high. Its because they are designed with a easy learning curve and documentation to match this curve.

D has the issue its designed to trow people into the deep end of the pool. Reading the responses on the basic nitpicking issues with D ( that i posted here ), you can tell that people "do not get it". Its small issues but a lot of small issues simply increase complexity. One mole in a garden is not a issue and can be overlooked. A hundred and people prefer the garden next door.

D will never be a language that draws in a lot of people from scripting languages like Python, PHP, Ruby simply because its clearly not designed with this mindset. It also does not help how much strange code decisions have been made in the past, that result in awkward library issues.

The problem is most languages allow you to program 80 to 90% of the task with eases.

D is focused on providing those extra 10 a 20% but in doing so the language has gotten complex, the library filled with years of scruff and because it focused on that extra 20%, it only draws in a selective crowd that keeps pushing more and more into that boundary. And that same crowd is not focused on leaving C++ any time soon, as C++ keeps evolving and improving. Anybody really focused on going into this 20% market, will look at the players, the tools and simply say: "Why D? Why not C++ 14/17/20".

This very much compact the issue that D has in attracting new users.
February 10, 2018
On 10.02.2018 14:05, Mark wrote:
> On Saturday, 10 February 2018 at 12:35:39 UTC, Timon Gehr wrote:
>> So as expected, the difference is that for parametrically polymorphic functions, the type T /does not need to be known at compile time/.
> 
> According to this definition C++ doesn't support parametric polymorphism either, does it?

It does not. C++ templates are a kind of restricted hygienic macro system, similar to D templates. It is however common for programmers to apply PL-theoretical terms in a somewhat sloppy way, e.g. here: https://rosettacode.org/wiki/Parametric_polymorphism

(Fun fact: it is actually only called "polymorphism". "Parametric" is added to distinguish the term from its usage related to virtual method calls in object-oriented programming languages.)

To be fair, templates quite successfully simulate parametric polymorphism for a large subset of its use cases and the compile-time code generation aspect can be very useful too.

> Are there any C-style languages that allow that?
> 

C#. Also, to some extent, Java.
February 10, 2018
On Friday, 9 February 2018 at 07:54:49 UTC, Suliman wrote:
> Which language futures by your opinion make D harder?

For me, one of the attractive qualities of D is its relative simplicity. Key comparison points are C++, Scala, and Python. Python being the simplest, then D, not far off, with Scala and C++ being more complex. Entirely subjective, not measured in any empirical way.

That said, a couple of D constructs that I personally find increases friction:

* Static arrays aren't not ranges. I continually forget to slice them when I want to use them as ranges. The compiler errors are often complex template instantiation failure messages.

* Template instantiation failures - It takes longer than I'd like to figure out why a template failed to instantiate. This is especially true when there are multiple overloads, each with multiple template constraints.

* Auto-decoding - Mentioned by multiple people. It's mainly an issue after you've decided you need to avoid it. Figuring out how out to utilize Phobos routines without having them engage auto-decoding on your behalf is challenging.

--Jon
February 10, 2018
On Saturday, 10 February 2018 at 12:44:14 UTC, rjframe wrote:
> On Fri, 09 Feb 2018 22:36:19 +0000, Ralph Doncaster wrote:
>
>> Frankly, I think it is doomed to be a niche-use language.  While many more things were done right compared to C++, too many things were done wrong and there doesn't seem to be interest in breaking backward compatibility to excise them from D.
>
> Yes.
>
> If the current "let's get C++ programmers to like us" stuff continues, some of these problems will have to be fixed. I'm expecting C++20 to be a nice language for new projects, where you can ignore a lot of the blech (though not quite enough of it), and moving to C++20 will be easier than moving to D. The ROI just won't be there for most people (for many, it doesn't seem to be there today...).

I also agree. One of the prime benefits of a language with a smaller community is that you can make it _better_ *faster*, breaking backcompat more easily and more often, and getting a better language in the process. After all, if its users wanted something slow to evolve, they'd be using one of the industry-accepted behemoths.

> I'm not sure that being a niche language is a bad thing; if we just say, "this is D; if you like it come and use it, come help make it better, but if it doesn't help you -- that's OK. There are other languages too" -- we may have more freedom to explore what D can do best. I've done compile- time stuff in D I would never have even considered attempting with C++ - and I haven't done nearly as much as others here. I don't think we've really explored the fullness of the language yet, and I wonder if that's only going to be possible if we quit comparing ourselves so much to C++.

I don't think D is designed to be a niche language. It's a general purpose language, open source with multiple implementations, solid engineering, and even has good-looking syntax. It's not niche at all, it just doesn't have hoards of users. D is well-positioned to be hugely popular, but I think to succeed its leadership needs to be willing to fix things they want to fix and not worry about breaking backcompat.

February 10, 2018
On Saturday, 10 February 2018 at 20:55:00 UTC, John Gabriele wrote:
>
> {snip} It's not niche at all, it just doesn't have hoards of users. D is well-positioned to be hugely popular, but I think to succeed its leadership needs to be willing to fix things they want to fix and not worry about breaking backcompat.

Augh! "hordes", not "hoards".