www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - auto: useful, annoying or bad practice?

reply Gerald <gerald.b.nunn gmail.com> writes:
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?
Apr 30 2018
next sibling parent JN <666total wp.pl> writes:
On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
 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.
I'm of the same opinion. I think auto (or var in other languages) obscures the types and requires an IDE to reveal what the real type is (hopefully the IDE can figure it out). But it seems we are in minority. Even Java is adopting auto (called var there) and everyone is cheering for that :)
Apr 30 2018
prev sibling next sibling parent reply Giles Bathgate <giles.bathgate gmail.com> writes:
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.
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. But I don't have a problem with type inference in Rust...etc not that that is a good argument, but just that its something you have to get used to, because people will use the feature whether you like it or not.
Apr 30 2018
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Apr 30, 2018 at 09:31:48PM +0000, Giles Bathgate via Digitalmars-d
wrote:
[...]
 functions that return auto are a bit odd IMHO, that is a feature
 unique to D. But I don't have a problem with type inference in

 Rust...etc not that that is a good argument, but just that its
 something you have to get used to, because people will use the feature
 whether you like it or not.
[...] To me, a function that returns auto is basically saying "you should not need to know, nor should you depend on, the dirty details of the actual return type". There's a time and place for that, and I don't recommend liberally sprinkling auto return types everywhere just for laziness' sake. But when it's used, it should be used for improving encapsulation, by reducing the leakage of implementation details of the return type, and limiting the calling code to using only agreed-upon properties (i.e., via documentation, contract, etc.) or introspection using static if (meaning that your code is explicitly checking for a certain property before using it, which is a good defensive design, and presumably will be able to handle the case where said property no longer holds). Basically, it's stepping away from the bad old practice of the caller code digging into the innards of your data types that were never meant for public use, and consequently you being locked into a particular implementation because changing said innards will break existing code, even if existing code isn't using the type "properly"; and stepping into a better world of more encapsulated and flexible code. It's stepping away from that old world of code depending on "undocumented features" and "hidden calls", to a better world of code that will Just Work(tm) even if the underlying library changes a return type, and code that can adapt itself to the return type by using introspection to discover what is/isn't available without hardcoding anything or making unfounded assumptions about what the return type has/doesn't have. T -- Music critic: "That's an imitation fugue!"
Apr 30 2018
parent Chris <wendlec tcd.ie> writes:
On Monday, 30 April 2018 at 21:56:23 UTC, H. S. Teoh wrote:
 On Mon, Apr 30, 2018 at 09:31:48PM +0000, Giles Bathgate via 
 Digitalmars-d wrote: [...]
[...]
 T
