digitalmars.D - Has the ban on returning function nested structs been lifted?
- Andrej Mitrovic (55/55) Mar 18 2011 From TDPL, page 263:
- Andrej Mitrovic (22/22) Mar 18 2011 This seems to work with classes as well. The TDPL has an example of a
- spir (7/62) Mar 18 2011 Great magic auto is. Auto with you be.
- Jonathan M Davis (7/95) Mar 18 2011 Yeah. Actually, Andrei has been making changes to std.range and std.algo...
- Andrei Alexandrescu (3/9) Mar 18 2011 Yah, TDPL needs changing. Auto returns + local types = just awesome.
- Jonathan M Davis (12/22) Mar 18 2011 Actually, the coolest part about it IMHO is that it highlights the fact ...
- bearophile (4/13) Mar 18 2011 auto variable inference is indeed almost necessary if you want to use la...
- Jonathan M Davis (10/32) Mar 18 2011 You get the simple types in Haskell, because _everything_ in Haskell is ...
- Simen kjaeraas (32/61) Mar 19 2011 And we can have something similar in D:
- Jonathan M Davis (3/73) Mar 19 2011 typeof is your friend.
- Simen kjaeraas (15/19) Mar 19 2011 Only when there is a definite type. Consider:
- spir (11/63) Mar 19 2011 I guess something similar should be the base design of ranges. "Range of...
- Simen kjaeraas (5/10) Mar 19 2011 Such a scheme precludes the usage of structs as ranges, though. It would
- spir (7/15) Mar 19 2011 Oh, yes, seems you're right. Too bad.
- Andrej Mitrovic (2/2) Mar 18 2011 Can auto functions with local types work if you're distributing your
- so (22/24) Mar 18 2011 As much as i love to have it (IMO a big issue for library design in
- Tomek =?ISO-8859-2?B?U293afFza2k=?= (4/5) Mar 18 2011 Why is it awesome?
- so (4/7) Mar 18 2011 Clean namespace and clean implementation are the first two i can think o...
From TDPL, page 263: "Nested struct objects cannot be returned from functions because the caller doesn't have access to their type". However auto seems to work around this limitation: module structInFunction; import std.stdio; void main() { auto local = foo(0); assert(local.sum() == 30); writeln(typeid(local)); // structInFunction.foo.Local } auto foo(int a) { int z = a + 10; struct Local { int x; int y; int sum() { return x + y + z; } } return Local(10, 10); } I don't have a use case for this, personally. But it does seem to work. Well, almost. The following issues a runtime error: module structInFunction; import std.stdio; void main() { auto local = foo(0); writeln(local.sum()); assert(local.sum() == 30); writeln(typeid(local)); // structInFunction.foo.Local } auto foo(int a) { int z = a + 10; struct Local { int x = 10; int y = 10; int sum() { return x + y + z; } } Local local; return local; } object.Error: Access Violation An explicit call to the ctor like this works with no runtime errors: auto local = Local(); return local;
Mar 18 2011
This seems to work with classes as well. The TDPL has an example of a class that subclasses a class definition in module scope. But this one is defined in function scope, doesn't derive, and still works: void main() { auto local = foo(0); assert(local.sum() == 30); } auto foo(int a) { int z = a + 10; class Local { int x = 10; int y = 10; int sum() { return x + y + z; } } return new Local(); }
Mar 18 2011
On 03/18/2011 06:05 PM, Andrej Mitrovic wrote:From TDPL, page 263: "Nested struct objects cannot be returned from functions because the caller doesn't have access to their type". However auto seems to work around this limitation: module structInFunction; import std.stdio; void main() { auto local = foo(0); assert(local.sum() == 30); writeln(typeid(local)); // structInFunction.foo.Local } auto foo(int a) { int z = a + 10; struct Local { int x; int y; int sum() { return x + y + z; } } return Local(10, 10); } I don't have a use case for this, personally. But it does seem to work. Well, almost. The following issues a runtime error: module structInFunction; import std.stdio; void main() { auto local = foo(0); writeln(local.sum()); assert(local.sum() == 30); writeln(typeid(local)); // structInFunction.foo.Local } auto foo(int a) { int z = a + 10; struct Local { int x = 10; int y = 10; int sum() { return x + y + z; } } Local local; return local; } object.Error: Access Violation An explicit call to the ctor like this works with no runtime errors: auto local = Local(); return local;Great magic auto is. Auto with you be. Denis -- _________________ vita es estrany spir.wikidot.com
Mar 18 2011
On Friday, March 18, 2011 10:19:10 spir wrote:On 03/18/2011 06:05 PM, Andrej Mitrovic wrote:Yeah. Actually, Andrei has been making changes to std.range and std.algorithm so that _most_ functions which return new range types work this way. So, if TDPL says that it's illegal, it probably needs to be changed. Either that or we need to stop switching over to doing things that way. On the whole though, it strikes me as a positive change. - Jonathan M DavisFrom TDPL, page 263: "Nested struct objects cannot be returned from functions because the caller doesn't have access to their type". However auto seems to work around this limitation: module structInFunction; import std.stdio; void main() { auto local = foo(0); assert(local.sum() == 30); writeln(typeid(local)); // structInFunction.foo.Local } auto foo(int a) { int z = a + 10; struct Local { int x; int y; int sum() { return x + y + z; } } return Local(10, 10); } I don't have a use case for this, personally. But it does seem to work. Well, almost. The following issues a runtime error: module structInFunction; import std.stdio; void main() { auto local = foo(0); writeln(local.sum()); assert(local.sum() == 30); writeln(typeid(local)); // structInFunction.foo.Local } auto foo(int a) { int z = a + 10; struct Local { int x = 10; int y = 10; int sum() { return x + y + z; } } Local local; return local; } object.Error: Access Violation An explicit call to the ctor like this works with no runtime errors: auto local = Local(); return local;Great magic auto is. Auto with you be.
Mar 18 2011
On 3/18/11 1:21 PM, Jonathan M Davis wrote:Yeah. Actually, Andrei has been making changes to std.range and std.algorithm so that _most_ functions which return new range types work this way. So, if TDPL says that it's illegal, it probably needs to be changed. Either that or we need to stop switching over to doing things that way. On the whole though, it strikes me as a positive change. - Jonathan M DavisYah, TDPL needs changing. Auto returns + local types = just awesome. Andrei
Mar 18 2011
On Friday 18 March 2011 12:48:17 Andrei Alexandrescu wrote:On 3/18/11 1:21 PM, Jonathan M Davis wrote:Actually, the coolest part about it IMHO is that it highlights the fact that you should be using auto with std.algorithm and _not_ care about the exact types of the return types. Knowing the exact return type for those functions is generally unnecessary and is often scary anyway (especially with the functions which return lazy ranges like map and until). Making the functions return auto and completely hiding the return type pretty much forces the issue. There's still likely to be some confusion for those new to D, but it makes the proper way to use std.algorithm more obvious. I'd hate to deal with any code which used std.algorithm without auto. That would get ugly _fast_. So, yeah. auto returns + local types are indeed awesome. - Jonathan M DavisYeah. Actually, Andrei has been making changes to std.range and std.algorithm so that _most_ functions which return new range types work this way. So, if TDPL says that it's illegal, it probably needs to be changed. Either that or we need to stop switching over to doing things that way. On the whole though, it strikes me as a positive change. - Jonathan M DavisYah, TDPL needs changing. Auto returns + local types = just awesome.
Mar 18 2011
Jonathan M Davis:Actually, the coolest part about it IMHO is that it highlights the fact that you should be using auto with std.algorithm and _not_ care about the exact types of the return types. Knowing the exact return type for those functions is generally unnecessary and is often scary anyway (especially with the functions which return lazy ranges like map and until). Making the functions return auto and completely hiding the return type pretty much forces the issue. There's still likely to be some confusion for those new to D, but it makes the proper way to use std.algorithm more obvious. I'd hate to deal with any code which used std.algorithm without auto. That would get ugly _fast_.auto variable inference is indeed almost necessary if you want to use lazy functions as the ones in Phobos. But I have to say that those types are scary because of the current design of those Phobos higher order functions. In Haskell if you have an iterable and you perform a map on it using a function that returns an int, you produce something like a [Int], that's a lazy list of machine integers. This is a very simple type. If you perform another map on that list, and the mapping function returns an int again, the type of the whole result is [Int] still. The type you work with doesn't grow more and more as they build a tree of lazy delegates... Bye, bearophile
Mar 18 2011
On Friday, March 18, 2011 15:48:53 bearophile wrote:Jonathan M Davis:You get the simple types in Haskell, because _everything_ in Haskell is lazy. _Nothing_ is actually computed until it has to be. So, the fact that a list is lazily executed doesn't really affect the type system. Not everything is lazy in D, so that doesn't work. And honestly, while the return type of functions like map and until may look fairly ugly, auto makes their ugliness pretty much irrelevant. I think that D has a solid solution. Haskell looks cleaner only because it forces laziness on everything. - Jonathan M DavisActually, the coolest part about it IMHO is that it highlights the fact that you should be using auto with std.algorithm and _not_ care about the exact types of the return types. Knowing the exact return type for those functions is generally unnecessary and is often scary anyway (especially with the functions which return lazy ranges like map and until). Making the functions return auto and completely hiding the return type pretty much forces the issue. There's still likely to be some confusion for those new to D, but it makes the proper way to use std.algorithm more obvious. I'd hate to deal with any code which used std.algorithm without auto. That would get ugly _fast_.auto variable inference is indeed almost necessary if you want to use lazy functions as the ones in Phobos. But I have to say that those types are scary because of the current design of those Phobos higher order functions. In Haskell if you have an iterable and you perform a map on it using a function that returns an int, you produce something like a [Int], that's a lazy list of machine integers. This is a very simple type. If you perform another map on that list, and the mapping function returns an int again, the type of the whole result is [Int] still. The type you work with have found a more complex solution, they build a tree of lazy delegates...
Mar 18 2011
On Fri, 18 Mar 2011 23:48:53 +0100, bearophile <bearophileHUGS lycos.com> wrote:Jonathan M Davis:And we can have something similar in D: struct Range( T ) { void delegate( ) popFrontDg; bool delegate( ) emptyDg; T delegate( ) frontDg; this( R )( R range ) if ( isForwardRange!R && is( ElementType!R == T ) ) { auto rng = range.save(); popFrontDg = ( ){ rng.popFront(); }; emptyDg = ( ){ return rng.empty; }; frontDg = ( ){ return rng.front; }; } property T front( ) { return frontDg( ); } property bool empty( ) { return emptyDg( ); } void popFront( ) { popFrontDg( ); } } Range!(ElementType!R) range( R )( R rng ) if ( isForwardRange!R ) { return Range!(ElementType!R)( rng ); } There are times when I've wanted something like this because I don't know the resultant type of a bunch of range operations, but have to save it in a struct or class. -- SimenActually, the coolest part about it IMHO is that it highlights the fact that you should be using auto with std.algorithm and _not_ care about the exact types of the return types. Knowing the exact return type for those functions is generally unnecessary and is often scary anyway (especially with the functions which return lazy ranges like map and until). Making the functions return auto and completely hiding the return type pretty much forces the issue. There's still likely to be some confusion for those new to D, but it makes the proper way to use std.algorithm more obvious. I'd hate to deal with any code which used std.algorithm without auto. That would get ugly _fast_.auto variable inference is indeed almost necessary if you want to use lazy functions as the ones in Phobos. But I have to say that those types are scary because of the current design of those Phobos higher order functions. In Haskell if you have an iterable and you perform a map on it using a function that returns an int, you produce something like a [Int], that's a lazy list of machine integers. This is a very simple type. If you perform another map on that list, and the mapping function returns an int again, the type of the whole result is [Int] still. The type you work with doesn't grow more and more as with Phobos functions. tree of lazy delegates...
Mar 19 2011
On Saturday 19 March 2011 02:27:46 Simen kjaeraas wrote:On Fri, 18 Mar 2011 23:48:53 +0100, bearophile <bearophileHUGS lycos.com> wrote:typeof is your friend. - Jonathan M DavisJonathan M Davis:And we can have something similar in D: struct Range( T ) { void delegate( ) popFrontDg; bool delegate( ) emptyDg; T delegate( ) frontDg; this( R )( R range ) if ( isForwardRange!R && is( ElementType!R == T ) ) { auto rng = range.save(); popFrontDg = ( ){ rng.popFront(); }; emptyDg = ( ){ return rng.empty; }; frontDg = ( ){ return rng.front; }; } property T front( ) { return frontDg( ); } property bool empty( ) { return emptyDg( ); } void popFront( ) { popFrontDg( ); } } Range!(ElementType!R) range( R )( R rng ) if ( isForwardRange!R ) { return Range!(ElementType!R)( rng ); } There are times when I've wanted something like this because I don't know the resultant type of a bunch of range operations, but have to save it in a struct or class.Actually, the coolest part about it IMHO is that it highlights the fact that you should be using auto with std.algorithm and _not_ care about the exact types of the return types. Knowing the exact return type for those functions is generally unnecessary and is often scary anyway (especially with the functions which return lazy ranges like map and until). Making the functions return auto and completely hiding the return type pretty much forces the issue. There's still likely to be some confusion for those new to D, but it makes the proper way to use std.algorithm more obvious. I'd hate to deal with any code which used std.algorithm without auto. That would get ugly _fast_.auto variable inference is indeed almost necessary if you want to use lazy functions as the ones in Phobos. But I have to say that those types are scary because of the current design of those Phobos higher order functions. In Haskell if you have an iterable and you perform a map on it using a function that returns an int, you produce something like a [Int], that's a lazy list of machine integers. This is a very simple type. If you perform another map on that list, and the mapping function returns an int again, the type of the whole result is [Int] still. The type you work with doesn't grow more and more as with Phobos functions. tree of lazy delegates...
Mar 19 2011
On Sat, 19 Mar 2011 11:18:17 +0100, Jonathan M Davis <jmdavisProg gmx.com> wrote:Only when there is a definite type. Consider: struct foo { Range!int rng; this( int[] arr, bool b ) { if ( b ) { rng = arr; } else { rng = map!"a+b"( arr ); } } } -- SimenThere are times when I've wanted something like this because I don't know the resultant type of a bunch of range operations, but have to save it in a struct or class.typeof is your friend.
Mar 19 2011
On 03/19/2011 10:27 AM, Simen kjaeraas wrote:On Fri, 18 Mar 2011 23:48:53 +0100, bearophile <bearophileHUGS lycos.com> wrote:I guess something similar should be the base design of ranges. "Range of X" could simply mean "lazy sequence of X", an on-demand array (lol); and that would be the return type of every function returning a range. The complexity (of filter-ing, map-ping, find-ind) could be hidden inside the object, not exposed in the outer type. Denis -- _________________ vita es estrany spir.wikidot.comJonathan M Davis:And we can have something similar in D: struct Range( T ) { void delegate( ) popFrontDg; bool delegate( ) emptyDg; T delegate( ) frontDg; this( R )( R range ) if ( isForwardRange!R && is( ElementType!R == T ) ) { auto rng = range.save(); popFrontDg = ( ){ rng.popFront(); }; emptyDg = ( ){ return rng.empty; }; frontDg = ( ){ return rng.front; }; } property T front( ) { return frontDg( ); } property bool empty( ) { return emptyDg( ); } void popFront( ) { popFrontDg( ); } } Range!(ElementType!R) range( R )( R rng ) if ( isForwardRange!R ) { return Range!(ElementType!R)( rng ); } There are times when I've wanted something like this because I don't know the resultant type of a bunch of range operations, but have to save it in a struct or class.Actually, the coolest part about it IMHO is that it highlights the fact that you should be using auto with std.algorithm and _not_ care about the exact types of the return types. Knowing the exact return type for those functions is generally unnecessary and is often scary anyway (especially with the functions which return lazy ranges like map and until). Making the functions return auto and completely hiding the return type pretty much forces the issue. There's still likely to be some confusion for those new to D, but it makes the proper way to use std.algorithm more obvious. I'd hate to deal with any code which used std.algorithm without auto. That would get ugly _fast_.auto variable inference is indeed almost necessary if you want to use lazy functions as the ones in Phobos. But I have to say that those types are scary because of the current design of those Phobos higher order functions. In Haskell if you have an iterable and you perform a map on it using a function that returns an int, you produce something like a [Int], that's a lazy list of machine integers. This is a very simple type. If you perform another map on that list, and the mapping function returns an int again, the type of the whole result is [Int] still. The type you work with doesn't grow more and solution, they build a tree of lazy delegates...
Mar 19 2011
On Sat, 19 Mar 2011 13:05:59 +0100, spir <denis.spir gmail.com> wrote:I guess something similar should be the base design of ranges. "Range of X" could simply mean "lazy sequence of X", an on-demand array (lol); and that would be the return type of every function returning a range. The complexity (of filter-ing, map-ping, find-ind) could be hidden inside the object, not exposed in the outer type.Such a scheme precludes the usage of structs as ranges, though. It would require virtual functions. -- Simen
Mar 19 2011
On 03/19/2011 01:40 PM, Simen kjaeraas wrote:On Sat, 19 Mar 2011 13:05:59 +0100, spir <denis.spir gmail.com> wrote:Oh, yes, seems you're right. Too bad. Denis -- _________________ vita es estrany spir.wikidot.comI guess something similar should be the base design of ranges. "Range of X" could simply mean "lazy sequence of X", an on-demand array (lol); and that would be the return type of every function returning a range. The complexity (of filter-ing, map-ping, find-ind) could be hidden inside the object, not exposed in the outer type.Such a scheme precludes the usage of structs as ranges, though. It would require virtual functions.
Mar 19 2011
Can auto functions with local types work if you're distributing your library in binary form (.lib) and only .di interface files?
Mar 18 2011
On Fri, 18 Mar 2011 22:25:49 +0200, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Can auto functions with local types work if you're distributing your library in binary form (.lib) and only .di interface files?As much as i love to have it (IMO a big issue for library design in general) i am afraid it is not possible because of dynamic libraries. If only we could expose only the parts we want, with zero overhead (I don't know, maybe it is already possible somehow). There is not a single elegant solution in the languages i know. --- di file struct A { // imposter A { method1 method2 } --- d file struct A { method1 method2 method3 ... // data int a, b, c; }
Mar 18 2011
Andrei Alexandrescu napisa=B3:Auto returns + local types =3D just awesome.Why is it awesome? --=20 Tomek
Mar 18 2011
On Sat, 19 Mar 2011 00:07:40 +0200, Tomek Sowi=C5=84ski <just ask.me> wr= ote:Andrei Alexandrescu napisa=C5=82:Clean namespace and clean implementation are the first two i can think o= f.Auto returns + local types =3D just awesome.Why is it awesome?
Mar 18 2011