May 11, 2012
On Friday, 11 May 2012 at 18:18:21 UTC, Andrei Alexandrescu wrote:
> At the same time it clarifies, documents, and statistically verifies that you pass a sorted range. Also, D's binary search works with non-array ranges, but C#'s works only with arrays (which it assumes sorted only by convention).
>
> I think we copiously made the right call there.
>
> Andrei


Right, right, I understand all of that...

But just because the *reasons* are correct that doesn't mean it's more intuitive.

("Intuitive", by definition, means that you have a good chance of getting it right without needing to look it up. Binary searching in D failed this miserably for me.)
May 11, 2012
On Friday, 11 May 2012 at 18:25:17 UTC, Jonathan M Davis wrote:
>> Actually, that is a WAT even for somebody coming from C/C++.
>
> Really? That's pretty much exactly what I would have expected, and it would
> really suck if it returned a bool. It's like what you get with find on
> std::map, only it's a pointer instead of an iterator.
>
> - Jonathan M Davis


Again, try to see it from the perspective of a Python user, not a
C++ user.
May 11, 2012
On Friday, 11 May 2012 at 18:21:24 UTC, H. S. Teoh wrote:
> Templates are stencils for generating code. There's nothing confusing about that.


"Stencils for generating code"? _This_??! :O

  template hasMember(T, string name)
  { enum hasMember = __traits(hasMember, T, name); }


Imagine a new user's confusion when seeing something like this.
(Not sure I got it exactly right, but my point is there.)

May 11, 2012
On Fri, May 11, 2012 at 08:38:41PM +0200, Mehrdad wrote:
> On Friday, 11 May 2012 at 18:21:24 UTC, H. S. Teoh wrote:
> >Templates are stencils for generating code. There's nothing confusing about that.
> 
> 
> "Stencils for generating code"? _This_??! :O
> 
>   template hasMember(T, string name)
>   { enum hasMember = __traits(hasMember, T, name); }
> 
> 
> Imagine a new user's confusion when seeing something like this. (Not sure I got it exactly right, but my point is there.)

Yes, that's exactly how stencils work. You're essentially generating a declaration of the form:

	enum hasMember = ...;

where what's in the ... depends on the parameters you passed to the template. So for example, if T is say MyClass, and name is myfield, then the stencil stamps out:

	enum hasMember = __traits(hasMember, MyClass, myfield);

Now, bad __traits syntax aside (and I do agree with you that __traits() could've had more intuitive syntax), this is just a piece of code that depends on some compile-time knowledge. So if MyClass has a field called myfield, then the final code produced is just:

	enum hasMember = true;

Otherwise, the code produced is:

	enum hasMember = false;

You could've typed out the final code yourself, and the effect is exactly the same. The only difference is that capturing the process of producing this code inside a template allows you to do generic programming, that is, instead of checking each class for a particular member in every instance, you define a template that, in effect, replicates this process for you. And once you have this automation, you can build upon it -- whence comes templates that call other templates, function signatures constraints, etc..

If newbies had the proper introduction to the concept of templates (instead of being dunked in the deep end of the pool with signature constraints that use templates, nested templates, etc., with no prior warning), they wouldn't have such a hard time with them.


T

-- 
There are 10 kinds of people in the world: those who can count in binary, and those who can't.
May 11, 2012
On 11-05-2012 20:28, Mehrdad wrote:
> On Friday, 11 May 2012 at 18:16:02 UTC, Alex Rønne Petersen wrote:
>> On 11-05-2012 20:05, Mehrdad wrote:
>>> 1. "templates": they seem to take in just about anything, and return
>>> just about anything. They can be pretty confusing, especially when
>>> youhave to worry about overloading vs. specialization vs.
>>> constraints, or types vs. aliases...
>>
>> Right, D's template system is definitely inspired by C++, no argument
>> there. But in the vast majority of code, you really don't have to use
>> templates for everything.
>
> Not sure I agree.
>
> Templates are almost *impossible* to ignore when working with ranges.
>
>>> 2. Tuples (I'm referring to both tuple() and TypeTuple()) are very
>>> confusing if you don't happen to suddenly "get" them.
>> I don't know about that. They seemed simple enough to me.
>
> Me too, as a matter of fact.
>
> But they definitely aren't intuitive (just judge by the number of
> questions about them that pop up on StackOverflow or elsewhere...)

True, but I think what we lack is clear and concise documentation on them.

>
>>> 3. Ranges aren't confusing, but USING them certainly is.>
>> On the contrary, I actually think we're lacking proper documentation
>> on what ranges are.
>
> Yeah probably.
>
>>> Consider: In C#, you say Array.BinarySearch(arr) to search an array.
>>> It's VERY straightforward.
>>> In D, it's VERY CONFUSING
>> Library issue.
>
> Yeah, you got me there...
> But in practice it doesn't make a difference what the cause is, so much
> as what the effect is...

True. I'm just speaking from a purely language perspective. But D is a batteries-included language, so if the standard library has problems, you could arguably say D has problems as a whole...

>
>
>>> 6. The 'in' operator returns a pointer (instead of a boolean). Which
>>> is fine, except that it's not what you'd expect in any languages
>>> other than C/C++. i.e. Non-intuitive
>> I've always found it very intuitive actually... and it saves me from
>> having to write extra code to do the actual lookup (speed isn't even
>> the point here, I just want concise code).
>
> Yes, I agree, but consider that D users should NOT have to work with
> pointers to do something so basic, like testing to see if something is
> in an associative array!

I agree that the fact that it returns a *pointer* is probably not ideal. Maybe if D had proper pattern matching, it could have returned some kind of Option!T type or whatever...

>
> The mere fact that it uses a pointer makes it unintuitive to a large
> number of people coming from C#/Java/Python/etc.
>
>
>> But again, most code really won't be doing all that much low-level
>> metaprogramming.
>
> That's the hope, but I keep on finding myself putting __traits(compiles,
> ...) on template conditions and such...

