www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Add support implicit conversion between types

reply "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
Do you have any plans to support implicit conversion between 
types?

I have some code like this:

struct Foo
{
	this(int i)
	{
		//do something useful
	}
}

void bar(Foo f)
{
	//do something else
}

void main()
{
	Foo f = 5;//works
	
	bar(f);//works
	
	bar(Foo(5));//works
	
	bar(5);//Error: function app.bar (Foo f) is not callable using
argument types (int)
}

So, D can't use constructor to convert "int" to "Foo" implicitly.
Can we add "implicit" keyword to allow do this:

struct Foo
{
	implicit this(int i)
	{
		//do something useful
	}
}

C++ allows this, but have "explicit" keyword.

implicit and explicit cases.
Sep 06 2013
next sibling parent Benjamin Thaut <code benjamin-thaut.de> writes:
Am 06.09.2013 12:33, schrieb ilya-stromberg:
 Do you have any plans to support implicit conversion between types?

 I have some code like this:

 struct Foo
 {
      this(int i)
      {
          //do something useful
      }
 }

 void bar(Foo f)
 {
      //do something else
 }

 void main()
 {
      Foo f = 5;//works

      bar(f);//works

      bar(Foo(5));//works

      bar(5);//Error: function app.bar (Foo f) is not callable using
 argument types (int)
 }

 So, D can't use constructor to convert "int" to "Foo" implicitly.
 Can we add "implicit" keyword to allow do this:

 struct Foo
 {
      implicit this(int i)
      {
          //do something useful
      }
 }

 C++ allows this, but have "explicit" keyword.

 and explicit cases.
I also made almost the same suggestion some time ago. I would support a feature like this. -- Kind Regards Benjamin Thaut
Sep 06 2013
prev sibling next sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Friday, 6 September 2013 at 10:33:07 UTC, ilya-stromberg wrote:
 Do you have any plans to support implicit conversion between 
 types?
Implicit conversions open up a whole can of worms for the sake of a small amount of convenience. I'm not sure it's a fair trade.
Sep 06 2013
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Friday, 6 September 2013 at 11:04:31 UTC, Peter Alexander 
wrote:
 Implicit conversions open up a whole can of worms for the sake 
 of a small amount of convenience.

 I'm not sure it's a fair trade.
Agreed. It was quite common source of troubles in my C++ days and have never offered much convenience.
Sep 06 2013
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 06.09.2013 13:27, schrieb Dicebot:
 On Friday, 6 September 2013 at 11:04:31 UTC, Peter Alexander wrote:
 Implicit conversions open up a whole can of worms for the sake of a
 small amount of convenience.

 I'm not sure it's a fair trade.
Agreed. It was quite common source of troubles in my C++ days and have never offered much convenience.
Its only a source of troubles in C++ because it is the default behavior. But if you design a library it can make the usage of your api easier and also you have a few more options to stay backwards compatible with your old api.
Sep 06 2013
next sibling parent "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 11:32:11 UTC, Benjamin Thaut wrote:
 Am 06.09.2013 13:27, schrieb Dicebot:
 On Friday, 6 September 2013 at 11:04:31 UTC, Peter Alexander 
 wrote:
 Implicit conversions open up a whole can of worms for the 
 sake of a
 small amount of convenience.

 I'm not sure it's a fair trade.
Agreed. It was quite common source of troubles in my C++ days and have never offered much convenience.
Its only a source of troubles in C++ because it is the default behavior. But if you design a library it can make the usage of your api easier and also you have a few more options to stay backwards compatible with your old api.
Totally agree. The default implicit conversion in C++ is a mistake. The conversion must be explicit by default (like in D now). The programmer should use implicit conversion ONLY IF IT SAFE (like ftom "int" to "long"). For any other cases programmer should use explicit conversion (default). conversions by default and ability to explicitly add implicit conversion. And I never had problems with it.
Sep 06 2013
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Friday, 6 September 2013 at 11:32:11 UTC, Benjamin Thaut wrote:
 Its only a source of troubles in C++ because it is the default 
 behavior. But if you design a library it can make the usage of 
 your api easier and also you have a few more options to stay 
 backwards compatible with your old api.
