www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A const idiom + a different 'delete'

reply bearophile <bearophileHUGS lycos.com> writes:
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
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
 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
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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 the
local namespace. It's similar to the #undef directive of C. Bye, bearophile
Aug 11 2010
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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.com
 wrote:
 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 the
local namespace. It's similar to the #undef directive of C. Bye, bearophile
Aug 11 2010
prev sibling next sibling parent reply Jonathan M Davis <jmdavisprog gmail.com> writes:
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,
 bearophile
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. 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
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Wednesday, August 11, 2010 10:52:30 bearophile wrote:
 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
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 Davis
Aug 11 2010
prev sibling parent Adam Ruppe <destructionator gmail.com> writes:
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
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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 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
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. 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
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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.com
 wrote:
 The scope trick won't work. You can't modify a const after the declaration.
Aug 11 2010
prev sibling next sibling parent Kagamin <spam here.lot> writes:
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
prev sibling next sibling parent reply Shin Fujishiro <rsinfu gmail.com> writes:
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
parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling parent Pillsy <pillsbury gmail.com> writes:
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