www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to get an inout constructor working with a template wrapper

reply aliak <something something.com> writes:
Hi,

In the code below:

struct W(T) {
     T val;
     this(T val) inout {
         this.val = val;
     }
}

class C {}

void main() {
    W!C a = new C;
    immutable W!C b = new C;
}

W!C a = new C results in: "Error: cannot implicitly convert 
expression val of type C to inout(C)."

If I remove the inout on the constructor then the error is on the 
other line and is: "Error: mutable method W!(C).W.this is not 
callable using a immutable object"

If the class is changed to a struct through,  then the 
constructor with inout works on both lines in main above.

So I guess this has something to do with reference types (As the 
same behaviour is exhibited if T == int*) What's the recommended 
way to handle this?

Cheers,
- Ali
Jul 22 2018
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/22/2018 03:51 PM, aliak wrote:
 Hi,

 In the code below:

 struct W(T) {
      T val;
      this(T val) inout {
          this.val = val;
      }
 }

 class C {}

 void main() {
     W!C a = new C;
     immutable W!C b = new C;
 }

 W!C a = new C results in: "Error: cannot implicitly convert expression
 val of type C to inout(C)."

 If I remove the inout on the constructor then the error is on the other
 line and is: "Error: mutable method W!(C).W.this is not callable using a
 immutable object"

 If the class is changed to a struct through,  then the constructor with
 inout works on both lines in main above.

 So I guess this has something to do with reference types (As the same
 behaviour is exhibited if T == int*) What's the recommended way to
 handle this?

 Cheers,
 - Ali
Without much confidence on my side, first, I think you need to make the constructor parameter inout(T) as well. Otherwise, you may be making a const(W!T) initialized with a non-const T. After that, I like the "type constructor" syntax in main_alt() below (which works) but a better approach is to use a convenience function like wrap() below: struct W(T) { T val; this(inout(T) val) inout { this.val = val; } } class C {} void main_alt() { auto a = W!C(new C); auto b = immutable W!(immutable C)(new C); } auto wrap(T)(inout T t) { return inout(W!T)(t); } void main() { auto a = wrap(new C); auto b = wrap(new immutable(C)); } Ali "taklitlerinden sakınınız" :o)
Jul 22 2018
parent reply aliak <something something.com> writes:
On Sunday, 22 July 2018 at 23:11:09 UTC, Ali Çehreli wrote:
 Without much confidence on my side, first, I think you need to 
 make the constructor parameter inout(T) as well. Otherwise, you 
 may be making a const(W!T) initialized with a non-const T.

 After that, I like the "type constructor" syntax in main_alt() 
 below (which works) but a better approach is to use a 
 convenience function like wrap() below:

 struct W(T) {
     T val;
     this(inout(T) val) inout {
         this.val = val;
     }
 }

 class C {}

 void main_alt() {
     auto a = W!C(new C);
     auto b = immutable W!(immutable C)(new C);
 }

 auto wrap(T)(inout T t) {
     return inout(W!T)(t);
 }

 void main() {
     auto a = wrap(new C);
     auto b = wrap(new immutable(C));
 }

 Ali
 "taklitlerinden sakınınız" :o)
Thank you Ali! That helped :) I've gotten most of it sorted out now, and the factory wrap is definitely the way to go, it also turned out that inout(T) and inout T (so inout without parens) was surprisingly different (maybe it's a bug? - to test you can remove the parens around U on line 3 in this sample: https://run.dlang.io/is/gd5oxW Also over there, line 24: auto si = wrap!(immutable int)(3); seems to be giving problems. Any ideas there? Error is: onlineapp.d(8): Error: inout on return means inout must be on a parameter as well for pure nothrow nogc safe inout(W!(immutable(int)))(immutable(int) t) onlineapp.d(23): Error: template instance `onlineapp.wrap!(immutable(int))` error instantiating To make it compile successfully you can either: 1) Chance immutable to const, then it works for some reason. 2) Change the line to: "auto si = wrap(cast(immutable int)3);" - i.e. do not explicitly provide type information. Cheers, - Ali
Jul 23 2018
next sibling parent aliak <something something.com> writes:
On Monday, 23 July 2018 at 12:02:58 UTC, aliak wrote:
 https://run.dlang.io/is/gd5oxW
