Re: implicit construction operator

2018-03-02 Thread Nick Treleaven via Digitalmars-d

On Monday, 26 February 2018 at 21:07:52 UTC, Meta wrote:
This is possible in the language today using the implicit class 
construction feature of runtime variadic arrays:


class VArray
{
Variant[] va;

this(T...)(T ts) { foreach(t; ts) { va ~= Variant(t); } }
}

void test(VArray ta...)

...

test(1, "asdf", false);


Problems:
* The author of `test` has to specifically design support for 
this to work. `test` might be in a library outside the control of 
the programmer.
* `test` might be part of a template such as a type constructor 
taking a type as a template parameter - you can't pass e.g. 
VArray as the type because `test` wouldn't have the ... ellipsis 
- if it did then `test` would be variadic when the type was an 
array (which would be inconsistent).
* If there's an overload `test(int,string,bool)` then implicit 
construction will no longer work - this is a general problem with 
the (Aggregate arg...) feature.


That said, there is exactly 1 case where I really, really want 
some kind of implicit conversion:


struct Success {}
struct Error { string msg; }

alias Result = Algebraic!(Success, Error);

Result connectToHost(IPAddress host)
{
//do some stuff
if (operationSucceeded)
{
return Success();
}
else
{
return Error(statusMessage);
}
}

This currently doesn't work, and you instead have to return 
Result(Success()) and

Result(Error(statusMessage)).


This is a great example. If we had `@implicit this(T v)` it could 
be restricted to accepting literals, struct constructor call 
expressions and new expressions for `v`. That would allow support 
for your example, plus allow:


void f(Result);
f(Success());
f(Error(statusMessage));

This should only work for an exactly matching implicit 
constructor. Suppose we have a type Q that has an implicit 
constructor for type P, and P has an implicit constructor for int:


void g(Q);
g(P()); // OK
g(5); // error, int is not implicitly convertible to Q

For g(5), the compiler doesn't consider P's `@implicit 
this(int)`, it only matches an implicit constructor of Q when the 
argument type is P exactly.


Re: implicit construction operator

2018-02-28 Thread Nick Treleaven via Digitalmars-d

On Monday, 26 February 2018 at 21:36:49 UTC, ketmar wrote:

aliak wrote:

It makes libraries *much* more intuitive and expressive (C++ 
just got it wrong by choosing the wrong default). If you allow 
library authors to opt in instead of opt out then it becomes a 
conscious decision for one.


library authors can create overloads for various argument 
types. with `foo(T...) (T args)` it is possible to generate 
conversions with CTFE. so we *already* have such feature.


No, we do not. Presumably you're not suggesting every function 
ever written has to be a template, and has to manually support 
every custom type ever written's chosen implicit conversions?


Re: implicit construction operator

2018-02-27 Thread Rubn via Digitalmars-d

On Monday, 26 February 2018 at 19:32:44 UTC, ketmar wrote:

WebFreak001 wrote:


Now before you would have only been able to do this:

---
Nullable!Foo a;
foo(a, Nullable!int(5));
---

but now you should also be able to do:

---
Nullable!Foo x = null;
Nullable!Foo y = 5;

foo(null, 5);


please no. such unobvious type conversions goes out of control 
really fast. there is a reason why D doesn't have such thing, 
this is not an oversight, but a design decision.


The bigger mistake is D allowing implicit conversion when a type 
is declared, with no way to disable it when it wasn't asked for.


struct SomeStruct
{
this(int)
{
}
}

SomeStruct value = 10; // I didn't want this

int oops();
SomeStruct value2 = oops(); // Didn't want this either.

SomeStruct ok() { return SomeStruct(10); }
SomeStruct value2 = ok(); // this is what I wanted

On the other hand I want to create a "null" like value for my own 
types. This currently isn't possible in D, because there is no 
implicit conversion allowed for structs. You can be explicit and 
declare every type, but "null" wouldn't be as convenient if you 
had to specify the type it has to convert to every time you 
wanted to use it. I can only imagine the hell hole DMD's source 
code would be if that was the case, with its excessive use of 
null.


You can make the same argument about mixin's, it's possible to 
create some really nasty code with it. That is unreadable and 
unmaintainable, it's especially a pain in the ass when you have 
to debug such code. Yet, there it is in D. Most useful features 
have the capability of being misused, that doesn't mean we 
shouldn't use them.


Re: implicit construction operator

2018-02-27 Thread TheFlyingFiddle via Digitalmars-d

On Monday, 26 February 2018 at 23:33:48 UTC, H. S. Teoh wrote:
Not really a big deal (and auto kind of ruins it) but it would 
make stuff consistent between user types and built in ones.


Not sure what you mean here.  In a user type, if opBinary!"/" 
returns an int, then you still have the same problem.


Yes, your right, did not really think that through. Also if you 
overload opAssign you get implicit conversions in assign 
expressions...











Re: implicit construction operator

2018-02-27 Thread aliak via Digitalmars-d

On Tuesday, 27 February 2018 at 13:36:30 UTC, ketmar wrote:

aliak wrote:


On Monday, 26 February 2018 at 21:34:21 UTC, ketmar wrote:

aliak wrote:

And if that's also a no no, how about char -> int. Or int -> 
float? Is ok?


no, it is not ok. it was a mistake. it is now too late to fix 
it, but we can avoid doing more of the same kind.


Oops, yeah, ok, D char ...  char -> int yeah mistake.

int -> long even?


can you see a difference between *charactes* and *integers*? i 
can.


Yes I can. That's why I said it was a mistake. A BigInt -> Int 
would also be integers. But a f(BigInt i) cannot be called with 
f(3) where as f(long i) can. So not sure what your point is.





Re: implicit construction operator

2018-02-27 Thread ketmar via Digitalmars-d

aliak wrote:


On Monday, 26 February 2018 at 21:34:21 UTC, ketmar wrote:

aliak wrote:

And if that's also a no no, how about char -> int. Or int -> float? Is 
ok?


no, it is not ok. it was a mistake. it is now too late to fix it, but we 
can avoid doing more of the same kind.


Oops, yeah, ok, D char ...  char -> int yeah mistake.

int -> long even?


can you see a difference between *charactes* and *integers*? i can.


Re: implicit construction operator

2018-02-27 Thread biozic via Digitalmars-d

On Monday, 26 February 2018 at 19:25:06 UTC, WebFreak001 wrote:
hi, I had an idea from using some C# which I think would be 
really cool in D. Basically allow modifying implicit 
construction of a type. Right now we only have struct 
opAssign/constructor implicit conversion, but this addition 
would also add it to classes and make it even more convenient.

[...]

What's your opinion on this? I think it is really useful to 
have when interoping with scripting languages or doing 
(de)serialization or for these attribute wrappers which are 
there because of no function attribute UDAs. I was looking into 
implementing this to dmd myself but the code on the implicit 
cast checks looks a bit hardcoded for the types that are in 
right now and not really suitable to implement this, but maybe 
someone with more knowledge about dmd internals can do this 
with ease.


This is only a single direction T -> Wrapper and I think this 
would actually be enough, but it would maybe be possible to add 
@implicit to opCast in the future.


Such implicit conversion was once 'on the table', see this 
thread: 
https://forum.dlang.org/post/lv9iqs$1jp3$1...@digitalmars.com.


I also think that controlled implicit conversion would be 
sometimes useful, notably with wrapper structs. For now, the only 
solution is to use templated functions (in some cases, all the 
code using wrapper structs has to be templated...). Pragmatism 
and cautious design sometimes defeat 'EVIL'.





Re: implicit construction operator

2018-02-26 Thread aliak via Digitalmars-d

On Monday, 26 February 2018 at 21:36:49 UTC, ketmar wrote:

aliak wrote:

It makes libraries *much* more intuitive and expressive (C++ 
just got it wrong by choosing the wrong default). If you allow 
library authors to opt in instead of opt out then it becomes a 
conscious decision for one.


library authors can create overloads for various argument 
types. with `foo(T...) (T args)` it is possible to generate 
conversions with CTFE. so we *already* have such feature.


Touche.


Re: implicit construction operator

2018-02-26 Thread aliak via Digitalmars-d

On Monday, 26 February 2018 at 21:34:21 UTC, ketmar wrote:

aliak wrote:

And if that's also a no no, how about char -> int. Or int -> 
float? Is ok?


no, it is not ok. it was a mistake. it is now too late to fix 
it, but we can avoid doing more of the same kind.


Oops, yeah, ok, D char ...  char -> int yeah mistake.

int -> long even?


Re: implicit construction operator

2018-02-26 Thread H. S. Teoh via Digitalmars-d
On Mon, Feb 26, 2018 at 09:45:03PM +, TheFlyingFiddle via Digitalmars-d 
wrote:
> On Monday, 26 February 2018 at 21:30:09 UTC, aliak wrote:
> > On Monday, 26 February 2018 at 19:32:44 UTC, ketmar wrote:
> > > WebFreak001 wrote:
> > And if that's also a no no, how about char -> int. Or int -> float?
> > Is ok?
> > 
> > Maybe there're some valid arguments, to disallow it *completely*
> > though?
> > 
> > Cheers
> 
> I would be very happy if char -> int and int -> float was not implicit.

Yeah, implicit char -> int is totally evil, and should never have gotten
into the language in the first place. To wit:

void foo(dchar ch) {
backup_files();
}
void foo(byte b) {
format_disk();
}
foo('a');   // guess which one gets called.


> This has happened to me enough times that i remember it:
> 
> float a = some_int / some_other_int; //ops i don't think this was what I 
> wanted.
> constrast
> float a = some_int.to!float / some_other_int.to!float; //ugly but at-least 
> its clear.

Actually, since operations involving float are "infectious", all you
need is:

float a = some_int.to!float / some_other_int;


> Not really a big deal (and auto kind of ruins it) but it would make
> stuff consistent between user types and built in ones.

Not sure what you mean here.  In a user type, if opBinary!"/" returns an
int, then you still have the same problem.


T

-- 
He who laughs last thinks slowest.


Re: implicit construction operator

