www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Understand typeof trick

reply Joakim =?UTF-8?B?QnLDpG5uc3Ryw7Zt?= <notfornow dev.null.com> writes:
Hello,

In 
http://forum.dlang.org/post/ojawnpggfaxevqpmrdww forum.dlang.org 
Adam uses findSkip as an example and especially points out the "D 
idiom with is/typeof".

I'm not quite sure I understand it correctly. Please correct me 
if I have misunderstood anything regarding the idiom.

findSkip: 

bool findSkip
   (alias pred = "a == b", R1, R2)
   (ref R1 haystack, R2 needle)
     if (isForwardRange!R1 &&
         isForwardRange!R2 &&
         is(                        [C]
            typeof(                 [B]
                   binaryFun!pred(  [A]
                                  haystack.front, needle.front))));

[A]
Nothing special. findSkip's pred is passed on to binaryFun. 
binaryFun's constraints thus apply to findSkip's pred.


[B]
Evaluates to the function type "constructed" by binaryFun.

[C]
The is expression is true if A->B is valid "code".
It is used to convert any compiler errors to "false" (thus the 
constraint wouldn't be fulfilled).

Question:
I guess that binaryFun is used in the implementation of findSkip.
The reason for using this type of idiom is to avoid "compilation 
errors" to occur in the implementation when pred/R1/R2 is 
"funky". It "confuses" the user.
The idiom is thus used to move errors to the call site?
"D idiom: constraint error at call site"?
Dec 25 2015
next sibling parent reply anonymous <anonymous example.com> writes:
On 25.12.2015 13:10, Joakim Brännström wrote:
 In http://forum.dlang.org/post/ojawnpggfaxevqpmrdww forum.dlang.org Adam
 uses findSkip as an example and especially points out the "D idiom with
 is/typeof".

 I'm not quite sure I understand it correctly. Please correct me if I
 have misunderstood anything regarding the idiom.


 bool findSkip
    (alias pred = "a == b", R1, R2)
    (ref R1 haystack, R2 needle)
      if (isForwardRange!R1 &&
          isForwardRange!R2 &&
          is(                        [C]
             typeof(                 [B]
                    binaryFun!pred(  [A]
                                   haystack.front, needle.front))));

 [A]
 Nothing special. findSkip's pred is passed on to binaryFun. binaryFun's
 constraints thus apply to findSkip's pred.

Yup, and then the resulting function is called with arguments `haystack.front, needle.front`. The template instantiation, and the function call can fail compilation (more precisely: semantic analysis). In that case, A is marked as being "broken".
 [B]
 Evaluates to the function type "constructed" by binaryFun.
Almost. It evaluates to the type of the expression. The expression is a function call, so typeof evaluates to the return type of the generated function. If A has been marked broken, then B does not become a proper type, and it's marked "broken" as well.
 [C]
 The is expression is true if A->B is valid "code".
It's true if the argument, i.e. B, is a proper type. In particular, B must not be marked "broken". If B is "broken", then C is false. Any error messages that would usually be printed for the broken A/B are suppressed.
 It is used to convert any compiler errors to "false" (thus the
 constraint wouldn't be fulfilled).
Yes, but be aware that this only works on semantic errors, not on syntax errors. For example, `is(foo())` and `is(typeof(foo(); bar();))` don't give you false, but they error out in the syntax checking phase. That second one leads us to the longest form of the is-typeof idiom: `is(typeof({foo(); bar();}))`. Wrapping the code in a delegate so that it's an expression, which can be passed to typeof.
 Question:
 I guess that binaryFun is used in the implementation of findSkip.
Yeah, the constraint wouldn't make sense otherwise.
 The reason for using this type of idiom is to avoid "compilation errors"
 to occur in the implementation when pred/R1/R2 is "funky". It "confuses"
 the user.
 The idiom is thus used to move errors to the call site?
 "D idiom: constraint error at call site"?
I think Adam meant just the is(typeof(...)) thing itself, regardless of where it appears. Constraints are used to reject bad instantiations early on, yes. They're also used to distinguish different template "overloads".
Dec 25 2015
parent Joakim =?UTF-8?B?QnLDpG5uc3Ryw7Zt?= <notfornow dev.null.com> writes:
On Friday, 25 December 2015 at 14:55:04 UTC, anonymous wrote:
 On 25.12.2015 13:10, Joakim Brännström wrote:
 [B]
 Evaluates to the function type "constructed" by binaryFun.
Almost. It evaluates to the type of the expression. The expression is a function call, so typeof evaluates to the return type of the generated function.
Ahh, I missed this. The subtle difference between: int fun(uint x) { return 1; } pragma(msg, typeof(fun)); // -> int(uint x) pragma(msg, typeof(fun()); // -> int
 That second one leads us to the longest form of the is-typeof 
 idiom: `is(typeof({foo(); bar();}))`. Wrapping the code in a 
 delegate so that it's an expression, which can be passed to 
 typeof.
Nice, didn't know that was possible. I'll remember that. Thank you for the detailed answer.
Dec 25 2015
prev sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
Well, what I'd really want to document here's isn't necessarily 
the nitty-gritty of the idiom and why it is used (that's a thing 
for api authors, but these docs are targeted at api consumers), 
but more just what it actually means at a glance.

That line of code simply means "pred must be a function that 
takes two arguments of the same type as the front of needle and 
haystack"

The binaryFun helper means you can pass your own function, 
callable object, or string! The string is one of those "a == b" 
style things you sometimes see in Phobos.

I could write on that line of code for probably a full page from 
several perspectives - there's quite a lot going on there.

But the user of findSkip, when reading that doc, wants to simply 
know that constraint is about the predicate function, that it 
must take those arguments. Once you get used to the pattern, 
you'll know what it means, but first-time readers would surely 
appreciate a translation right there... and examples of what kind 
of compiler errors will result if you make a mistake.

(And I think it also must return bool, though the constraint 
doesn't say that... nor does the prose... nor do any of the 
examples get into it. Gah, so either I'm wrong or that's an 
undocumented requirement. is(typeof()) could check the return 
value though.)
Dec 25 2015