Sorry wrong link! This one is correct -> https://run.dlang.io/is/azxmGN
Jul 23 2018
prev sibling next sibling parent reply Timoses <timosesu gmail.com> writes:
On Monday, 23 July 2018 at 12:02:58 UTC, aliak wrote:
 Thank you Ali! That helped :) I've gotten most of it sorted out 
 now, and the factory wrap is definitely the way to go, it also 
 turned out that inout(T) and inout T (so inout without parens) 
 was surprisingly different (maybe it's a bug? - to test you can 
 remove the parens around U on line 3 in this sample: 
 https://run.dlang.io/is/gd5oxW

 Also over there, line 24:

 auto si = wrap!(immutable int)(3);
Both of these seem to work (as you pointed out) // immutable(W!int) auto si = wrap!(int)(cast(immutable)3); // or wrap(cast(immutable)3); // W!(immutable(int)) auto si2 = W!(immutable int)(3);
 seems to be giving problems. Any ideas there? Error is:

 onlineapp.d(8): Error: inout on return means inout must be on a 
 parameter as well for pure nothrow  nogc  safe 
 inout(W!(immutable(int)))(immutable(int) t)
 onlineapp.d(23): Error: template instance 
 `onlineapp.wrap!(immutable(int))` error instantiating

 To make it compile successfully you can either:

 1) Chance immutable to const, then it works for some reason.
 2) Change the line to: "auto si = wrap(cast(immutable int)3);" 
 - i.e. do not explicitly provide type information.
I don't know why wrap!(immutable int)(3); is not working. The error message "Error: inout on return means inout must be on a parameter as well for pure nothrow nogc safe inout(W!(immutable(int)))(return immutable(int) t)" sounds very odd and not at all helpful, at least regarding that removing immutable from the template argument works.
 Cheers,
 - Ali
The depths of D. Why does the following only work with "return ref"? struct W(T) { T val; this(U : T)(auto ref inout U val) inout { pragma(msg, typeof(val)); this.val = val; } } // Fails without "return ref" (escaping t warning...) auto wrap(T)(return ref inout T t) { return inout W!T(t); } class C {} void main() { immutable C ci = new immutable C; auto i = wrap(im); pragma(msg, typeof(i)); }
Jul 23 2018
parent reply aliak <something something.com> writes:
On Monday, 23 July 2018 at 14:46:32 UTC, Timoses wrote:
 On Monday, 23 July 2018 at 12:02:58 UTC, aliak wrote:
 [...]
Both of these seem to work (as you pointed out) // immutable(W!int) auto si = wrap!(int)(cast(immutable)3); // or wrap(cast(immutable)3); // W!(immutable(int)) auto si2 = W!(immutable int)(3);
 [...]
I don't know why wrap!(immutable int)(3); is not working. The error message "Error: inout on return means inout must be on a parameter as well for pure nothrow nogc safe inout(W!(immutable(int)))(return immutable(int) t)" sounds very odd and not at all helpful, at least regarding that removing immutable from the template argument works.
 [...]
The depths of D. Why does the following only work with "return ref"? struct W(T) { T val; this(U : T)(auto ref inout U val) inout { pragma(msg, typeof(val)); this.val = val; } } // Fails without "return ref" (escaping t warning...) auto wrap(T)(return ref inout T t) { return inout W!T(t); } class C {} void main() { immutable C ci = new immutable C; auto i = wrap(im); pragma(msg, typeof(i)); }
Ok, thanks to Simen from another post [0], I just figured out what the correct constructor and factory method for a template wrapper should be: https://run.dlang.io/is/S4vHzL struct W(T) { T val; this(U : T, this This)(auto ref U val) { this.val = val; } } auto wrap(T)(auto ref T t) { return W!T(t); } Seems to catch all cases! [0]: https://forum.dlang.org/thread/hxbeektmpnmfdbvjrtcf forum.dlang.org
Jul 27 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/27/18 9:29 AM, aliak wrote:
 Ok, thanks to Simen from another post [0], I just figured out what the 
 correct constructor and factory method for a template wrapper should be:
 
 https://run.dlang.io/is/S4vHzL
 
 struct W(T) {
      T val;
      this(U : T, this This)(auto ref U val) {
          this.val = val;
      }
 }
 
 auto wrap(T)(auto ref T t) {
      return W!T(t);
 }
 
 Seems to catch all cases!
