May 04, 2018
On Fri, May 04, 2018 at 12:12:09AM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:
[...]
> The problem with structural typing is that it's unable to distinguish between intended matches and accidental, unintended matches. This is because it doesn't REQUIRE types/functions/etc to clearly state, "Yes, I *intend* to be implementing interface XYZ". Or perhaps more accurately: "Yes, I *intend* for this to satisfy isXYZ!T". (Where "isXYZ" might be something like "isInputRange" or "isInfiniteRange", or both, etc.)
> 
> Note the all-caps "REQUIRE" in the paragraph above. That is to say, it's not enough for the developer of struct Foo to toss in...
> 
> static assert(isXYZ!Foo);
> 
> ...because the problem doesn't lie with the "intended matches". The problem lies with the "unintended matches". And that static assert does nothing to help isXYZ figure out that some other type, Bar, from some other package, is NOT deliberately designed to satisfy isXYZ even if it just happens to *look* like it satisfies it purely by chance.

This is a good point.  However, I'm failing to see it as a big problem, because for it to be a problem in the first place, you have to have *deliberately* passed an object of said type into a function that expects that particular concept.  Using your previous example, if somebody declared a JackInTheBox type that "accidentally" has .front, .popFront, .empty but not actually intending to be an input range, this is not a problem until you *explicitly* pass a JackInTheBox to something that expects an input range.  Just because JackInTheBox happens to accidentally implement the input range API without intending to have input range semantics, doesn't by itself cause any problem.  As long as you don't try passing it off as an input range, there's nothing to worry about.

And even when this explicit passing is done, what's the worst that could happen?  The JackInTheBox wouldn't actually implement input range functionality and would cause a bug.  So you'd just debug it like you would debug any other problem.

I'm not saying it's ideal, but it doesn't seem to be the huge big problem that people are making it out to be.


[...]
> So back you your question: How else would you do DoI?
> 
> Answer: By making isXYZ!T reject all T which DO NOT satisfy a deliberate, cannot-be-an-accident, condition of isXYZ's choosing.
> 
> Thus, type T *cannot* satisfy isXYZ without T's developer saying "Yes, I hereby certify it is my deliberate intention that T satisfies isXYZ and that, if it does satisfy, it is by my own intention and not by coincidental happenstance."
> 
> The exact details of this condition aren't terribly important, but I like Neia's suggestion of utilizing UDAs for this purpose. An old idea I had before UDAs existed was to require a dummy member enum named something like _satisfies_module_foo_bar_isXYZ, which of course would be abstracted away by something more convenient...a mixin or such (but nobody seemed remotely interested). But I like the UDA idea better.

Yeah, the UDA idea is a pretty good one.  I might even adopt it in my code. :-)


T

-- 
Fact is stranger than fiction.
May 05, 2018
On 05/04/2018 12:54 PM, H. S. Teoh wrote:
> 
> This is a good point.  However, I'm failing to see it as a big problem,
> because for it to be a problem in the first place, you have to have
> *deliberately* passed an object of said type into a function that
> expects that particular concept.

First of all, just to be clear in case I wasn't, I didn't mean that it's equally dangerous as globals, just that it's analogous. Globals are, of course, more dangerous.

Regarding the need to deliberately pass an object of said type to a function in order to cause a problem, that's not always true: It could be a matter of the compiler choosing the wrong overload. Or sticking it in the wrong parameter on certain templated functions.

Also, generally speaking, even without structural typing, the danger of passing an object of the wrong type to the wrong parameter/function/overload IS considered to be significant enough to help justify the whole premise of static typing. (Well, at least according to a certain faction of programmers, anyway ;) )

And ultimately, from a design standpoint, I really see no compelling reason *not* to require a formal declaration of "This deliberately implements isXYZ", other than just...some people just dislike it. So like, yea, we *can* get by without it...but...why would we want to?
May 05, 2018
On Monday, 30 April 2018 at 21:31:48 UTC, Giles Bathgate wrote:
> On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
>>[...]
>
> It takes some getting used to. Type inference is useful because often you don't care/know, or want to type out the full name of the type for every variable. It does, however, assume that the developer can also do type inference (when/if you need to know the type). When it's not clear what the type is by looking at the right-hand side perhaps the codebase has bigger problems.
>
> functions that return auto are a bit odd IMHO, that is a feature unique to D.

C++14 would beg to differ. As would any language with Hindley-Milner type inference.

Atila
May 06, 2018
On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
> I'll freely admit I haven't put a ton of thought into this post (never a good start), however I'm genuinely curious what people's feeling are with regards to the auto keyword.
>
> Speaking for myself, I dislike the auto keyword. Some of this is because I have a preference for static languages and I find auto adds ambiguity with little benefit. Additionally, I find it annoying that the phobos documentation relies heavily on auto obscuring return types and making it a bit more difficult to follow what is happening which gives me a bad taste for it.
>
> Having said, the thing that really started my thinking about this was this post I made:
>
> https://forum.dlang.org/thread/fytefnejxqdgotjkprpo@forum.dlang.org
>
> Where in order to declare a public variable for the RedBlackTree lowerBound/upperBound methods I had to fall back on using the ReturnType template to declare a variable. Jonathan was nice enough to point me in the right direction and maybe there's a way to do this without having to fall back on ReturnType. However this made be wonder if reliance on auto could discourage API writers from having sane return types.
>
> So I'm curious, what's the consensus on auto?