2018-02-26 Thread TheFlyingFiddle via Digitalmars-d

On Monday, 26 February 2018 at 21:30:09 UTC, aliak wrote:

On Monday, 26 February 2018 at 19:32:44 UTC, ketmar wrote:

WebFreak001 wrote:
And if that's also a no no, how about char -> int. Or int -> 
float? Is ok?


Maybe there're some valid arguments, to disallow it 
*completely* though?


Cheers


I would be very happy if char -> int and int -> float was not 
implicit.


This has happened to me enough times that i remember it:

float a = some_int / some_other_int; //ops i don't think this was 
what I wanted.

constrast
float a = some_int.to!float / some_other_int.to!float; //ugly but 
at-least its clear.


Not really a big deal (and auto kind of ruins it) but it would 
make stuff consistent between user types and built in ones.


Re: implicit construction operator

2018-02-26 Thread ketmar via Digitalmars-d

aliak wrote:

It makes libraries *much* more intuitive and expressive (C++ just got it 
wrong by choosing the wrong default). If you allow library authors to opt 
in instead of opt out then it becomes a conscious decision for one.


library authors can create overloads for various argument types. with 
`foo(T...) (T args)` it is possible to generate conversions with CTFE. so 
we *already* have such feature.


Re: implicit construction operator

2018-02-26 Thread aliak via Digitalmars-d

On Monday, 26 February 2018 at 19:32:44 UTC, ketmar wrote:

WebFreak001 wrote:


Now before you would have only been able to do this:

---
Nullable!Foo a;
foo(a, Nullable!int(5));
---

but now you should also be able to do:

---
Nullable!Foo x = null;
Nullable!Foo y = 5;

foo(null, 5);


please no. such unobvious type conversions goes out of control 
really fast. there is a reason why D doesn't have such thing, 
this is not an oversight, but a design decision.


How if you don't mind me asking? Seems like many features can go 
out of control if used incorrectly, but that doesn't mean they're 
a bad idea. I can understand they're a slippery slope, but it 
seems some people think it's a completely bad idea.


It makes libraries *much* more intuitive and expressive (C++ just 
got it wrong by choosing the wrong default). If you allow library 
authors to opt in instead of opt out then it becomes a conscious 
decision for one.


But at the very least, shouldn't we put some thought in to 
allowing it for inherent sub-type relationships. Where an alias 
this is used, there's probably such a relationship. BigInt to int 
is another one. Algebraic types as mentioned falls under that 
category as well.


And if that's also a no no, how about char -> int. Or int -> 
float? Is ok?


Maybe there're some valid arguments, to disallow it *completely* 
though?


Cheers




Re: implicit construction operator

2018-02-26 Thread ketmar via Digitalmars-d

aliak wrote:


And if that's also a no no, how about char -> int. Or int -> float? Is ok?


no, it is not ok. it was a mistake. it is now too late to fix it, but we 
can avoid doing more of the same kind.


Re: implicit construction operator

2018-02-26 Thread Meta via Digitalmars-d

On Monday, 26 February 2018 at 19:25:06 UTC, WebFreak001 wrote:

Now this would be really useful for Variant:

---
struct Variant {
this(U)(U value) @implicit { ... }
}

void bar(Variant x, Variant y) {}

Variant[] myObjects = [1, 2, "abc", new Node()];
Variant a = 4;
bar(4, "asdf");
---


This is possible in the language today using the implicit class 
construction feature of runtime variadic arrays:


class VArray
{
Variant[] va;

this(T...)(T ts) { foreach(t; ts) { va ~= Variant(t); } }
}

void test(VArray ta...)
{
foreach (v; ta.va)
{
writeln(v.type);
}
}

void main()
{
test(1, "asdf", false);
}




What's your opinion on this?


This is a very slippery slope to fall down. Even `alias this` is 
pushing the limit of what I think we should allow.


That said, there is exactly 1 case where I really, really want 
some kind of implicit conversion:


struct Success {}
struct Error { string msg; }

alias Result = Algebraic!(Success, Error);

Result connectToHost(IPAddress host)
{
//do some stuff
if (operationSucceeded)
{
return Success();
}
else
{
return Error(statusMessage);
}
}

This currently doesn't work, and you instead have to return 
Result(Success()) and
Result(Error(statusMessage)). I would love to have some way of 
implicitly constructing an Algebraic from any of the possible 
underlying types. It would bring us very close (if not all the 
way) to having Algebraic work identically to sum types in other 
languages such as Rust, Swift, Haskell, etc. Having to explicitly 
wrapping the values in an Algebraic doesn't seem like a big deal, 
but it makes it really annoying to use it in everyday code.


Re: implicit construction operator

2018-02-26 Thread ketmar via Digitalmars-d

WebFreak001 wrote:


Now before you would have only been able to do this:

---
Nullable!Foo a;
foo(a, Nullable!int(5));
---

but now you should also be able to do:

---
Nullable!Foo x = null;
Nullable!Foo y = 5;

foo(null, 5);


please no. such unobvious type conversions goes out of control really fast. 
there is a reason why D doesn't have such thing, this is not an oversight, 
but a design decision.