And instantiate a new template for all mutabilities. Whereas inout would only instantiate one (and disallows modification of val if not const or immutable). -Steve
Jul 27 2018
parent reply aliak <something something.com> writes:
On Friday, 27 July 2018 at 14:38:27 UTC, Steven Schveighoffer 
wrote:
 On 7/27/18 9:29 AM, aliak wrote:
 Ok, thanks to Simen from another post [0], I just figured out 
 what the correct constructor and factory method for a template 
 wrapper should be:
 
 https://run.dlang.io/is/S4vHzL
 
 struct W(T) {
      T val;
      this(U : T, this This)(auto ref U val) {
          this.val = val;
      }
 }
 
 auto wrap(T)(auto ref T t) {
      return W!T(t);
 }
 
 Seems to catch all cases!
And instantiate a new template for all mutabilities. Whereas inout would only instantiate one (and disallows modification of val if not const or immutable). -Steve
If you change the ctor to be inout then you get (from the link above): onlineapp.d(4): Error: cannot implicitly convert expression val of type onlineapp.C to inout(C) onlineapp.d(28): Error: template instance `onlineapp.W!(C).W.__ctor!(C)` error instantiating onlineapp.d(4): Error: cannot implicitly convert expression val of type S1 to inout(S1) onlineapp.d(44): Error: template instance `onlineapp.W!(S1).W.__ctor!(S1)` error instantiating onlineapp.d(4): Error: cannot implicitly convert expression val of type onlineapp.C to inout(C) onlineapp.d(9): Error: template instance `onlineapp.W!(C).W.__ctor!(C)` error instantiating onlineapp.d(52): instantiated from here: wrap!(C) onlineapp.d(4): Error: cannot implicitly convert expression val of type const(C) to inout(const(C)) onlineapp.d(9): Error: template instance `onlineapp.W!(const(C)).W.__ctor!(const(C))` error instantiating onlineapp.d(53): instantiated from here: wrap!(const(C)) Am I applying inout incorrectly?
Jul 28 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/28/18 6:09 PM, aliak wrote:
 On Friday, 27 July 2018 at 14:38:27 UTC, Steven Schveighoffer wrote:
 On 7/27/18 9:29 AM, aliak wrote:
 Ok, thanks to Simen from another post [0], I just figured out what 
 the correct constructor and factory method for a template wrapper 
 should be:

 https://run.dlang.io/is/S4vHzL

 struct W(T) {
      T val;
      this(U : T, this This)(auto ref U val) {
          this.val = val;
      }
 }

 auto wrap(T)(auto ref T t) {
      return W!T(t);
 }

 Seems to catch all cases!