Auto is useful... simple as that. If you don't like it don't use it. If you get confused about it then beef up your game in analysis. Obscurity can be done many ways so complaining about one specific way is not very thought out.

myfunkytypethatyoudontknowabouthiddenthrough100layersofabstraction foo();

May 09, 2018
On Monday, April 30, 2018 21:11:07 Gerald via Digitalmars-d wrote:
> I'll freely admit I haven't put a ton of thought into this post (never a good start), however I'm genuinely curious what people's feeling are with regards to the auto keyword.
>
> Speaking for myself, I dislike the auto keyword. Some of this is because I have a preference for static languages and I find auto adds ambiguity with little benefit. Additionally, I find it annoying that the phobos documentation relies heavily on auto obscuring return types and making it a bit more difficult to follow what is happening which gives me a bad taste for it.
>
> Having said, the thing that really started my thinking about this was this post I made:
>
> https://forum.dlang.org/thread/fytefnejxqdgotjkprpo@forum.dlang.org
>
> Where in order to declare a public variable for the RedBlackTree lowerBound/upperBound methods I had to fall back on using the ReturnType template to declare a variable. Jonathan was nice enough to point me in the right direction and maybe there's a way to do this without having to fall back on ReturnType. However this made be wonder if reliance on auto could discourage API writers from having sane return types.
>
> So I'm curious, what's the consensus on auto?

I think that the overall consensus is that it's great but that you do have to be careful about using it when it reduces clarity without adding other benefits.

I remember when std.algorithm didn't use auto in any of its function signatures, because there was a bug in ddoc that made it so that functions that returned auto didn't show up in the docs. It was terrible. Seeing it would have scared off _way_ more people than any concerns over auto returns being confusing. You really, really, really don't want to know what many auto return types look like - especially if ranges are involved. You end up with templates wrapping templates, and seemingly simple stuff ends up looking ugly fast - e.g. until returns something like Until!("a == b", string, char). Having auto in the documentation is _must_ nicer.

Really, auto return functions make things possible that would never be possible without it simply because the code would be too hard to read. In the cases where all you care about is what API a return type has and not what the return type is, auto is a true enabler. Voldemort types then take than a step further by removing the possibility of referring to the type by name and forcing you to go off of its API, which improves maintenance from the perspective of the person maintaining the function (because then they can change the return type as much as they like so long as its API is the same). But even without Voldemort types, auto return types simplify the information to remove all of that extra stuff that you don't care about the type.

Of course, that can be taken too far and/or handled badly. The user of a function doesn't necessary have to care what the return type is, but they _do_ have to know enough to know what its API is. And that means that whenever a function returns auto, it needs to be clear in its documentation about what it's returning. If it's not, then obviously, the use auto becomes a problem. At least an explicit return type would have then made it possible for the user to look up the return type, whereas with auto and bad documentation, they're forced to use stuff like typeof and pragma(msg, ...) to figure out what the type is or to go look at the source code. So, while auto is awesome, anyone writing such a function needs to do a good job with documentation and try to put themselves in the shoes of whoever is using the function.

Another thing that can be taken from that is that if a function is designed to return something specific as opposed to an arbitrary type with a specific API, then it's generally better to put the type in the signature so that it's clear rather than use auto.

As for auto inside functions, I'd argue that it should be used heavily, and I think that most of us do. The cases where there's really no debate are functions that return auto, and when using the type's name would just be duplicating information. e.g. it's just bad practice to do

Type foo = new Type(42);

instead of

auto foo = new Type(42);

Avoiding auto buys you nothing and just increases your code maintenance if you change the type later. It's when you start using auto all over the place that it gets more debatable. e.g.

auto foo = someFunc();
foo.doSomething();
auto bar = someOtherFunc(foo);

Depending on what the codes doing, heavy use of auto can sometimes make such functions hard to read, and some common sense should be used. However, by using auto, you're frequently making it easier to refactor code when types change, as they sometimes do. In that snippet, so long as I can call doSomething on foo (be it as a member function or via UFCS) and so long as someOtherFunc accepts whatever type foo is, I don't necessarily care if the type of foo changes. If I need to refactor someFunc so that it returns something else that has a compatible API, then someFunc can be changed without having to change the code using it, whereas if the type name had been used explicitly, I would have had to change a lot more. And if the new type doesn't have a compatible API, then it will fail to compile either way, so the use of auto didn't matter.