It's not just that single arg constructors are by default implicit. Implicit conversions complicate things, like template argument deduction and overload resolution.
Sep 06 2013
prev sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Friday, 6 September 2013 at 11:32:11 UTC, Benjamin Thaut wrote:
 Its only a source of troubles in C++ because it is the default 
 behavior. But if you design a library it can make the usage of 
 your api easier and also you have a few more options to stay 
 backwards compatible with your old api.
Probably. But what is the gain? `foo(Foo(5))` looks better than `foo(5)` to me in every possible way. For example, use case that justifies operator overloading (despite the danger) in my eyes is ability to replace built-in types with custom ones. What is the similar rationale for implicit conversion?
Sep 06 2013
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 06.09.2013 15:01, schrieb Dicebot:
 On Friday, 6 September 2013 at 11:32:11 UTC, Benjamin Thaut wrote:
 Its only a source of troubles in C++ because it is the default
 behavior. But if you design a library it can make the usage of your
 api easier and also you have a few more options to stay backwards
 compatible with your old api.
Probably. But what is the gain? `foo(Foo(5))` looks better than `foo(5)` to me in every possible way. For example, use case that justifies operator overloading (despite the danger) in my eyes is ability to replace built-in types with custom ones. What is the similar rationale for implicit conversion?
Try implementing a custom string class in D that does not depend on the GC and you will know. Your code will be littered with explict constructions of strings, which makes it look totally ugly.
Sep 06 2013
parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Friday, 6 September 2013 at 13:15:21 UTC, Benjamin Thaut wrote:
 Am 06.09.2013 15:01, schrieb Dicebot:
 Probably. But what is the gain? `foo(Foo(5))` looks better 
 than `foo(5)`
 to me in every possible way.

 For example, use case that justifies operator overloading 
 (despite the
 danger) in my eyes is ability to replace built-in types with 
 custom
 ones. What is the similar rationale for implicit conversion?
Try implementing a custom string class in D that does not depend on the GC and you will know. Your code will be littered with explict constructions of strings, which makes it look totally ugly.
Could you not just implement a .str property that does the conversion? Surely littering code with .str isn't too ugly? I understand the desire for it, but implicit coercions truly are evil.
Sep 07 2013
prev sibling next sibling parent reply "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 13:01:14 UTC, Dicebot wrote:
 For example, use case that justifies operator overloading 
 (despite the danger) in my eyes is ability to replace built-in 
 types with custom ones. What is the similar rationale for 
 implicit conversion?