And instantiate a new template for all mutabilities. Whereas inout would only instantiate one (and disallows modification of val if not const or immutable). -Steve
If you change the ctor to be inout then you get (from the link above): onlineapp.d(4): Error: cannot implicitly convert expression val of type onlineapp.C to inout(C) onlineapp.d(28): Error: template instance `onlineapp.W!(C).W.__ctor!(C)` error instantiating onlineapp.d(4): Error: cannot implicitly convert expression val of type S1 to inout(S1) onlineapp.d(44): Error: template instance `onlineapp.W!(S1).W.__ctor!(S1)` error instantiating onlineapp.d(4): Error: cannot implicitly convert expression val of type onlineapp.C to inout(C) onlineapp.d(9): Error: template instance `onlineapp.W!(C).W.__ctor!(C)` error instantiating onlineapp.d(52):        instantiated from here: wrap!(C) onlineapp.d(4): Error: cannot implicitly convert expression val of type const(C) to inout(const(C)) onlineapp.d(9): Error: template instance `onlineapp.W!(const(C)).W.__ctor!(const(C))` error instantiating onlineapp.d(53):        instantiated from here: wrap!(const(C)) Am I applying inout incorrectly?
No, you need to apply it to wrap as well. I can't get run.dlang.io to work for posting a link, so here is my modified version: struct W(T) { T val; this(U : T)(auto ref inout(U) val) inout { this.val = val; } } auto wrap(T)(auto ref inout(T) t) { return inout W!T(t); } class C {} struct S0 {} struct S1 { C c; } void f_dprimitive() { int a = 3; const int b = 3; immutable int c = 3; const int d = a; immutable int e = a; auto sc = wrap(const(int)(3)); // note the modifications here auto si = wrap(immutable(int)(3)); } void f_class() { W!C a = new C(); const W!C b = new C(); immutable W!C c = new immutable C(); const W!C d = a; //immutable W!C e = a; // cannot implicitly convert mutable to immutable } void f_struct() { W!S0 a = S0(); const W!S0 b = S0(); immutable W!S0 c = S0(); const W!S0 d = a; immutable W!S0 e = a; } void f_struct_with_indirection() { W!S1 a = S1(); const W!S1 b = S1(); immutable W!S1 c = immutable S1(); const W!S1 d = a; // immutable W!S1 e = a; // cannot implicitly convert mutable to immutable } void f_wrapper() { auto a = wrap(new C); auto b = wrap(new const C); auto c = wrap(new immutable C); const W!C d = a; // immutable W!C e = a; // cannot implicitly convert mutable to immutable } void f_wrapper2() { Object ma = new C(); Object ca = new const C(); Object ia = new immutable C(); auto a = wrap(cast(C)ma); auto b = wrap(cast(const C)ma); auto c = wrap(cast(immutable C)ma); } void main() { }
Jul 29 2018
parent reply aliak <something something.com> writes:
On Sunday, 29 July 2018 at 12:45:48 UTC, Steven Schveighoffer 
wrote:
 
 Am I applying inout incorrectly?
No, you need to apply it to wrap as well. I can't get run.dlang.io to work for posting a link, so here is my modified version:
Ah bugger, right! Ok so there's no way to make explicit instantiation involving immutable work in the face of an inout parameter? Seems rather inconsistent no? https://issues.dlang.org/show_bug.cgi?id=19126 Thanks, - Ali
Jul 29 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/29/18 1:46 PM, aliak wrote:
 On Sunday, 29 July 2018 at 12:45:48 UTC, Steven Schveighoffer wrote:
 Am I applying inout incorrectly?
No, you need to apply it to wrap as well. I can't get run.dlang.io to work for posting a link, so here is my modified version:
Ah bugger, right! Ok so there's no way to make explicit instantiation involving immutable work in the face of an inout parameter? Seems rather inconsistent no?
It's not that there's no way, the issue is simply that you are explicitly instantiating incorrectly. wrap!int(immutable(int)(3)); -Steve
Jul 31 2018
parent reply aliak <something something.com> writes:
On Tuesday, 31 July 2018 at 12:37:34 UTC, Steven Schveighoffer 
wrote:
 On 7/29/18 1:46 PM, aliak wrote:
 On Sunday, 29 July 2018 at 12:45:48 UTC, Steven Schveighoffer 
 wrote:
 Am I applying inout incorrectly?
No, you need to apply it to wrap as well. I can't get run.dlang.io to work for posting a link, so here is my modified version:
Ah bugger, right! Ok so there's no way to make explicit instantiation involving immutable work in the face of an inout parameter? Seems rather inconsistent no?
It's not that there's no way, the issue is simply that you are explicitly instantiating incorrectly. wrap!int(immutable(int)(3)); -Steve
Ok bear with me, but I'm really confused why "wrap!int(immutable(int)(3))" is "correct". This all seems very inconsistent: 1. wrap!(int)(3); // ok 2. wrap!(const int)(3); // ok 3. wrap!(immutable int)(3); // nope 4. wrap!(int)(3); // ok 5. wrap!(const int)(const(int)(3)); // ok 6. wrap!(immutable int)(immutable(int)(3)); // ok! So for 3, compiler sees the instantiation: inout(W!(immutable int)) wrap(immutable(int)) If I understood you correctly? But then what does it see in number 6, which works fine? And why is 2 ok if 3 is not? And finally, why can't the compiler leave the inout there and then it doesn't need to complain about it? Cheers, - Ali
Jul 31 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/31/18 5:29 PM, aliak wrote:
 On Tuesday, 31 July 2018 at 12:37:34 UTC, Steven Schveighoffer wrote:
 On 7/29/18 1:46 PM, aliak wrote:
 On Sunday, 29 July 2018 at 12:45:48 UTC, Steven Schveighoffer wrote:
 Am I applying inout incorrectly?
