On Thursday, 9 February 2017 at 05:40:01 UTC, Jonathan M Davis
wrote:
On Friday, February 03, 2017 14:43:01 Dominikus Dittes Scherkl
via Digitalmars-d wrote:
Any thoughts?
This is really cool, but I have a couple of concerns about this
and how it seems deficient in comparison to DIP 1005. I
mentioned it in Andrei's PR for this, but no one has responded
to my comment.
This first problem is UFCS. While this technique works great
for parameters or on types you want to list in the template
constraint, I don't see how it can work with UFCS. So, if you
have something like
auto func(alias pred, R)(R range)
if(isInputRange!R &&
is(typeof(pred(range.front)) == bool))
{...}
you can do
auto func(alias pred, R)(R range)
if(from!"std.range.primitives".isInputRange!R &&
is(typeof(pred(range.front)) == bool))
{...}
but you can't import std.range.primitives.front to make dynamic
arrays work, whereas with DIP 1005, you can just add the import
to the function declaration, and it will work without having to
tie the import to a specific symbol.
And while in many cases, you can just forgo UFCS - e.g.
auto func(P)(P param)
if(is(typeof(from!"std.algorithm".find(param)) == param))
{...}
that not only doesn't work with the range primitives, but it's
bad practice to use UFCS in the function body and not the
template constraint (since the semantics may not be the same),
meaning that not using UFCS in the template constraint means
not using it in the function body for anything that's in the
template constraint (which many folks won't like and others
won't understand, resulting in subtle bugs), and any case where
you want a function to work with types that define a function
as a member function as well as with types that use a free
function, you need to use UFCS and thus cannot choose to not
use it in the template constraint.
So, unless there's something that I don't understand about this
technique (which is definitely possible), it seems like it does
not work with UFCS and thus makes it considerably worse than
1005 for a lot of templated code, much as it would work great
for code that doesn't need UFCS.
The other problem is how much more verbose it is. With DIP
1005, you can do
with(import std.datetime)
auto foo(SysTime st1, SysTime st2, Duration d);
The import is only listed once, whereas with this technique,
you have to list it for each symbol. e.g.
auto foo(from!"std.datetime".SysTime st1,
from!"std.datetime".SysTime st2,
from!"std.datetime".Duration d);
The result is much more verbose, and if you have several
symbols that need imports between the return type, parameters,
and template constraint, you quickly end up with a lot of extra
text in the middle of your function signatures just because you
want to tie the imports to the functions that use them. With
DIP 1005, the imports are next to the function but separate
where they avoid the need for repeating imports and don't get
mixed into the middle of the function signature.
So, while the proposed technique is really cool and clever in
what it lets us do without actually altering the language, it
seems like it's quite a bit worse than DIP 1005. As such, I'm
inclined to argue that we should favor DIP 1005 over this
proposal, as cool as it is.
- Jonathan M Davis
It's a hack on top of a hack, but you can do something like this:
void test(T1, T2, alias isIntegral =
from!"std.traits".isIntegral,(T1 t1, T2 t2)
if (isIntegral!T1 && isIntegral!T2)
{
pragma(msg, isIntegral!T1);
pragma(msg, isIntegral!T2);
}
void main()
{
}
The only problem is that it allows a user to supply their own
value for isIntegral, which can lead to some weird error messages
and confusion if they don't know about this idiom.
Also it can't quite do UFCS, because you cannot use UFCS with
local symbols:
void test(T1, T2,
alias isIntegral = from!"std.traits".isIntegral,
alias chop = from!"std.string".chop)
(T1 t1, T2 t2)
if (isIntegral!T1 && isIntegral!T2 && T1.stringof.chop() ==
"ubyt") //Error: no property 'chop' for type 'string'
{
}