For exaple, for generic code: T factorial(T)(T number) { return number <= 1 ? 1 : number * factorial!T(number - 1); } void main() { //works: factorial!int(5); //doesn't work: factorial!BigInt(5); } It can be critical for more complex cases, when you call one generic function from another one, like this: unittest { alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, BigInt) IntegralTypeList; foreach(T; IntegralTypeList) { assert(factorial!T(3) == 6);//Error: factorial (BigInt number) is not callable using argument types (int) } }
Sep 06 2013
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Friday, 6 September 2013 at 13:21:44 UTC, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 13:01:14 UTC, Dicebot wrote:
 For example, use case that justifies operator overloading 
 (despite the danger) in my eyes is ability to replace built-in 
 types with custom ones. What is the similar rationale for 
 implicit conversion?
For exaple, for generic code: ...
So, what essentially is needed, is ability to implicitly convert literals of built-in types to user types, not any possible implicit conversion? I think allowing it with such restrictions can be reasonably clean.
Sep 06 2013
parent reply "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 13:30:26 UTC, Dicebot wrote:
 So, what essentially is needed, is ability to implicitly 
 convert literals of built-in types to user types, not any 
 possible implicit conversion?

 I think allowing it with such restrictions can be reasonably 
 clean.
Yes, the ability to implicitly convert literals of built-in types to user types is REALLY needed. The 2-nd error from "factorial" example import std.bigint; void main() { assert(factorial!BigInt(BigInt(3)) == 6); //Error: incompatible types for ((1) : (number.opBinary(factorial(number.opBinary(1))))): 'int' and 'BigInt' } The correct factorial implementation is: T factorial(T)(T number) { enum T one = 1; return number <= one ? one : number * factorial!T(number - one); } It's complicated, how do you think?
 not any possible implicit conversion?
I didn't think about yet. May be you are right, but I think it can be also useful. For example, if you can't change user-defined type.
Sep 06 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 6 September 2013 at 13:50:25 UTC, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 13:30:26 UTC, Dicebot wrote:
 So, what essentially is needed, is ability to implicitly 
 convert literals of built-in types to user types, not any 
 possible implicit conversion?

 I think allowing it with such restrictions can be reasonably 
 clean.
Yes, the ability to implicitly convert literals of built-in types to user types is REALLY needed. The 2-nd error from "factorial" example import std.bigint; void main() { assert(factorial!BigInt(BigInt(3)) == 6); //Error: incompatible types for ((1) : (number.opBinary(factorial(number.opBinary(1))))): 'int' and 'BigInt' } The correct factorial implementation is: T factorial(T)(T number) { enum T one = 1; return number <= one ? one : number * factorial!T(number - one); }
Or just: //---- import std.bigint; T factorial(T)(T number) { return number <= 1 ? T(1) : number * factorial!T(number - 1); } //---- The problem though is that this requires "uniform construction", which we don't have yet: http://d.puremagic.com/issues/show_bug.cgi?id=9112
Sep 06 2013
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Sep 06, 2013 at 03:21:42PM +0200, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 13:01:14 UTC, Dicebot wrote:
For example, use case that justifies operator overloading (despite
the danger) in my eyes is ability to replace built-in types with
custom ones. What is the similar rationale for implicit
conversion?
For exaple, for generic code: T factorial(T)(T number) { return number <= 1 ? 1 : number * factorial!T(number - 1); } void main() { //works: factorial!int(5); //doesn't work: factorial!BigInt(5); }
I thought the usual D idiom was to write factorial(5) and factorial(BigInt(5)) and let the compiler figure out which template instance you wanted?
 It can be critical for more complex cases, when you call one generic
 function from another one, like this:
 
 unittest
 {
    alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long,
 ulong, BigInt) IntegralTypeList;
 
    foreach(T; IntegralTypeList)
    {
       assert(factorial!T(3) == 6);//Error: factorial (BigInt number)
 is not callable using argument types (int)
You could just write factorial(T(3)) ? T -- Prosperity breeds contempt, and poverty breeds consent. -- Suck.com
Sep 06 2013
parent reply "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 14:26:17 UTC, H. S. Teoh wrote:
 I thought the usual D idiom was to write factorial(5) and
 factorial(BigInt(5)) and let the compiler figure out which 
 template
 instance you wanted?
Yes, but it isn't always possible.
 It can be critical for more complex cases, when you call one 
 generic
 function from another one, like this:
 
 unittest
 {
    alias TypeTuple!(byte, ubyte, short, ushort, int, uint, 
 long,
 ulong, BigInt) IntegralTypeList;
 
    foreach(T; IntegralTypeList)
    {
       assert(factorial!T(3) == 6);//Error: factorial (BigInt 
 number)
 is not callable using argument types (int)
You could just write factorial(T(3)) ?
No, I have the error: Error: function expected before (), not byte of type byte Error: function expected before (), not ubyte of type ubyte Error: function expected before (), not short of type short Error: function expected before (), not ushort of type ushort Error: function expected before (), not int of type int Error: function expected before (), not uint of type uint Error: function expected before (), not long of type long Error: function expected before (), not ulong of type ulong As monarch_dodra pointed above, we haven't got "uniform construction" support.
Sep 06 2013
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Friday, 6 September 2013 at 15:14:50 UTC, ilya-stromberg wrote:
 No, I have the error:
 Error: function expected before (), not byte of type byte
 Error: function expected before (), not ubyte of type ubyte
 Error: function expected before (), not short of type short
 Error: function expected before (), not ushort of type ushort
 Error: function expected before (), not int of type int
 Error: function expected before (), not uint of type uint
 Error: function expected before (), not long of type long
 Error: function expected before (), not ulong of type ulong

 As monarch_dodra pointed above, we haven't got "uniform 
 construction" support.
Yeah, this one, contrary, seems to be really crucial feature lacking.
Sep 06 2013
parent "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 15:47:27 UTC, Dicebot wrote:
 On Friday, 6 September 2013 at 15:14:50 UTC, ilya-stromberg 
 wrote:
 As monarch_dodra pointed above, we haven't got "uniform 
 construction" support.
Yeah, this one, contrary, seems to be really crucial feature lacking.
Yes, I agree. It solves problem of the difference between built-in and user-defined types for explicit cast.
Sep 06 2013
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Sep 06, 2013 at 05:14:48PM +0200, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 14:26:17 UTC, H. S. Teoh wrote:
I thought the usual D idiom was to write factorial(5) and
factorial(BigInt(5)) and let the compiler figure out which template
instance you wanted?
Yes, but it isn't always possible.
It can be critical for more complex cases, when you call one generic
function from another one, like this:

unittest
{
   alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long,
ulong, BigInt) IntegralTypeList;

   foreach(T; IntegralTypeList)
   {
      assert(factorial!T(3) == 6);//Error: factorial (BigInt
number)
is not callable using argument types (int)
You could just write factorial(T(3)) ?
No, I have the error: Error: function expected before (), not byte of type byte Error: function expected before (), not ubyte of type ubyte Error: function expected before (), not short of type short Error: function expected before (), not ushort of type ushort Error: function expected before (), not int of type int Error: function expected before (), not uint of type uint Error: function expected before (), not long of type long Error: function expected before (), not ulong of type ulong As monarch_dodra pointed above, we haven't got "uniform construction" support.
Hmm, I see. This is an unfortunate limitation. In C++, writing int(123) actually works. Looks like D is lacking in this area. :-( T -- Answer: Because it breaks the logical sequence of discussion. Question: Why is top posting bad?
Sep 06 2013
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
06-Sep-2013 21:05, H. S. Teoh пишет:
 On Fri, Sep 06, 2013 at 05:14:48PM +0200, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 14:26:17 UTC, H. S. Teoh wrote:
 I thought the usual D idiom was to write factorial(5) and
 factorial(BigInt(5)) and let the compiler figure out which template
 instance you wanted?
Yes, but it isn't always possible.
 It can be critical for more complex cases, when you call one generic
 function from another one, like this:

 unittest
 {
    alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long,
 ulong, BigInt) IntegralTypeList;

    foreach(T; IntegralTypeList)
    {
       assert(factorial!T(3) == 6);//Error: factorial (BigInt
 number)
 is not callable using argument types (int)
You could just write factorial(T(3)) ?
No, I have the error: Error: function expected before (), not byte of type byte Error: function expected before (), not ubyte of type ubyte Error: function expected before (), not short of type short Error: function expected before (), not ushort of type ushort Error: function expected before (), not int of type int Error: function expected before (), not uint of type uint Error: function expected before (), not long of type long Error: function expected before (), not ulong of type ulong As monarch_dodra pointed above, we haven't got "uniform construction" support.
Hmm, I see. This is an unfortunate limitation. In C++, writing int(123) actually works. Looks like D is lacking in this area. :-(
I swear I've seen a pull request that enables it. -- Dmitry Olshansky
Sep 06 2013
parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2013-09-06, 19:12, Dmitry Olshansky wrote:

 06-Sep-2013 21:05, H. S. Teoh =D0=BF=D0=B8=D1=88=D0=B5=D1=82:
 On Fri, Sep 06, 2013 at 05:14:48PM +0200, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 14:26:17 UTC, H. S. Teoh wrote:
 I thought the usual D idiom was to write factorial(5) and
 factorial(BigInt(5)) and let the compiler figure out which template=
 instance you wanted?
Yes, but it isn't always possible.
 It can be critical for more complex cases, when you call one gener=
ic
 function from another one, like this:

 unittest
 {
    alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long,
 ulong, BigInt) IntegralTypeList;

    foreach(T; IntegralTypeList)
    {
       assert(factorial!T(3) =3D=3D 6);//Error: factorial (BigInt
 number)
 is not callable using argument types (int)
You could just write factorial(T(3)) ?
No, I have the error: Error: function expected before (), not byte of type byte Error: function expected before (), not ubyte of type ubyte Error: function expected before (), not short of type short Error: function expected before (), not ushort of type ushort Error: function expected before (), not int of type int Error: function expected before (), not uint of type uint Error: function expected before (), not long of type long Error: function expected before (), not ulong of type ulong As monarch_dodra pointed above, we haven't got "uniform construction" support.
Hmm, I see. This is an unfortunate limitation. In C++, writing int(12=
3)
 actually works. Looks like D is lacking in this area. :-(
I swear I've seen a pull request that enables it.
This one? https://github.com/D-Programming-Language/dmd/pull/1356 It seems to be on its way. -- = Simen
Sep 07 2013
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Sep 06, 2013 at 10:05:47AM -0700, H. S. Teoh wrote:
 On Fri, Sep 06, 2013 at 05:14:48PM +0200, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 14:26:17 UTC, H. S. Teoh wrote:
I thought the usual D idiom was to write factorial(5) and
factorial(BigInt(5)) and let the compiler figure out which template
instance you wanted?
Yes, but it isn't always possible.
It can be critical for more complex cases, when you call one generic
function from another one, like this:

unittest
{
   alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long,
ulong, BigInt) IntegralTypeList;

   foreach(T; IntegralTypeList)
   {
      assert(factorial!T(3) == 6);//Error: factorial (BigInt
number)
is not callable using argument types (int)
You could just write factorial(T(3)) ?
No, I have the error: Error: function expected before (), not byte of type byte Error: function expected before (), not ubyte of type ubyte Error: function expected before (), not short of type short Error: function expected before (), not ushort of type ushort Error: function expected before (), not int of type int Error: function expected before (), not uint of type uint Error: function expected before (), not long of type long Error: function expected before (), not ulong of type ulong As monarch_dodra pointed above, we haven't got "uniform construction" support.
Hmm, I see. This is an unfortunate limitation. In C++, writing int(123) actually works. Looks like D is lacking in this area. :-(
[...] Hmm, could this be a possible (though somewhat ugly) workaround? foreach (T; IntegralTypeList) { assert(factorial(to!T(3) == 6)); } AFAIK, if T==int, then std.conv.to should simply alias itself away. And it *should* be able to handle ctors that take the requisite type, I think. T -- "The number you have dialed is imaginary. Please rotate your phone 90 degrees and try again."
Sep 06 2013
parent reply "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 17:15:03 UTC, H. S. Teoh wrote:
 Hmm, could this be a possible (though somewhat ugly) workaround?

 	foreach (T; IntegralTypeList)
 	{
 		assert(factorial(to!T(3) == 6));
 	}

 AFAIK, if T==int, then std.conv.to should simply alias itself 
 away. And
 it *should* be able to handle ctors that take the requisite 
 type, I
 think.
I use: foreach (T; IntegralTypeList) { assert(factorial(cast(T)3) == 6)); } I works, but looks a little strange. I belive that compiler should optimise this (do nothing), but I am not sure.
Sep 06 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Sep 06, 2013 at 07:25:21PM +0200, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 17:15:03 UTC, H. S. Teoh wrote:
Hmm, could this be a possible (though somewhat ugly) workaround?

	foreach (T; IntegralTypeList)
	{
		assert(factorial(to!T(3) == 6));
	}

AFAIK, if T==int, then std.conv.to should simply alias itself away.
And it *should* be able to handle ctors that take the requisite type,
I think.
I use: foreach (T; IntegralTypeList) { assert(factorial(cast(T)3) == 6)); } I works, but looks a little strange. I belive that compiler should optimise this (do nothing), but I am not sure.
I checked the disassembly for cast(T)5 for T==int and T==BigInt. Seems the compiler is doing the "right thing" in both cases. For T==int, the compiler just loads 5 directly into the register; for T==BigInt, it calls the BigInt ctor with 5 as parameter to construct an instance of BigInt. So, this seems to work, even though it looks ugly. T -- A linguistics professor was lecturing to his class one day. "In English," he said, "A double negative forms a positive. In some languages, though, such as Russian, a double negative is still a negative. However, there is no language wherein a double positive can form a negative." A voice from the back of the room piped up, "Yeah, yeah."
Sep 06 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Friday, 6 September 2013 at 13:01:14 UTC, Dicebot wrote:
 On Friday, 6 September 2013 at 11:32:11 UTC, Benjamin Thaut 
 wrote:
 Its only a source of troubles in C++ because it is the default 
 behavior. But if you design a library it can make the usage of 
 your api easier and also you have a few more options to stay 
 backwards compatible with your old api.
Probably. But what is the gain? `foo(Foo(5))` looks better than `foo(5)` to me in every possible way.
This is very convenient when dealing with unknown types. Think JSON manipulation for instance.
Sep 06 2013
prev sibling parent "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 13:01:14 UTC, Dicebot wrote:
 On Friday, 6 September 2013 at 11:32:11 UTC, Benjamin Thaut 
 wrote:
 Its only a source of troubles in C++ because it is the default 
 behavior. But if you design a library it can make the usage of 
 your api easier and also you have a few more options to stay 
 backwards compatible with your old api.
Probably. But what is the gain? `foo(Foo(5))` looks better than `foo(5)` to me in every possible way. For example, use case that justifies operator overloading (despite the danger) in my eyes is ability to replace built-in types with custom ones. What is the similar rationale for implicit conversion?
In a few cases it's really useful. Assume that you have user-defined type, for example BigInt. You must implement A LOT OF "foo" functions which works only whith BigInt, like RSA, DSA and etc. For example, 100-500 different functions. Do you still want to write every time `foo(BigInt(5))`? (Remember: you have to use only BigInt). So, the short answer is the same as for operator overloading: in my eyes is ability to replace built-in types with custom ones. Yes, it can be dangerous, and we must to provide explicit cast by default. But in many cases it can simplify a life and a code.
Sep 06 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 6 September 2013 at 11:04:31 UTC, Peter Alexander 
wrote:
 On Friday, 6 September 2013 at 10:33:07 UTC, ilya-stromberg 
 wrote:
 Do you have any plans to support implicit conversion between 
 types?
Implicit conversions open up a whole can of worms for the sake of a small amount of convenience. I'm not sure it's a fair trade.
I think we should be able to do it, but in unconvenient ways. Like alias this + a property.
Sep 06 2013
parent Benjamin Thaut <code benjamin-thaut.de> writes:
Am 06.09.2013 13:44, schrieb deadalnix:
 On Friday, 6 September 2013 at 11:04:31 UTC, Peter Alexander wrote:
 On Friday, 6 September 2013 at 10:33:07 UTC, ilya-stromberg wrote:
 Do you have any plans to support implicit conversion between types?
Implicit conversions open up a whole can of worms for the sake of a small amount of convenience. I'm not sure it's a fair trade.
I think we should be able to do it, but in unconvenient ways. Like alias this + a property.
But that doesn't work for construction. Also its not possible to do it for primitive types (especially string comes to mind)
Sep 06 2013
prev sibling next sibling parent reply "Flamaros" <flamaros.xavier gmail.com> writes:
On Friday, 6 September 2013 at 10:33:07 UTC, ilya-stromberg wrote:
 Do you have any plans to support implicit conversion between 
 types?

 I have some code like this:

 struct Foo
 {
 	this(int i)
 	{
 		//do something useful
 	}
 }

 void bar(Foo f)
 {
 	//do something else
 }

 void main()
 {
 	Foo f = 5;//works
 	
 	bar(f);//works
 	
 	bar(Foo(5));//works
I think that the good way, to do
 	bar(5);//Error: function app.bar (Foo f) is not callable using
 argument types (int)
 }

 So, D can't use constructor to convert "int" to "Foo" 
 implicitly.
 Can we add "implicit" keyword to allow do this:

 struct Foo
 {
 	implicit this(int i)
 	{
 		//do something useful
 	}
 }

 C++ allows this, but have "explicit" keyword.

 implicit and explicit cases.
It's difficult to never forget the "explicit" keyword in c++, and this little mistake can make you loose a lot of time. I prefer to only have explicit conversions with cast or constructors calls, that all make the code easier to understand.
Sep 06 2013
parent reply "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 13:25:37 UTC, Flamaros wrote:
 It's difficult to never forget the "explicit" keyword in c++, 
 and this little mistake can make you loose a lot of time.

 I prefer to only have explicit conversions with cast or 
 constructors calls, that all make the code easier to understand.
Yes. I sugest keep current D bechavior by default, but add "implicit" keyword. It allows to add implicit cast only if it SAFE.
Sep 06 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 6 September 2013 at 13:31:41 UTC, ilya-stromberg wrote:
 On Friday, 6 September 2013 at 13:25:37 UTC, Flamaros wrote:
 It's difficult to never forget the "explicit" keyword in c++, 
 and this little mistake can make you loose a lot of time.

 I prefer to only have explicit conversions with cast or 
 constructors calls, that all make the code easier to 
 understand.
Yes. I sugest keep current D bechavior by default, but add "implicit" keyword. It allows to add implicit cast only if it SAFE.
You don't want a keyword for that. Something obscure and ugly is required.
Sep 06 2013
parent reply "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Friday, 6 September 2013 at 13:39:20 UTC, deadalnix wrote:
 You don't want a keyword for that. Something obscure and ugly 
 is required.
We can use, for example, "opImplicitRightCast" struct Foo { Foo opImplicitRightCast(T)(T from); }
Sep 06 2013
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2013-09-06, 15:57, ilya-stromberg wrote:

 On Friday, 6 September 2013 at 13:39:20 UTC, deadalnix wrote:
 You don't want a keyword for that. Something obscure and ugly is  =
 required.
We can use, for example, "opImplicitRightCast" struct Foo { Foo opImplicitRightCast(T)(T from); }
It's a bit weird in D though, as operators are instance methods, and opImplicitRightCast (or opImplicitCastFrom, which is the name used in discussions before, see WalterAndrei.pdf from back when dinosaurs roamed= = the earth) should definitely not be an instance method but a static one. That said, I belive opImplicitCastFrom is The Right Solution=E2=84=A2. I= t's = explicit, it's a bit ugly, but not so much it hurts. -- = Simen
Sep 07 2013
parent reply "ilya-stromberg" <ilya-stromberg-2009 yandex.ru> writes:
On Saturday, 7 September 2013 at 13:02:39 UTC, Simen Kjaeraas 
wrote:
 It's a bit weird in D though, as operators are instance 
 methods, and
 opImplicitRightCast (or opImplicitCastFrom, which is the name 
 used in
 discussions before, see WalterAndrei.pdf from back when 
 dinosaurs roamed the
 earth) should definitely not be an instance method but a static 
 one.

 That said, I belive opImplicitCastFrom is The Right Solution™. 
 It's explicit,
 it's a bit ugly, but not so much it hurts.
Yes, "opImplicitCastFrom" looks better. I didn't know about "WalterAndrei.pdf" file. Can you give me a link to the file? It's intresting to read. class Foo { public Foo(int temp) { } public static implicit operator Foo(int temp) { return new Foo(temp); } } So, I agree, the "opImplicitCastFrom" should be also static.
Sep 07 2013
parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2013-09-07, 15:19, ilya-stromberg wrote:

 On Saturday, 7 September 2013 at 13:02:39 UTC, Simen Kjaeraas wrote:
 It's a bit weird in D though, as operators are instance methods, and
 opImplicitRightCast (or opImplicitCastFrom, which is the name used in=
 discussions before, see WalterAndrei.pdf from back when dinosaurs  =
 roamed the
 earth) should definitely not be an instance method but a static one.

 That said, I belive opImplicitCastFrom is The Right Solution=E2=84=A2=
. It's =
 explicit,
 it's a bit ugly, but not so much it hurts.
Yes, "opImplicitCastFrom" looks better. I didn't know about =
 "WalterAndrei.pdf" file. Can you give me a link to the file? It's  =
 intresting to read.
http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf It's from the D conference back in 2007, as the URL indicates. opImplicitCastFrom is mentioned on page 22. It's kinda interesting to see what was happening back then and how things have progressed. -- = Simen
Sep 08 2013
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, September 06, 2013 12:33:05 ilya-stromberg wrote:
 Do you have any plans to support implicit conversion between
 types?
 
 I have some code like this:
 
 struct Foo
 {
 this(int i)
 {
 //do something useful
 }
 }
 
 void bar(Foo f)
 {
 //do something else
 }
 
 void main()
 {
 Foo f = 5;//works
 
 bar(f);//works
 
 bar(Foo(5));//works
 
 bar(5);//Error: function app.bar (Foo f) is not callable using
 argument types (int)
 }
 
 So, D can't use constructor to convert "int" to "Foo" implicitly.
 Can we add "implicit" keyword to allow do this:
 
 struct Foo
 {
 implicit this(int i)
 {
 //do something useful
 }
 }
 
 C++ allows this, but have "explicit" keyword.

 implicit and explicit cases.
Personally, I think that this is opening a whole can of worms that should never be opened. alias this already causes enough trouble for stuff like templates (primarily because it becomes far too easy to pass a template constraint and yet fail to work in the actual code). It's ultimately way cleaner and far less bug-prone to disallow this sort of implicit conversion, especially when so much D code is generic code. - Jonathan M Davis
Sep 06 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 6 September 2013 at 17:49:04 UTC, Jonathan M Davis 
wrote:
 Personally, I think that this is opening a whole can of worms 
 that should
 never be opened. alias this already causes enough trouble for 
 stuff like
 templates (primarily because it becomes far too easy to pass a 
 template
 constraint and yet fail to work in the actual code). It's 
 ultimately way
 cleaner and far less bug-prone to disallow this sort of 
 implicit conversion,
 especially when so much D code is generic code.

 - Jonathan M Davis
The problem with "alias this" is that it was designed to emulate inheritance, but was *diverted*, specifically, to emulate "implicit casting instead". I think the results speak for themselves how well that worked... As a matter of fact, there are more than a few classes where I wish we could deprecate the static alias, and re-force explicit cast.
Sep 06 2013
prev sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Friday, 6 September 2013 at 10:33:07 UTC, ilya-stromberg wrote:
 Do you have any plans to support implicit conversion between 
 types?

 I have some code like this:

 struct Foo
 {
 	this(int i)
 	{
 		//do something useful
 	}
 }

 void bar(Foo f)
 {
 	//do something else
 }

 void main()
 {
 	Foo f = 5;//works
 	
 	bar(f);//works
 	
 	bar(Foo(5));//works
 	
 	bar(5);//Error: function app.bar (Foo f) is not callable using
 argument types (int)
 }
Actually D has this feature for classes: class A { this(int) {} } void foo(A a ...) {} void main() { foo(5); } This compiles and runs as expected. By the way, the code was fixed recently - before 2.063 this was allocating class on stack (so in D there were some kind of scoped stack classes), now this allocates on heap as usual. I don't see the rationale behind not working with structs.
Sep 07 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Saturday, 7 September 2013 at 19:37:28 UTC, Maxim Fomin wrote:
 Actually D has this feature for classes:

 class A
 {
 	this(int) {}
 }

 void foo(A a ...) {}

 void main()
 {
 	foo(5);
 }

 This compiles and runs as expected. By the way, the code was 
 fixed recently - before 2.063 this was allocating class on 
 stack (so in D there were some kind of scoped stack classes), 
 now this allocates on heap as usual. I don't see the rationale 
 behind not working with structs.
I don't see the rationale for it working for classes at all!
Sep 07 2013
parent "Ramon" <spam thanks.no> writes:
I understand and support Kenjis request (linked in this thread) 
in the "no casting, though" version.


approach and measure.
Whatever feature is introducing inconsistencies is almost never 
worth it.

We do have explicit conversion. If anyone feels that to look ugly 
he should ask what's worse, language inconsistencies or looks.

A+ -R
Sep 07 2013