No, you need to apply it to wrap as well. I can't get run.dlang.io to work for posting a link, so here is my modified version:
Ah bugger, right! Ok so there's no way to make explicit instantiation involving immutable work in the face of an inout parameter? Seems rather inconsistent no?
It's not that there's no way, the issue is simply that you are explicitly instantiating incorrectly. wrap!int(immutable(int)(3));
Ok bear with me, but I'm really confused why "wrap!int(immutable(int)(3))" is "correct".
Because inout is trying to combine all mutability modifiers into one. You want to specify the type, not the mutability, in the template parameter T. Essentially, if this were a normal function that takes only ints, you would write it once for all mutabilities: auto wrap(inout(int) x) Which works for mutable, const, or immutable int.
 
 This all seems very inconsistent:
 
 1. wrap!(int)(3); // ok
 2. wrap!(const int)(3); // ok
 3. wrap!(immutable int)(3); // nope
 4. wrap!(int)(3); // ok
 5. wrap!(const int)(const(int)(3)); // ok
 6. wrap!(immutable int)(immutable(int)(3)); // ok!
This doesn't make sense. Can you post runnable code? When I go back to your original failing example, and replace the 3 with immutable(int)(3), it still fails.
 
 So for 3, compiler sees the instantiation:
 
   inout(W!(immutable int)) wrap(immutable(int))
 
 If I understood you correctly?
Yes. You can see for yourself with pragma msg: pragma(msg, typeof(t)); // immutable(int)
 
 But then what does it see in number 6, which works fine?
I'm skeptical this is the case. Note that you may only see the instantiation error ONCE.
 And why is 2 ok 
 if 3 is not?
because inout(const(T)) cannot have its inout removed.
 And finally, why can't the compiler leave the inout there 
 and then it doesn't need to complain about it?
It has to strip inout to be consistent -- we want canonical types. It's the same reason immutable(const(int)) is just immutable(int), and const(const(const(int))) is just const(int). Having the original types be left in place would make for some weird rules. But the complaint is really the issue. Clearly inout is specified, so it shouldn't complain that it isn't. -Steve
Jul 31 2018
parent aliak <something something.com> writes:
On Tuesday, 31 July 2018 at 21:54:54 UTC, Steven Schveighoffer 
wrote:
 Because inout is trying to combine all mutability modifiers 
 into one. You want to specify the type, not the mutability, in 
 the template parameter T.
Ahhh. Ok I see... I think.
 This doesn't make sense. Can you post runnable code?
Hehe, ok, so I fell victim to compiler generating an error for number 3 and then nothing else :p But yes you're right, if I comment out number 3 then 6 errors as well. Sorry my bad!
 When I go back to your original failing example, and replace 
 the 3 with immutable(int)(3), it still fails.

 
 So for 3, compiler sees the instantiation:
 
   inout(W!(immutable int)) wrap(immutable(int))
 
 If I understood you correctly?
Yes. You can see for yourself with pragma msg: pragma(msg, typeof(t)); // immutable(int)
 
 But then what does it see in number 6, which works fine?
I'm skeptical this is the case. Note that you may only see the instantiation error ONCE.
Yep, you nailed that one.
 And why is 2 ok if 3 is not?
because inout(const(T)) cannot have its inout removed.
Because it may be an immutable? Or?
 But the complaint is really the issue. Clearly inout is 
 specified, so it shouldn't complain that it isn't.
Aye. I guess that's right.
 -Steve
Jul 31 2018
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/23/18 8:02 AM, aliak wrote:
 On Sunday, 22 July 2018 at 23:11:09 UTC, Ali Çehreli wrote:
 Without much confidence on my side, first, I think you need to make 
 the constructor parameter inout(T) as well. Otherwise, you may be 
 making a const(W!T) initialized with a non-const T.

 After that, I like the "type constructor" syntax in main_alt() below 
 (which works) but a better approach is to use a convenience function 
 like wrap() below:

 struct W(T) {
     T val;
     this(inout(T) val) inout {
         this.val = val;
     }
 }

 class C {}

 void main_alt() {
     auto a = W!C(new C);
     auto b = immutable W!(immutable C)(new C);
 }

 auto wrap(T)(inout T t) {
     return inout(W!T)(t);
 }

 void main() {
     auto a = wrap(new C);
     auto b = wrap(new immutable(C));
 }

 Ali
 "taklitlerinden sakınınız" :o)
