digitalmars.D - A const idiom + a different 'delete'
- bearophile (33/33) Aug 11 2010 This is an little idiom that is getting common in my code, it's similar ...
- bearophile (3/4) Aug 11 2010 It's similar to the #undef directive of C.
- Andrej Mitrovic (23/28) Aug 11 2010 I think a more appropriate word for that would be "hide", or maybe "free...
- Andrej Mitrovic (3/36) Aug 11 2010 Actually that was a pretty useless example. const needs to be evaluable ...
- Jonathan M Davis (22/89) Aug 11 2010 I think that's a total no-go because it would depend on program flow. Yo...
- bearophile (6/8) Aug 11 2010 You can't do that, you can't modify a const variable in that way :-)
- Jonathan M Davis (9/24) Aug 11 2010 Hits self on head. It's the whole issue with not being able to break out...
- Adam Ruppe (11/11) Aug 11 2010 Here's how I'd do it:
- bearophile (22/26) Aug 11 2010 I don't agree. To test a runtime value you need an if statement, and it ...
- Andrej Mitrovic (2/103) Aug 11 2010
- Andrej Mitrovic (3/5) Aug 11 2010 p.s. Sorry about quoting large ammounts of text, I need to break out of ...
- Kagamin (13/22) Aug 11 2010 void f()
- Shin Fujishiro (26/36) Aug 12 2010 It looks like LISP's (let ...) expression and personally I like it.
- bearophile (19/42) Aug 12 2010 I think this is more readable (not tested much):
- Pillsy (9/23) Aug 12 2010 It also makes for a fine replacement for the comma operator, because you...
This is an little idiom that is getting common in my code, it's similar to the 'transients' of Clojure language. Often I have to build a simple data structure like an array associative or another kind of array, it needs to be mutable because I fill it in few lines of code code. When it's complete I never need to change it again, so now I'd like it to be const. I can't freeze it, so I have to create a new variable: void main() { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; // now I'd like to freeze aa const(int[int]) aa = aa_; } If the array is fixed-sized the situation is worse because the last line copies the whole array. Sometimes I use a small function to do this. This is an alternative way to write it that I've never used because I don't like it much: void main() { const(int[int]) aa = { int[int] result; foreach (i; 0 .. 10) result[i] = i * i; return result; }(); } In Python I sometimes use "delete" to remove a name from the local namespace that I don't want to use any more. This cleaning purpose may be a replacement purpose for the delete keyword, but I don't know if you like it: void main() { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; // now I'd like to freeze aa const(int[int]) aa = aa_; delete aa_; // from now on the 'aa_' name can't be seen/used. } Here delete doesn't clean the aa_, it just removes the aa_ name from the local namespace. If there's another reference to aa_ it is unchanged and it sees an unchanged associative array. The point of removing the aa_ name is to keep the namespace tidy. As with variable definitions a goto can't jump over one delete. In Python you can later create a new variable with the same name, but in D the variable doesn't actually vanish from the stack, so I think it's better to disallow successive redefinitions of it. Bye, bearophile
Aug 11 2010
Here delete doesn't clean the aa_, it just removes the aa_ name from the local namespace.It's similar to the #undef directive of C. Bye, bearophile
Aug 11 2010
I think a more appropriate word for that would be "hide", or maybe "freeze". Here's another alternative, if you can call it that :p : import std.stdio; auto freeze(alias dg)() { return cast(const) dg(); } void main() { auto aa = freeze!( { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; return aa_; }); writeln(aa, " ", typeid(aa)); } But that returns const(const(int)[int]). I'm not sure if there's a way to hide a symbol, appart from introducing another scope with { }. That wouldn't work in this case, since you want a const type back. Perhaps using the "variant" type from Phobos could do the trick.. I haven't played with that yet. On Wed, Aug 11, 2010 at 6:01 PM, bearophile <bearophileHUGS lycos.com>wrote:Here delete doesn't clean the aa_, it just removes the aa_ name from thelocal namespace. It's similar to the #undef directive of C. Bye, bearophile
Aug 11 2010
Actually that was a pretty useless example. const needs to be evaluable at compile-time, so why did I bother creating a template function? lol.. On Wed, Aug 11, 2010 at 7:03 PM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:I think a more appropriate word for that would be "hide", or maybe "freeze". Here's another alternative, if you can call it that :p : import std.stdio; auto freeze(alias dg)() { return cast(const) dg(); } void main() { auto aa = freeze!( { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; return aa_; }); writeln(aa, " ", typeid(aa)); } But that returns const(const(int)[int]). I'm not sure if there's a way to hide a symbol, appart from introducing another scope with { }. That wouldn't work in this case, since you want a const type back. Perhaps using the "variant" type from Phobos could do the trick.. I haven't played with that yet. On Wed, Aug 11, 2010 at 6:01 PM, bearophile <bearophileHUGS lycos.com>wrote:Here delete doesn't clean the aa_, it just removes the aa_ name from thelocal namespace. It's similar to the #undef directive of C. Bye, bearophile
Aug 11 2010
On Wednesday, August 11, 2010 08:59:29 bearophile wrote:This is an little idiom that is getting common in my code, it's similar to the 'transients' of Clojure language. Often I have to build a simple data structure like an array associative or another kind of array, it needs to be mutable because I fill it in few lines of code code. When it's complete I never need to change it again, so now I'd like it to be const. I can't freeze it, so I have to create a new variable: void main() { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; // now I'd like to freeze aa const(int[int]) aa = aa_; } If the array is fixed-sized the situation is worse because the last line copies the whole array. Sometimes I use a small function to do this. This is an alternative way to write it that I've never used because I don't like it much: void main() { const(int[int]) aa = { int[int] result; foreach (i; 0 .. 10) result[i] = i * i; return result; }(); } In Python I sometimes use "delete" to remove a name from the local namespace that I don't want to use any more. This cleaning purpose may be a replacement purpose for the delete keyword, but I don't know if you like it: void main() { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; // now I'd like to freeze aa const(int[int]) aa = aa_; delete aa_; // from now on the 'aa_' name can't be seen/used. } Here delete doesn't clean the aa_, it just removes the aa_ name from the local namespace. If there's another reference to aa_ it is unchanged and it sees an unchanged associative array. The point of removing the aa_ name is to keep the namespace tidy. As with variable definitions a goto can't jump over one delete. In Python you can later create a new variable with the same name, but in D the variable doesn't actually vanish from the stack, so I think it's better to disallow successive redefinitions of it. Bye, bearophileI think that's a total no-go because it would depend on program flow. You're trying to alter a compile-time property at runtime. All it takes is having delete within an if statement, and all of a sudden, depending on how your program runs, the name may or may not be visible. You kind hide the variable creation inside another scope if you want this sort of behavior: void main() { const(int[int]) aa; { int[int] aa_; foreach(i; 0 .. 10) aa_[i] = i * i; aa = aa_; } } That naturally removes the name from the namespace just fine. It's possible that this particular idiom calls for some sort of language addition or having something added to Phobos, but removing symbols from a namespace with statements is not going to work very well - if at all - in a compiled, statically-typed language like D. - Jonathan M Davis
Aug 11 2010
Jonathan M Davis:I think that's a total no-go because it would depend on program flow.You are of course right, it's the silly thing of the day. The #undef works because it's not scoped.You kind hide the variable creation inside another scope if you want this sort of behavior:<You can't do that, you can't modify a const variable in that way :-) I will just keep two variables, one immutable and one mutable, as in the first example. Later, bearophile
Aug 11 2010
On Wednesday, August 11, 2010 10:52:30 bearophile wrote:Jonathan M Davis:Hits self on head. It's the whole issue with not being able to break out the referent from the reference declaring it const. Bleh. I wish that we'd found a better solution to that. In any case, then you should be able to use Rebindable to fix the problem. The other possibility is to just create a function to return the variable. That way, the const variable gets properly initialized rather than having to find a way to assign to it later. - Jonathan M DavisI think that's a total no-go because it would depend on program flow.You are of course right, it's the silly thing of the day. The #undef works because it's not scoped.You kind hide the variable creation inside another scope if you want this sort of behavior:<You can't do that, you can't modify a const variable in that way :-) I will just keep two variables, one immutable and one mutable, as in the first example. Later, bearophile
Aug 11 2010
Here's how I'd do it: void main() { const T[] buildArray() { T[] t; // fill in t return t; } const T[] t = buildArray; } So the creation is factored away into that nested function which is used to initialized the reference that you keep around.
Aug 11 2010
Jonathan M Davis:I think that's a total no-go because it would depend on program flow. You're trying to alter a compile-time property at runtime. All it takes is having delete within an if statement, and all of a sudden, depending on how your program runs, the name may or may not be visible.I don't agree. To test a runtime value you need an if statement, and it creates the scope of its then and else parts, if you remove a variable inside one of those two scopes, x is present again when the then and else scopes are finished. So what's the problem? import std.conv: to; void main(string[] args) { int x = 10; enum int y = 5; // here x can be used if (args.length > 1) { // here x can be used delete x; // here x can't be used float x; // not allowed } else { // here x can be used } // here x can be used static if (y < 2) delete x; // here x can or can't be used according to the compile-time value of y } Bye, bearophile
Aug 11 2010
The scope trick won't work. You can't modify a const after the declaration. On Wed, Aug 11, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.com>wrote:On Wednesday, August 11, 2010 08:59:29 bearophile wrote:This is an little idiom that is getting common in my code, it's similartothe 'transients' of Clojure language. Often I have to build a simple data structure like an array associativeoranother kind of array, it needs to be mutable because I fill it in few lines of code code. When it's complete I never need to change it again,sonow I'd like it to be const. I can't freeze it, so I have to create a new variable: void main() { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; // now I'd like to freeze aa const(int[int]) aa = aa_; } If the array is fixed-sized the situation is worse because the last line copies the whole array. Sometimes I use a small function to do this. This is an alternative way to write it that I've never used because Idon'tlike it much: void main() { const(int[int]) aa = { int[int] result; foreach (i; 0 .. 10) result[i] = i * i; return result; }(); } In Python I sometimes use "delete" to remove a name from the local namespace that I don't want to use any more. This cleaning purpose may be a replacement purpose for the delete keyword, but I don't know if youlikeit: void main() { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; // now I'd like to freeze aa const(int[int]) aa = aa_; delete aa_; // from now on the 'aa_' name can't be seen/used. } Here delete doesn't clean the aa_, it just removes the aa_ name from the local namespace. If there's another reference to aa_ it is unchanged and it sees an unchanged associative array. The point of removing the aa_nameis to keep the namespace tidy. As with variable definitions a goto can't jump over one delete. In Python you can later create a new variable with the same name, but in D the variable doesn't actually vanish from the stack, so I think it's bettertodisallow successive redefinitions of it. Bye, bearophileI think that's a total no-go because it would depend on program flow. You're trying to alter a compile-time property at runtime. All it takes is having delete within an if statement, and all of a sudden, depending on how your program runs, the name may or may not be visible. You kind hide the variable creation inside another scope if you want this sort of behavior: void main() { const(int[int]) aa; { int[int] aa_; foreach(i; 0 .. 10) aa_[i] = i * i; aa = aa_; } } That naturally removes the name from the namespace just fine. It's possible that this particular idiom calls for some sort of language addition or having something added to Phobos, but removing symbols from a namespace with statements is not going to work very well - if at all - in a compiled, statically-typed language like D. - Jonathan M Davis
Aug 11 2010
p.s. Sorry about quoting large ammounts of text, I need to break out of that habbit. On Wed, Aug 11, 2010 at 7:28 PM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:The scope trick won't work. You can't modify a const after the declaration.
Aug 11 2010
bearophile Wrote:void main() { int[int] aa_; foreach (i; 0 .. 10) aa_[i] = i * i; // now I'd like to freeze aa const(int[int]) aa = aa_; }void f() { { int[int] aa; //fill g(aa); } void g(ref const int[int] bb) { //do stuff } }
Aug 11 2010
bearophile <bearophileHUGS lycos.com> wrote:This is an alternative way to write it that I've never used because I don't like it much: void main() { const(int[int]) aa = { int[int] result; foreach (i; 0 .. 10) result[i] = i * i; return result; }(); }It looks like LISP's (let ...) expression and personally I like it. The idiom is really useful for building immutable things, including compile-time constants. And recently I found it works well with string mixins. -------------------- import std.conv; auto ref reverseArgs(alias fun, Args...)(auto ref Args args) { return mixin( { string rargs; foreach (i, Arg; Args) { if (i > 0) rargs ~= ", "; rargs ~= "args[" ~ to!string(args.length - i - 1) ~ "]"; } return "fun(" ~ rargs ~ ")"; }()); } void main() { int div(int a, int b) { return a / b; } assert(reverseArgs!div(3, 27) == 9); }
Aug 12 2010
Shin Fujishiro:import std.conv; auto ref reverseArgs(alias fun, Args...)(auto ref Args args) { return mixin( { string rargs; foreach (i, Arg; Args) { if (i > 0) rargs ~= ", "; rargs ~= "args[" ~ to!string(args.length - i - 1) ~ "]"; } return "fun(" ~ rargs ~ ")"; }()); } void main() { int div(int a, int b) { return a / b; } assert(reverseArgs!div(3, 27) == 9); }I think this is more readable (not tested much): import std.typecons: TypeTuple; // this needs to be template of std.typecons template ReverseTypeTuple(T...) { // too much long name static if (T.length) alias TypeTuple!(ReverseTypeTuple!(T[1 .. $]), T[0]) ReverseTypeTuple; else alias T ReverseTypeTuple; } auto ref reverseArgs(alias fun, Args...)(auto ref Args args) { return fun(ReverseTypeTuple!args); } int div(int a, int b) { return a / b; } void main() { assert(reverseArgs!div(3, 27) == 9); } Bye, bearophile
Aug 12 2010
Shin Fujishiro Wrote:bearophile <bearophileHUGS lycos.com> wrote:This is an alternative way to write it that I've never used< because I don't like it much:void main() { const(int[int]) aa = { int[int] result; foreach (i; 0 .. 10) result[i] = i * i; return result; }(); }It looks like LISP's (let ...) expression and personally I like it. The idiom is really useful for building immutable things, including compile-time constants.It also makes for a fine replacement for the comma operator, because you can use it anywhere you can use an expression. I'd love a bit of syntactic sugar for the idiom, that would do the same thing the Lisp LET does, transforming let (x = a, y = b) { foo(x); bar(y); return quux(x, y); } into (x, y){ foo(x); bar(y); return quux(x, y); }(a, b) automatically. Cheers, Pillsy
Aug 12 2010