The only real problem here is how readable the code is with auto, and that's going to be very dependent on what the code is doing it and who is reading it. But the types can still be figured out just by looking at the functions being called (and if you're actually using an IDE like many us don't, then you could probably hover your mouse over the type too see what it is without even looking at the documentation for the function), and honestly, I would argue that if you don't know what the functions being called are doing, then you need to look them up anyway. And if you do know what they're doing, then you know what they're returning, and auto isn't a problem at all.

Really, if it becomes a readability problem, then use explicit types, but in general, I think that using auto ultimately makes the code more maintainable, much as it can take some getting used to.

And I think that the biggest problems with using auto simply come from not being used to it and not knowing how to deal with it in those annoying cases where you need an explicit type but don't have one (e.g. to declare a member variable which isn't directly initialized) - but even that is really more of an issue with Voldemort types than auto specifically (much as they're related).

I think that experienced D programmers tend to use auto pretty much everywhere inside of functions and only use explicit types where necessary. The bigger question is when to return auto or not, and as long as the documentation is solid, that's usually not a problem - though if using auto isn't going to buy you anything there, then you might as well make the type explicit, since that improves clarity and simplifies the documentation.

- Jonathan M Davis

May 09, 2018
On Wednesday, 9 May 2018 at 12:44:34 UTC, Jonathan M Davis wrote:
> On Monday, April 30, 2018 21:11:07 Gerald via Digitalmars-d wrote:
>> [...]
>
> I think that the overall consensus is that it's great but that you do have to be careful about using it when it reduces clarity without adding other benefits.
>
> [...]

Using "auto" you can also have multiple return types.

auto foo(T)(T value)
{
    static if (is(T == int)) return "int: " ~ to!string(value);
    else return value;
}

You cannot give that function a specific return type as it's either T or it's string. It's not a single type.
May 09, 2018
On Wednesday, 9 May 2018 at 13:22:56 UTC, bauss wrote:
> Using "auto" you can also have multiple return types.
>
> auto foo(T)(T value)
> {
>     static if (is(T == int)) return "int: " ~ to!string(value);
>     else return value;
> }
>
> You cannot give that function a specific return type as it's either T or it's string. It's not a single type.

Its funny, because you example makes this look like a very bad feature. But there are legitimate cases which doesn't actually chance the api of the returned type.
May 09, 2018
On Wednesday, May 09, 2018 14:31:00 Jesse Phillips via Digitalmars-d wrote:
> On Wednesday, 9 May 2018 at 13:22:56 UTC, bauss wrote:
> > Using "auto" you can also have multiple return types.
> >
> > auto foo(T)(T value)
> > {
> >
> >     static if (is(T == int)) return "int: " ~ to!string(value);
> >     else return value;
> >
> > }
> >
> > You cannot give that function a specific return type as it's either T or it's string. It's not a single type.
>
> Its funny, because you example makes this look like a very bad feature. But there are legitimate cases which doesn't actually chance the api of the returned type.

Basically, it falls into the same situation as ranges and Voldemort types. If you have a situation where it doesn't make sense to restrict the return type to a specific type, where the return type depends on the template arguments, or where the return type should be hidden, then auto is great. But in all of those situations, the API has to be well-known, or the caller can't do anything useful. Worst case, you end up with a base API that's common to all of the return types (e.g. forward range) but where you have to test whether a particular set of template arguments results in a return type which supports more (e.g. a random-access range).

Ultimately, the key is that the user of the function needs to be able to know how to use the return type. In some cases, that means returning a specific type, whereas in others, it means using auto and being clear in the documentation about what kind of API the return type has. As long as the API is clear, then auto can be fantastic, but if the documentation is poorly written (or non-existant), then it can be a serious problem.

- Jonathan M Davis

May 11, 2018
On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
>
> So I'm curious, what's the consensus on auto?

My rule, is when I can't be bothered typing it all out, or can't be bothered working out what it is I'm actually meant to type, then I use auto (or var).

i.e. I use it as a time saver, and that's all.

The exception to that rule, is when it's important to the person writing/reading that code, to known what type is being used (and that's a rather subjective decision). In most cases, the type declaration is for other reasons (i.e not human consumption).

btw. In 1969, John C. Reynolds published his paper/specification on GEDANKEN - a typeless programming language, and type declaration was simply not permitted.

(Gedanken is German for thought experiment, more or less)

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.492.9205&rep=rep1&type=pdf

I wonder whether this is where languages will eventually end up again, in the future some time.

May 11, 2018
On Wednesday, 9 May 2018 at 15:06:55 UTC, Jonathan M Davis wrote:
> Ultimately, the key is that the user of the function needs to be able to know how to use the return type. In some cases, that means returning a specific type, whereas in others, it means using auto and being clear in the documentation about what kind of API the return type has. As long as the API is clear, then auto can be fantastic, but if the documentation is poorly written (or non-existant), then it can be a serious problem.
>
> - Jonathan M Davis

He also needs to know what requirements the parameters of the function should satisfy. We have template constraints for that, even though that could also have been "implemented" through documentation.