I've actually found that std.traits and if () constraints on declarations tend to work for most of my cases. But YMMV and all that.

-- 
- Alex
May 11, 2012
> Just a note: I believe I *had* seen SortedRange in the docs, but I'd never realized there's something called assumeSorted() that I was supposed to call... so I was searching up and down for how to search an *arbitrary* container, not how to search something which was already pre-sorted for me.
> (In retrospect, I probably should've just coded binary search myself...)
> It's very counterintuitive to have to make a new object (or struct) just to do binary search on an array...

Maybe the documentation changed since, but currently the
documentation for SortedRange says:

"To construct a SortedRange from a range r that is known to be already sorted, use assumeSorted described below."

And there is assumeSorted in the example.
May 11, 2012
On Friday, 11 May 2012 at 18:05:58 UTC, Mehrdad wrote:
> 1. "templates": they seem to take in just about anything, and return just about anything. They can be pretty confusing, especially when you have to worry about overloading vs. specialization vs. constraints, or types vs. aliases...

I really have to disagree on this. I programmed in C++ for awhile and templates were always a huge stumbling block for me. The syntax didn't sync up for me and they were extremely difficult to implement well, which made any generic programming I attempted feel half-hearted (IMO).

When I started looking at D's templates, I was _afraid_ that it would be as much of a pain in the ass. After putting it off and shying away from it, I decided to dive in and discovered that they were significantly easier to work with ... and more importantly, it was nearly straightforward to implement them _well_. It's absolutely no problem specifying that data type with certain properties should use this function and everything else should use this other function. In C++, you have to learn complicated, arbitrary idioms such as SFINAE to get any serious work done at all ... and it looks like all of it was written in another language when you're done, making maintenance a nightmare.

It's not surprising that entire C++ books are dedicated to teaching the absurd complexities of C++ templates. If anything, my experience with C++ templates hindered me.

> 3. Ranges aren't confusing, but USING them certainly is.
>    ...
>    Consider: In C#, you say Array.BinarySearch(arr) to search an array.

There's definitely not enough info on how to get stuff done with ranges. I knew about assumeSorted and the fact that std.algorithm.sort returns a sorted range, but only because I carefully combed through the libraries looking for stuff like that.

That said, the D way is actually very well designed once you discover it. Like Andrei said, the code clarifies its intentions.


Hilariously, this style also makes the code look more magical. You want to say something is unique and, thus, can be legitimately made immutable? assumeUnique. Oh, you want to have a memoized version of funct? memoize!funct. I leave my friends wondering how D knew how to do all this stuff just by being "told" to. Magic API, that's how.

May 11, 2012
On 11-05-2012 20:34, Mehrdad wrote:
> On Friday, 11 May 2012 at 18:25:17 UTC, Jonathan M Davis wrote:
>>> Actually, that is a WAT even for somebody coming from C/C++.
>>
>> Really? That's pretty much exactly what I would have expected, and it
>> would
>> really suck if it returned a bool. It's like what you get with find on
>> std::map, only it's a pointer instead of an iterator.
>>
>> - Jonathan M Davis
>
>
> Again, try to see it from the perspective of a Python user, not a
> C++ user.

Speaking of which, 'in' on arrays.........

*hint hint*

-- 
- Alex
May 11, 2012
On Fri, May 11, 2012 at 8:55 PM, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:

> If newbies had the proper introduction to the concept of templates (instead of being dunked in the deep end of the pool with signature constraints that use templates, nested templates, etc., with no prior warning), they wouldn't have such a hard time with them.

I wrote a tutorial on templates, any comment is welcome:

https://github.com/PhilippeSigaud/D-templates-tutorial

Direct link to the pdf:

https://github.com/PhilippeSigaud/D-templates-tutorial/blob/master/dtemplates.pdf?raw=true

I need to get back to it, I was side-tracked by other projects and still have things to add to the doc.


Philippe
May 11, 2012
On Friday, 11 May 2012 at 18:53:57 UTC, H. S. Teoh wrote:
> On Fri, May 11, 2012 at 08:38:41PM +0200, Mehrdad wrote:
>> On Friday, 11 May 2012 at 18:21:24 UTC, H. S. Teoh wrote:
>> >Templates are stencils for generating code. There's nothing
>> >confusing about that.
>> 
>> 
>> "Stencils for generating code"? _This_??! :O
>> 
>>   template hasMember(T, string name)
>>   { enum hasMember = __traits(hasMember, T, name); }
>> 
>> 
>> Imagine a new user's confusion when seeing something like this.
>> (Not sure I got it exactly right, but my point is there.)
>
> Yes, that's exactly how stencils work. You're essentially generating a declaration of the form:
>
> 	enum hasMember = ...;
>
> <snip>


That's not how you see it when you're learning though.

It's more like, I can imagine someone asking these:

1. Why the heck do I see "hasMember" twice?
2. What does this have to do with enums?
3. Where is anything getting "returned"???
4. So you mean templates are THINGS?? I thought you needed a template SOMETHING, like a template struct, template function, etc...
5. What the heck is TypeTuple!()? Where's the blueprint?


etc.