Thank you Ali! That helped :) I've gotten most of it sorted out now, and the factory wrap is definitely the way to go, it also turned out that inout(T) and inout T (so inout without parens) was surprisingly different (maybe it's a bug? - to test you can remove the parens around U on line 3 in this sample: https://run.dlang.io/is/gd5oxW
This seems like a bug to me. The semantic difference is that inout(U) means the TYPE inout(U), whereas inout U means the variable U has the STORAGE CLASS inout, which also happens to make it an inout(U) type. As far as I know, the storage class inout shouldn't have any other effect on the semantic meaning. I can't imagine any difference there, but it appears to not recognize that return should be inferred? I don't know.
 Also over there, line 24:
 
 auto si = wrap!(immutable int)(3);
 
 seems to be giving problems. Any ideas there? Error is:
 
 onlineapp.d(8): Error: inout on return means inout must be on a 
 parameter as well for pure nothrow  nogc  safe 
 inout(W!(immutable(int)))(immutable(int) t)
 onlineapp.d(23): Error: template instance 
 `onlineapp.wrap!(immutable(int))` error instantiating
The problem here is that inout(immutable(int)) is equivalent to immutable(int). That is, all flavors of mutability are equivalent to immutable(int): /*mutable*/(immutable(int)) => immutable(int) const(immutable(int)) => immutable(int) immutable(immutable(int)) => immutable(int) So the compiler really looks at your wrap instantiation like this; inout(W!(immutable(int))) wrap(immutable(int) t) which triggers the (really bad) message. I'd ask, why are you even worrying about explicit instantiation? Why not just wrap(3)? or (if you really want to test it) wrap(immutable(int)(3))?
 
 To make it compile successfully you can either:
 
 1) Chance immutable to const, then it works for some reason.
Because immutable(const(int)) => immutable(int), so the compiler can't remove the inout behind your back.
 2) Change the line to: "auto si = wrap(cast(immutable int)3);" - i.e. do 
 not explicitly provide type information.
Yep, do this :) Note that the point of inout is 2-fold: 1. reduce template instantiations. In fact, wrap!int works for const, mutable and immutable int. 2. ENSURE that the data isn't modified, even in the case of mutable parameters. -Steve
Jul 27 2018
parent aliak <something something.com> writes:
On Friday, 27 July 2018 at 14:34:54 UTC, Steven Schveighoffer 
wrote:
 The problem here is that inout(immutable(int)) is equivalent to 
 immutable(int).

 That is, all flavors of mutability are equivalent to 
 immutable(int):

 /*mutable*/(immutable(int)) => immutable(int)
       const(immutable(int)) => immutable(int)
   immutable(immutable(int)) => immutable(int)

 So the compiler really looks at your wrap instantiation like 
 this;

 inout(W!(immutable(int))) wrap(immutable(int) t)
Ah ok, so the compiler remove inout behind me back here? (And then tells me it needs to be there? :p)
 which triggers the (really bad) message.

 I'd ask, why are you even worrying about explicit 
 instantiation? Why not just wrap(3)?
Just because I don't see why it should not work really. Why not allow wrap!(immutable int)(3)?
 or (if you really want to test it) wrap(immutable(int)(3))?

 
 To make it compile successfully you can either:
 
 1) Chance immutable to const, then it works for some reason.
Because immutable(const(int)) => immutable(int), so the compiler can't remove the inout behind your back.
 2) Change the line to: "auto si = wrap(cast(immutable int)3);" 
 - i.e. do not explicitly provide type information.
Yep, do this :) Note that the point of inout is 2-fold: 1. reduce template instantiations. In fact, wrap!int works for const, mutable and immutable int. 2. ENSURE that the data isn't modified, even in the case of mutable parameters.
Thanks for the explanations! For some reason it's hard to get it all to *just work* right now without the template this. But it's probably some minor detail I'm just overlooking...
 -Steve
Jul 28 2018