On Monday, 30 April 2018 at 21:56:23 UTC, H. S. Teoh wrote: [...] Appart from the good points Teoh has made, imagine you would have to statically type in for loops: foreach (i; items) writefln("%s", i); And, yeah, chainig would indeed be a PIA: auto items = myRange.filter!(a => a.length); Most of the time you don't really care about the exact type, only at the end of the block (e.g. it should be `string[]`). And the compiler always tells you about the types, e.g. something like "cannot implicitly convert `words` of type MapResult to string[]". And then, _at the end_, you can use something like items.array; to get `string[]`. I think the problem is not `auto`, the problem is thinking in old ways. If you think old-style Java where you declare loads of statically typed class variables at the beginning, then you're in trouble. But in order to use `auto`, you have to use a different approach. Java: String name; String address; int uiid; ... String getName() { return name; } [...] In D, you are more likely to use home made structs / ranges / tuples as return types that all have the same properties, so auto makes more sense (incomplete code): auto byUser(R)(R entries) { struct User { private { R entries; } this(R entries) { this.entries = entries; } property bool empty() { return range empty? (true|false) } property void popFront() { // e.g. entries = entries[1..$]; } property auto front() { return whatever; // e.g. struct UserInfo { string name; size_t uuid; } } } return User(entries); } The struct `User` can perform whatever is necessary, it can be changed, even renamed but the return type will always conform to the range interface: empty, popFront, front, so the caller can be sure s/he can use it as a range. Also, the type of `entries` can change, but the code will still work. No need to redefine all the variables as a different type. Now auto users = entries.byUser(); can be whatever and you can perform other range based algorithms [1] auto herberts = entries.byUser.filter!(a => a.name == 'Herbert')...etc.etc. Imagine having to use handwritten static typing for that! [1] https://tour.dlang.org/tour/en/gems/range-algorithms
May 01 2018
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
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 05 2018
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Apr 30, 2018 at 09:11:07PM +0000, Gerald via Digitalmars-d wrote:
[...]
 So I'm curious, what's the consensus on auto?
Don't know what others think, but personally, it's one of the best things about D: all the safety of static typing, yet with the convenience of automatic type inference, so that I don't have to keep repeating myself. When I'm using chained range-based algorithms, I really do *not* want to have to name every intermediate type explicitly. That would be so cumbersome that the idiom would be essentially useless. Also, design by introspection. Dependence on explicit types is so last century. Design by introspection FTW! Decoupling your code from explicit types makes it more encapsulated, and gives you incentive to write more defensively, resulting in better, more change-resilient code. When an upstream library changes a return type, you can just recompile and go, rather than waste time patching the 150 different places where the explicit type was named. Let the machine do the work for you! As for ReturnType: I actually find it completely worthless. It adds needless template bloat, where typeof() (specifically, typeof(return)) would do just fine. And as for the documentation issue: either (1) the docs are b0rken and need to be rewritten because they didn't describe the returned type sufficiently well, in which case either the docs need to be fixed, or the library is poorly written and you should find another one; or (2) it is an indication that your code has extraneous dependencies on explicit return types and therefore is flaky and will break with the slightest upstream changes, in which case the code should be restructured and the needless assumptions / dependencies removed. T -- 2+2=4. 2*2=4. 2^2=4. Therefore, +, *, and ^ are the same operation.
Apr 30 2018
parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 04/30/2018 05:35 PM, H. S. Teoh wrote:
 
 Also, design by introspection.  Dependence on explicit types is so last
 century.  Design by introspection FTW!  Decoupling your code from
 explicit types makes it more encapsulated, and gives you incentive to
 write more defensively, resulting in better, more change-resilient code.
 When an upstream library changes a return type, you can just recompile
 and go, rather than waste time patching the 150 different places where
 the explicit type was named.  Let the machine do the work for you!
 
There's a lot about that I like too, but I really wish D didn't use structral typing to do it. I still think the only reason D's structral typing hasn't blown up in our faces is because it's still mostly limited to the basic ranges isn't not a prevalent part of most internal and external APIs. Structural typing basically amounts to a form of global namespace (which can also work out ok *if used sparingly*, not that I'd want to try). At first, D thankfully killed off the global namespace for the most part...but then it brings its pitfalls right back in the form of structural typing. Grrr... Structral typing isn't "If it walks like a duck and quacks like a duck...". Structural typing is "If it walks and it talks, then it must be a duck, because ducks walk and have their own form of talk, so clearly anything that walks and talks must be a duck."
May 01 2018
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, May 02, 2018 at 12:11:57AM -0400, Nick Sabalausky (Abscissa) via
Digitalmars-d wrote:
 On 04/30/2018 05:35 PM, H. S. Teoh wrote:
 
 Also, design by introspection.  Dependence on explicit types is so
 last century.  Design by introspection FTW!  Decoupling your code
 from explicit types makes it more encapsulated, and gives you
 incentive to write more defensively, resulting in better, more
 change-resilient code.  When an upstream library changes a return
 type, you can just recompile and go, rather than waste time patching
 the 150 different places where the explicit type was named.  Let the
 machine do the work for you!
 
There's a lot about that I like too, but I really wish D didn't use structral typing to do it. I still think the only reason D's structral typing hasn't blown up in our faces is because it's still mostly limited to the basic ranges isn't not a prevalent part of most internal and external APIs. Structural typing basically amounts to a form of global namespace (which can also work out ok *if used sparingly*, not that I'd want to try). At first, D thankfully killed off the global namespace for the most part...but then it brings its pitfalls right back in the form of structural typing. Grrr...
I've been using structural typing / design by introspection to good success in my latest project, actually. I was writing a submodule that handled interfacing with POVRay for 3D rendering (non-realtime, obviously), and wanted a clean API that didn't force tons of boilerplate on you. The main problem here is that POVRay has so many features, knobs, and dials that either you have to dumb things down so much that you're no longer using most of its features, which defeats the purpose of using it(!), or you have to essentially reinvent SDL in your API so that your users have access to everything they might need, which makes your API so complex and hairy it would require Java-style verbosity, complete with factory classes, wrapper types, a class hierarchy, and gratuitousUnreasonablyLongIdentifiers, just to initialize it to do something as simple as rendering a couple of polygons. Eventually, I settled on DoI by specifying some basic required properties for, say, polygons to be output as a mesh, y'know, vertex coordinates and lists of vertex indices for the polygons, with many optional parameters checked by static if. So if you wanted just to output a couple of polygons with flat textures, all you have to do is to pass in an array of vectors and an array of int[]'s, and off you go. If you wanted different textures for each polygon, make a struct that aliases int[] to this (or overloads opIndex) plus a .texture field to specify the texture. If you wanted surface normal interpolation, wrap your vectors in a struct that has a .normal field specifying the normals to interpolate. On the implementation side, it's just a bunch of static if's that optionally checks if a property is present, and provide implementation for it if it is. If it weren't for DoI, I would've ended up with a big, complicated API with tons of boolean switches or a class hierarchy to provide for all the variations that might be needed, and it would have required a ton of boilerplate just to output a couple of flat polygons. With DoI, I get to scale the amount of initialization code I need according to how many features I will actually use.
 Structral typing isn't "If it walks like a duck and quacks like a
 duck...".  Structural typing is "If it walks and it talks, then it
 must be a duck, because ducks walk and have their own form of talk, so
 clearly anything that walks and talks must be a duck."
How else would you do DoI, though? With Concepts? The advantage of using structural typing over concepts for DoI is that you would need an exponential number of concepts to catch up with a linear number of optional fields in a structural typing model. Sure, structural typing has its warts, but it's at least more scalable in this respect. T -- What do you call optometrist jokes? Vitreous humor.
May 02 2018
next sibling parent Mark <smarksc gmail.com> writes:
On Wednesday, 2 May 2018 at 14:05:49 UTC, H. S. Teoh wrote:
 How else would you do DoI, though?  With Concepts?  The 
 advantage of using structural typing over concepts for DoI is 
 that you would need an exponential number of concepts to catch 
 up with a linear number of optional fields in a structural 
 typing model.  Sure, structural typing has its warts, but it's 
 at least more scalable in this respect.


 T
Why? I don't think concepts force you to write a specific implementation for every possible combination of compile-time values.
May 02 2018
prev sibling next sibling parent Neia Neutuladh <neia ikeran.org> writes:
On Wednesday, 2 May 2018 at 14:05:49 UTC, H. S. Teoh wrote:
 How else would you do DoI, though?  With Concepts?  The 
 advantage of using structural typing over concepts for DoI is 
 that you would need an exponential number of concepts to catch 
 up with a linear number of optional fields in a structural 
 typing model.  Sure, structural typing has its warts, but it's 
 at least more scalable in this respect.
UDAs to the rescue! class JackInTheBox { // Which way is it facing? Direction front(); // Pop the box open and have the doll spring out toward the front side void popFront(); // Remove everything from the box. Return true if stuff was removed. bool empty(); } We're in for a bad time. Range class JackInTheBox { ... } Here I just shot myself in the foot. // A document is a range of pages, maybe. Range struct Document { // saves to disk SaveResults save(); } This is a subtler problem. struct Document { Range SaveResults save(); } I shot myself in the foot again. D's options for applying annotations to a block or in label style, it wouldn't be terribly onerous.
May 02 2018
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 05/02/2018 10:05 AM, H. S. Teoh wrote:
 
 I've been using structural typing / design by introspection to good
 success in my latest project, actually.
I don't doubt that. Similar to global namespace, if the structural typing is only used heavily by one or two components of a project (ie, libs, etc), then it's not too difficult to avoid problems. The real danger and problems come when a project uses several components that all make heavy use of either a global namespace (which D luckily doesn't really have) or structural typing.
 Structral typing isn't "If it walks like a duck and quacks like a
 duck...".  Structural typing is "If it walks and it talks, then it
 must be a duck, because ducks walk and have their own form of talk, so
 clearly anything that walks and talks must be a duck."
How else would you do DoI, though? With Concepts? The advantage of using structural typing over concepts for DoI is that you would need an exponential number of concepts to catch up with a linear number of optional fields in a structural typing model. Sure, structural typing has its warts, but it's at least more scalable in this respect.
With a slight variation on structural typing that's augmented with mandatory opt-in. I'll explain: 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. As an aside: Think you're not likely to hit false positives with structural typing? Well, in all honestly, unless you're really sloppy, you're not likely hit name collisions in the global namespace either...UNTIL you reach the point where projects are composed of many third-party packages, and most third party packages start using the global namespace in their own way. Then it becomes a distinct possibility. The same dynamic applies here because, like global namespaces, structural typing (by default) has no mechanism for scope-limiting or compartmentalization, and (intentionally) operates on unqualified names. We address the global namespace's lack of scope-limiting and compartmentalization through...well, namespaces. The namespaces may be implied by classes (old-school Java), by compilation units (D), or specified manually (C++). But how do we address structural typing's lack of scope-limiting and compartmentalization? Currently, we don't. And that's the problem. 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.
May 03 2018
next sibling parent reply Laeeth Isharc <laeeth laeeth.com> writes:
On Friday, 4 May 2018 at 04:12:09 UTC, Nick Sabalausky (Abscissa) 
wrote:
 On 05/02/2018 10:05 AM, H. S. Teoh wrote:
 [...]
I don't doubt that. Similar to global namespace, if the structural typing is only used heavily by one or two components of a project (ie, libs, etc), then it's not too difficult to avoid problems. The real danger and problems come when a project uses several components that all make heavy use of either a global namespace (which D luckily doesn't really have) or structural typing. [...]
Have you seen Atila's concepts library?
May 04 2018
parent "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 05/04/2018 03:56 AM, Laeeth Isharc wrote:
 On Friday, 4 May 2018 at 04:12:09 UTC, Nick Sabalausky (Abscissa) wrote:
 On 05/02/2018 10:05 AM, H. S. Teoh wrote:
 [...]
I don't doubt that. Similar to global namespace, if the structural typing is only used heavily by one or two components of a project (ie, libs, etc), then it's not too difficult to avoid problems. The real danger and problems come when a project uses several components that all make heavy use of either a global namespace (which D luckily doesn't really have) or structural typing. [...]
Have you seen Atila's concepts library?
Yea, last I checked, it doesn't address what I'm talking about at all. That lib's basically just this: struct Foo {...} static assert(isXYX!Foo); But with more detailed diagnostics. (Plus it can help you write the isXYZ function.) Unless something's changed since last looked at it, it didn't address my " models!(Foo, isFoo)", then isFoo!Foo should return false.
May 04 2018
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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 04 2018
parent "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
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 04 2018
prev sibling next sibling parent reply Craig Dillabaugh <craig.dillabaugh gmail.com> writes:
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.
clip
 So I'm curious, what's the consensus on auto?
As some have pointed out, it certainly has value. For example, in functions returning ranges, etc. where you wouldn't want to have to write out the whole type. However, as an infrequent D user I admit I prefer to see the actual type where it is feasible, as I find 'auto' is a barrier to understanding to someone who isn't familiar with a particular piece of code. I would never use auto in place of a basic type.
May 01 2018
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
auto has its uses, but it's wildly overused, especially in library code 
and documentation, and really, really, *really* much so in documentation 
examples.


On 05/01/2018 06:09 AM, Craig Dillabaugh via Digitalmars-d wrote:
 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.
clip
 So I'm curious, what's the consensus on auto?
As some have pointed out, it certainly has value. For example, in functions returning ranges, etc. where you wouldn't want to have to write out the whole type. However, as an infrequent D user I admit I prefer to see the actual type where it is feasible, as I find 'auto' is a barrier to understanding to someone who isn't familiar with a particular piece of code.  I would never use auto in place of a basic type.
May 20 2018
parent Chris <wendlec tcd.ie> writes:
On Sunday, 20 May 2018 at 23:01:39 UTC, Charles Hixson wrote:
 auto has its uses, but it's wildly overused, especially in 
 library code and documentation, and really, really, *really* 
 much so in documentation examples.
A lot of functions in `std.algorithm` are actually quite clear about it, e.g. `splitter`: "auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s) ... Returns: An input range of the subranges of elements between separators. If r is a forward range or bidirectional range, the returned range will be likewise. When a range is used a separator, bidirectionality isn't possible." So it obviously returns a range which you can process as a range. No need to know about the exact type (which may change). All you need to know is that it adheres to a minimal range interface (r.empty, r.front, r.popFront). Without `auto`, it'd be a solid mess and generics would become more or less useless. Even Java is moving into this direction and it makes sense for a lot of day to day issues, programmes will increasingly demand it. Code like the following is much more elegant than using the ould for loop: JSONObject jitems = new JSONObject(); data.entrySet() .forEach(i -> { jitems.put(i.getKey(), i.getValue()); });
May 21 2018
prev sibling next sibling parent Nick Sabalausky <a a.a> writes:
I'm a die-hard static typing fan, hate dynamic languages, heck I 
dont even like structural typing (ex, as used by D ranges). And 
that's exactly why I *love* auto. It lets you have static typing 
without turning programming into a 2000's-era C++/Java-style 
royal anti-DRY PITA.

I also think auto is especially nice for NOT requiring a 
heavy-weight IDE, because it makes refactorings and type renaming 
much simpler.

In the very rare case that I'm not clear what type a var is, 
first of all, that's usually a sign something else is wrong with 
the code, and secondly, that's trivially answered by tossing in a 
"pragma(msg, typeof(xxxx))".

Now, all that said, using auto for a function signature's return 
type shouldn't usually be done, except in very careful, specific 
"voldemort type" kinds of situations (and even then, I dont see a 
real big point).
May 01 2018
prev sibling next sibling parent reply Nick Sabalausky <a a.a> writes:
I'm a die-hard static typing fan, hate dynamic languages, heck I 
dont even like structural typing (ex, as used by D ranges). And 
that's exactly why I *love* auto. It lets you have static typing 
without turning programming into a 2000's-era C++/Java-style 
royal anti-DRY PITA.

I also think auto is especially nice for NOT requiring a 
heavy-weight IDE, because it makes refactorings and type renaming 
much simpler.

In the very rare case that I'm not clear what type a var is, 
first of all, that's usually a sign something else is wrong with 
the code, and secondly, that's trivially answered by tossing in a 
"pragma(msg, typeof(xxxx))".

Now, all that said, using auto for a function signature's return 
type shouldn't usually be done, except in very careful, specific 
"voldemort type" kinds of situations (and even then, I dont see a 
real big point).
May 01 2018
parent Dgame <r.schuett.1987 gmail.com> writes:
On Wednesday, 2 May 2018 at 00:01:42 UTC, Nick Sabalausky wrote:
 Now, all that said, using auto for a function signature's 
 return type shouldn't usually be done, except in very careful, 
 specific "voldemort type" kinds of situations (and even then, I 
 dont see a real big point).
I do it all the time because of attribute inference. D has way to many attributes, so I'm willing to automatically let the Compiler interfere which are appropriate, he knows it better anyway.
May 01 2018
prev sibling next sibling parent Neia Neutuladh <dhasenan ikeran.org> writes:
On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
 So I'm curious, what's the consensus on auto?
For local variables, it's not an unalloyed good, but it is good. When I use Java, everything is explicit, and sometimes that's nice. In D, I think I overuse `auto` for local variables. Sometimes I find myself switching it out for an explicit type, just because I want the compiler to check that the right-hand side is of the type I expect (eg with numeric conversion rules). For function return types, though? The consensus among Phobos devs is it's a "here be dragons" sign. It's a sign that there could be breaking changes to your code without warning if you use that return type in a nontrivial way. Of course, this isn't made clear anywhere, and they don't actually make these changes all that often. But even for that purpose, it's broken. Like your example was basically: RBRange!(RBNode!long*) divide(RedBlackTree!long tree, long pivot, bool first) { if (first) return tree.upperBound(pivot); return tree.lowerBound(pivot); } If the range type were public, your code would work. If subsequently the Phobos devs needed to change the range type, they could provide an alias, and your code would still work. If they needed to change upperBound to return a different type than lowerBound, there's nothing they can do about it; your code is broken. But since the return type for both functions is `auto`, they can tell you you were wrong to write that code and they were right to make it stop working.
May 01 2018
prev sibling next sibling parent Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
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.

 So I'm curious, what's the consensus on auto?
I'll start with auto for declaration and then talk about auto for return types. The main issue with utilizing auto is that a type will lead you to documentation on the capabilities of the variable and to locate the implementation of the code being called. The desire to decouple from the concrete type is admirable but generally the desire for the concrete type isn't about coupling with it. What I find though is that auto doesn't increase the difficulty of the objectives I described. What I find is in other languages type but an interface. This can be nice for capabilities of the variable but all to often I find that the API still expects you to convert to a concrete type. And you still need to track down the actually concrete type to review the actual implementation of that function. I find auto to be very useful for development as I can switch out the type be it a class/struct or interface rather easily. I have mixed feelings when it comes to debugging. For a stable and unchanging interface (concept not construct) it should be avoided. Use of auto in return type I also like but use sparingly. This is because it hides documentation of an API. And I'm not talking about the function documentation laking specification of what it returns I'm talking about types being a good way to make a point of reference. For example an auto function which returns a range can easily reference the Range API since it is the D interface for iteration. Custom/framework types don't have that luxury.
May 02 2018
prev sibling next sibling parent Nerve <nervecenter7 gmail.com> writes:
On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
 So I'm curious, what's the consensus on auto?
Speaking for myself: 1) I find auto to be useful when instantiating objects locally; eliminates redundancy, noise, and line size. 2) I avoid auto functions and despise them in API documentation. It’s difficult to work with the return type of a function when the definition is auto and the author didn’t follow up by describing exactly what was returned. 3) I avoid auto for primitive types because their type names are short, and declaring them C- and Ada-style at the beginning of a local block helps massively clarify what a function is about to do. So basically only for object instantiations. I’ve yet to find a great use for it outside of that.
May 03 2018
prev sibling next sibling parent IntegratedDimensions <IntegratedDimensions gmail.com> writes:
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 06 2018
prev sibling next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
parent reply bauss <jj_1337 live.dk> writes:
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
parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
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
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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 09 2018
parent reply Mark <smarksc gmail.com> writes:
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.
May 11 2018
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, May 11, 2018 at 04:57:05PM +0000, Mark via Digitalmars-d wrote:
 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.
This makes me wonder if it might be useful to have return-type constraints. A kind of static out-contract? Something that's part of the function declaration, that ensures that the return type satisfies certain properties. // Hypothetical syntax auto myfunc(R)(R r) if (isInputRange!R && isOutputRange!return) { ... // implementation here } The `isOutputRange!return` (this is just tentative syntax, you guys can probably think of better ways of writing this) statically enforces that the return type must satisfy `isOutputRange`, and, being part of the function signature, documents to the user what to expect of it. Could be valuable. Of course, we could repurpose existing out contracts for this, e.g.: auto myfunc(R)(R r) if (isInputRange!R) out(r) { static assert(isOutputRange!(typeof(r))); } do { } A little more verbose, and has issues with the semantics of -release, etc., but at least it gets the point across. T -- In order to understand recursion you must first understand recursion.
May 11 2018
next sibling parent reply Chris M. <chrismohrfeld comcast.net> writes:
On Friday, 11 May 2018 at 18:44:25 UTC, H. S. Teoh wrote:
 On Fri, May 11, 2018 at 04:57:05PM +0000, Mark via 
 Digitalmars-d wrote:
 [...]
This makes me wonder if it might be useful to have return-type constraints. A kind of static out-contract? Something that's part of the function declaration, that ensures that the return type satisfies certain properties. [...]
Coincidentally, over in Rust... https://blog.rust-lang.org/2018/05/10/Rust-1.26.html
May 11 2018
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, May 11, 2018 at 06:56:13PM +0000, Chris M. via Digitalmars-d wrote:
 On Friday, 11 May 2018 at 18:44:25 UTC, H. S. Teoh wrote:
 On Fri, May 11, 2018 at 04:57:05PM +0000, Mark via Digitalmars-d wrote:
 [...]
This makes me wonder if it might be useful to have return-type constraints. A kind of static out-contract? Something that's part of the function declaration, that ensures that the return type satisfies certain properties. [...]
Coincidentally, over in Rust... https://blog.rust-lang.org/2018/05/10/Rust-1.26.html
Great minds think alike. :-D (Or fools seldom differ?) T -- The problem with the world is that everybody else is stupid.
May 11 2018
prev sibling parent Mark <smarksc gmail.com> writes:
On Friday, 11 May 2018 at 18:44:25 UTC, H. S. Teoh wrote:
 On Fri, May 11, 2018 at 04:57:05PM +0000, Mark via 
 Digitalmars-d wrote:
 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.
This makes me wonder if it might be useful to have return-type constraints. A kind of static out-contract? Something that's part of the function declaration, that ensures that the return type satisfies certain properties. // Hypothetical syntax auto myfunc(R)(R r) if (isInputRange!R && isOutputRange!return) { ... // implementation here } The `isOutputRange!return` (this is just tentative syntax, you guys can probably think of better ways of writing this) statically enforces that the return type must satisfy `isOutputRange`, and, being part of the function signature, documents to the user what to expect of it.
-
 T
This method won't work for non-template functions (since template constraints can be used only in, well, templates). Granted, non-template functions with auto return type are pretty rare, but we probably don't want to impose an unnecessary restriction.
May 12 2018
prev sibling next sibling parent KingJoffrey <KingJoffrey KingJoffrey.com> writes:
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 10 2018
prev sibling next sibling parent reply KingJoffrey <KingJoffrey KingJoffrey.com> writes:
On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
 So I'm curious, what's the consensus on auto?
In the example below, what would I use, besides auto? ------------------------ module test; void main () { import std.stdio : writeln; auto result = newKing("King Joffrey"); writeln(result.getName); } auto newKing(string name) { class King { private string _name; public string getName() { return this._name ~ " : the one true king!"; } this() { this._name = name; } } return new King; } ------------------------------
May 16 2018
parent Neia Neutuladh <neia ikeran.org> writes:
On Wednesday, 16 May 2018 at 10:52:42 UTC, KingJoffrey wrote:
 On Monday, 30 April 2018 at 21:11:07 UTC, Gerald wrote:
 So I'm curious, what's the consensus on auto?
In the example below, what would I use, besides auto?
auto is required for Voldemort types, so you would change it to not be a Voldemort type.
May 16 2018
prev sibling parent reply Kagamin <spam here.lot> writes:
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.
I prefer types spelled as it helps to understand the code. In javascript I have to look around to figure out types of variables like `var id = 0L;` - to think someone would go to such length only to insist on type inference. In part this can be due to unreasonably difficult to spell types like ptrdiff_t, so I try to design my code in a way that it's easy to spell types.
May 17 2018
parent reply Chris <wendlec tcd.ie> writes:
On Thursday, 17 May 2018 at 11:38:13 UTC, Kagamin wrote:
 I prefer types spelled as it helps to understand the code. In 
 javascript I have to look around to figure out types of 

 surprising abuse like `var id = 0L;` - to think someone would 
 go to such length only to insist on type inference.
 In part this can be due to unreasonably difficult to spell 
 types like ptrdiff_t, so I try to design my code in a way that 
 it's easy to spell types.
I was working with Java 8 this week, and God did I miss `auto`. And now Java will get `var`[1], t'was about time, indeed! Java 8 introduced lambdas and functional style features. For lambdas and streams the compiler aleady performs automatic type inference (as it does for generics, btw). So the next logical step is local type inference with `var`. I think that a functional apporach (stream().filter().map()...) leads almost automatically to `auto` (pardon the pun). Else programming becomes very cumbersome, because you have to keep track of the types all the way down the chain. That's time better spent somewhere else. In a way Java has slowly been moving in that direction anyway, cf. this answer [2] that reminded me of D's `auto` return type. [1] http://segasolutionsinc.com/2018/03/30/var-in-java10/ [2] https://stackoverflow.com/questions/1348199/what-is-the-difference-between-the-hashmap-and-map-objects-in-java
May 18 2018
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Friday, 18 May 2018 at 10:09:20 UTC, Chris wrote:
 In a way Java has slowly been moving in that direction anyway, 
 cf. this answer [2] that reminded me of D's `auto` return type.
 [2] 
 https://stackoverflow.com/questions/1348199/what-is-the-difference-between-the-hashmap-and-map-objects-in-java
Except the return type that you wrote in that case tells you almost everything you can do with that value. If you specify the return type of a function as `auto`, it tells you nothing. The Java equivalent would be to return Object.
May 18 2018
parent reply Chris <wendlec tcd.ie> writes:
On Friday, 18 May 2018 at 16:25:52 UTC, Neia Neutuladh wrote:
 On Friday, 18 May 2018 at 10:09:20 UTC, Chris wrote:
 In a way Java has slowly been moving in that direction anyway, 
 cf. this answer [2] that reminded me of D's `auto` return type.
 [2] 
 https://stackoverflow.com/questions/1348199/what-is-the-difference-between-the-hashmap-and-map-objects-in-java
Except the return type that you wrote in that case tells you almost everything you can do with that value. If you specify the return type of a function as `auto`, it tells you nothing. The Java equivalent would be to return Object.
My point was about implementing an interface you can rely on, regardless of what auto returns, eg: auto name = user.name; user could be anything, but it still has the field / property 'name'.
May 19 2018
parent reply I love Ice Cream <IloveIcecream. icecreamsandwhich.com> writes:

perfect sense. There is nothing wrong with it and it takes out 
redundant 'noise':

     var i = "This is a string"; // don't need to know two times 
that this is a string.
     var j = SomethingThatReturns(); // IDE or function docs will 
have my back here...

FYI, I wouldn't use this for 'time saving'. It's to take out 
redundant noise, which can clutter code. It also enables easy 
switching of return types from methods which occasionally means 
you don't have to change any code. It is frequent the type you 
change it to might follow the same protocol/interface as the 
previous type. Or you are just not using anything special with 

and it's common with the .NET framework that this happens (for 
instance when working with collections). In short 'auto' or 'var' 
is not for time saving. It's to increase code readability and 
code refactorability. Those are it's benefits worth a damn.

Most people spend more time reading code than writing code so if 
you are worried about the time it takes to type out 'auto' vs. 
'SomeObnoxiouslyLongAndOverlyVerboseTypeName' then I think you 
are turning an anthill into a mountain. It's one thing when a 
language feature takes away the need to write hundreds of lines 
of common boiler plate code, but when it just prevents you from 
typing out a really long name? Come on...please...that's not why 
'auto'/'var' has gained industry recognition. There's just a lot 
of people that use it because it's an idiom without taking the 
time to understanding why it _is_ an idiom. So they say things 
like 'I type less, so I like it'.

Which brings me to where it probably is not a good place for 
it...in the return fadeclaration of a function/method. I'm very 
close to saying, even after having read some of the comments that 
try justifying it, that 100% of the time you want to specify the 
return type. When you create a function that returns something, 
you are actively deciding 'I need to return this thing, this is 
useful or the point of this function'. To not have that thing 
obviously spelled out in the API is a huge design mistake. Not 
only do you take away a self-documenting part of you function 
definition, you make someone have to either have a good IDE 
(which some people are adverse to using) or actually go read the 
source code. To not put the effort into thinking about the type 
(or constraints on the type when dealing with generic/templated 
code) is just lazy. And worse, it will facilitate libraries where 
people didn't think about their API as much as they should.
May 19 2018
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Sunday, 20 May 2018 at 00:44:13 UTC, I love Ice Cream wrote:
 Which brings me to where it probably is not a good place for 
 it...in the return fadeclaration of a function/method. I'm very 
 close to saying, even after having read some of the comments 
 that try justifying it, that 100% of the time you want to 
 specify the return type. When you create a function that 
 returns something, you are actively deciding 'I need to return 
 this thing, this is useful or the point of this function'. To 
 not have that thing obviously spelled out in the API is a huge 
 design mistake. Not only do you take away a self-documenting 
 part of you function definition, you make someone have to 
 either have a good IDE (which some people are adverse to using) 
 or actually go read the source code. To not put the effort into 
 thinking about the type (or constraints on the type when 
 dealing with generic/templated code) is just lazy. And worse, 
 it will facilitate libraries where people didn't think about 
 their API as much as they should.
The return type for range-oriented functions in std.algorithm is usually not terribly useful. You get a range whose capabilities depend on the type of range you pass in, so you have to read the source code in any case. But at least listing the actual return type lets you skip some reading. This design by introspection stuff is powerful, but it's unfriendly toward people trying to understand your code. D is very hard to make an IDE for that would actually tell you what type the return value is. `pragma(msg, typeof(foo))` is probably your best bet, and that's kind of terrible. Oh, there's also one other use of `auto` return types that I haven't seen mentioned before: it functions as a DMD pragma to include the function in generated .di files. Wretched, no?
May 19 2018
next sibling parent reply I love Ice Cream <IloveIcecream. icecreamsandwhich.com> writes:
On Sunday, 20 May 2018 at 02:34:38 UTC, Neia Neutuladh wrote:
 The return type for range-oriented functions in std.algorithm 
 is usually not terribly useful.
So the first question that comes to my mind are what are the 'rules' of the output. Which is really what typing is. It's a series of rules. Your object is allowed to call these methods/properties. It is 'this' size. Languages have generic returns. D is not the only language with a concept of returning a 'compiler determined type'. But the rules are always baked into the API. If it's not, it's a design failure and more thought should be put into it. The only place I wouldn't be so strict with auto returns is in private methods. However, I still might tell someone to think about what they are trying to return there. It's not an unimportant piece of the API.
May 20 2018
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, May 20, 2018 14:33:16 I love Ice Cream via Digitalmars-d wrote:
 On Sunday, 20 May 2018 at 02:34:38 UTC, Neia Neutuladh wrote:
 The return type for range-oriented functions in std.algorithm
 is usually not terribly useful.
So the first question that comes to my mind are what are the 'rules' of the output. Which is really what typing is. It's a series of rules. Your object is allowed to call these methods/properties. It is 'this' size. Languages have generic returns. D is not the only language with a concept of returning a 'compiler determined type'. But the rules are always baked into the API. If it's not, it's a design failure and more thought should be put into it. The only place I wouldn't be so strict with auto returns is in private methods. However, I still might tell someone to think about what they are trying to return there. It's not an unimportant piece of the API.
That's where you get into a combination of needing good documentation and needing to know what set of traits to use to test the type to see what functionality it has. But for better or worse, asking a type what it can do is a key piece of Design by Introspection. The result can be extremely powerful, as Andrei has talked about on multiple occasions (I'd suggest watching his dconf 2015 talk about it if you haven't), but it does place a higher burden on the programmer to figure out how to use a type. So, there are pros and cons. Ultimately, auto can be extemely useful, and much of what we do with D really wouldn't be reasonably feasible without it, but it also needs to be used intelligently, because while it does bring some serious benefits, it can be at the cost of clarity if used poorly. - Jonathan M Davis
May 20 2018
prev sibling parent reply I love Ice Cream <IloveIcecream. icecreamsandwhich.com> writes:
On Sunday, 20 May 2018 at 02:34:38 UTC, Neia Neutuladh wrote:
 D is very hard to make an IDE for that would actually tell you 
 what type the return value is.
This might sound a little hard, but that's probably a fundamental failure of the D language and explains some of the poor tooling around the language.
May 20 2018
parent Neia Neutuladh <neia ikeran.org> writes:
On Sunday, 20 May 2018 at 14:35:21 UTC, I love Ice Cream wrote:
 On Sunday, 20 May 2018 at 02:34:38 UTC, Neia Neutuladh wrote:
 D is very hard to make an IDE for that would actually tell you 
 what type the return value is.
This might sound a little hard, but that's probably a fundamental failure of the D language and explains some of the poor tooling around the language.
This is specifically when metaprogramming is going on. Most programming languages these days fall into one of three categories: * They don't support metaprogramming. * They support metaprogramming by reflection only, like Java and Go. As soon as you use any metaprogramming, your IDE can't tell you what types things are; you've got interface{} or Object and you're calling Field.get() and Method.invoke(). * They support metaprogramming by merit of being a dynamic language. As soon as you write a line of code, your IDE can't tell you what types things are. If you write D without any metaprogramming, it's as easy to write an IDE for that as for Java. The problem is that metaprogramming is useful, and in D it's not that hard, so we use it a lot. There are proposals floating around for "concepts" or "signatures" (basically, compile-time interfaces) that would help out a lot, but nobody's driven one of those proposals to completion, as far as I know.
May 20 2018