From: Brad C. <br...@cr...> - 2019-08-23 18:07:23
|
Hi Chapel Programmers — As many of you may know from our State of the Project presentation at CHIUW 2019, one of the main things that the Chapel team is currently focused on is getting the language to the point that we can minimize, if not eliminate, backwards-breaking changes in future releases for a core set of language features. To that end, we are working towards making next month's 1.20 release of Chapel the release candidate for what we've been referring to as Chapel 2.0. You can read more about this concept in the slides from CHIUW: https://chapel-lang.org/CHIUW/2019/CHIUW2019-Welcome-SoP.pdf Getting to that release candidate and its intended feature set has necessarily involved some growing pains in the name of improving the language. Examples from recent releases include the move from constructors to initializers, and the introduction of managed class types. This upcoming release features one more similarly significant change (along with several smaller ones), which is to distinguish between class variables that can store 'nil' and those that cannot. Briefly, the idea behind this concept is as follows: Historically, given a class 'C', a variable 'var myC: C = ...' could either store 'nil' or point to an object of type 'C'. In the new world (enabled by default on master yesterday), a variable of type 'C' will not be able to store 'nil' by default. Instead, the type specifier 'C?' indicates the case where a variable may refer to either 'nil' or an instance of 'C'. New syntax like 'myC!' can be used to assert/check that 'myC' is non-nil (halting if it's not). Needless to say, this is a fairly large change for code that uses classes, although one that we think is worthwhile given that it promotes safer coding by default, can reduce the need for nil-checking in many cases, and increases awareness of where nil dereferences may occur at compile-time. Given the impact of this change on Chapel code that uses classes, we wanted to offer up-front to help any users with transitioning their existing Chapel code from 1.19 to 1.20 — either by helping to work through the changes if your code can be shared, or by being even more available than usual to answer questions if not. If this help would be of interest to you, please reply to this mail and let me know. On behalf of the Chapel team at Cray, -Brad Chamberlain |
From: Brad C. <br...@cr...> - 2019-08-24 00:20:19
|
I'll answer the parts of this I remember the answers to offhand and leave the rest for others: > What would an array of `var A : [D] C` contain by default? This would be an error (assuming a semicolon after the 'C' rather than an `= ...`, as would `var A: C;` However, I believe at present, the compiler only checks the latter, not the former (i.e., there's still a TODO here). > Why not make all types nil-able by default and make nil-ability an > opt-in feature? That is a potential consideration for the future (but is not considered a breaking change since no other types are currently nilable). -Brad |
From: Brad C. <br...@cr...> - 2019-08-24 05:07:59
|
Hi Louis — I think there are a few options here (and probably others that the experts could suggest... in this case, I'm mostly a messenger who's just trying these features out for the first time today as well, so still picking up some tricks as I go). First an assumption: I take it that `recycleList.remove(n)` is going to store the thing that's removed in `n`? (i.e., `n = recycleList.remove();` might be another way to express it showing the assignment?) The first way to write this pattern that occurs to me is as follows, where I'm going to take the above liberty with reframing the recycleList API: proc recycle(): unmanaged object { try { return recycleList.remove(); } catch exception { return new unmanaged object(); } } Here, both branches return a non-nilable unmanaged object (assuming remove() does when it doesn't throw), so things are fine. Another way would be to use the '!' operator (and here I'll revert to the original API to avoid cheating): proc recycle(): unmanaged object { var n: unmanaged object?; // this needs to use ? since n will // initially be nil try { recycleList.remove(n); } catch exception { n = new unmanaged object(); } return n!; // will halt if 'n' is nil, but you've assured me it won't } Note, however, that if the try block throws an exception that doesn't match your exception pattern, 'n' will be 'nil' and the ! operator will fail. I think a third approach would be to cast 'n' to 'unmanaged object', but I'm not as expert at that approach yet (casting nilability away throws when the value is 'nil' whereas '!' halts). To your larger point, though, I think you're correct that nilability can't be proven correct by a compiler in all cases (I suspect its equivalent to the halting problem), so we do what compilers typically do: Be conservative when we can't be sure and rely on the user to help out in such cases. That said, note that checking nilability in Chapel is more about making sure that you don't assign a `nil` or a `C?` to a `C` without first doing something to convert it safely (like the '!' operator or a cast). And less about trying to infer whether something is assigned non-nil values along all paths or not. -Brad On Sat, 24 Aug 2019, Louis Jenkins wrote: > **Memory/Object Pool Pattern** > > If I know for a fact that a function will always return an object that > is not nil, such as the case of an object pool that will create a new > object if an object cannot be recycled, how do you represent this with > the new changes? It seems that the compiler cannot be determine if the > nil-ability of an object can be proven statically, such as the case in > the below code snippet; the compiler _could_ see that, in any and all > paths, it is impossible for `n` to be `nil`, and is therefore not > nil-able; a lot of static analyzers are capable of performing such > analysis as well. Would this be considered something 'impossible' to do > from a design standpoint, or would this be something that is planned to > be supported? If not, how do you prevent nil-ability tainting > everything? Is it planned to be able to say to the compiler: "I know > that this is not nil-able after this point, please stop interfering with > my code?" - I kid, I kid, but really, is there a way to tell it that > something is non-nilable after a point? > > """ > use List; > > var recycleList : list(unmanaged object); > proc recycle() : unmanaged object { > var n : unmanaged object; // ??? > try { > recycleList.remove(n); > } catch exception { > n = new unmanaged object(); > } > return n; > } > > var x = recycle(); > writeln(x); > """ > > > On 8/23/19, 8:20 PM, "Brad Chamberlain" <br...@cr...> wrote: > > > I'll answer the parts of this I remember the answers to offhand and leave > the rest for others: > > > What would an array of `var A : [D] C` contain by default? > > This would be an error (assuming a semicolon after the 'C' rather than an > `= ...`, as would `var A: C;` However, I believe at present, the compiler > only checks the latter, not the former (i.e., there's still a TODO here). > > > > Why not make all types nil-able by default and make nil-ability an > > opt-in feature? > > That is a potential consideration for the future (but is not considered a > breaking change since no other types are currently nilable). > > -Brad > > > > |
From: Brad C. <br...@cr...> - 2019-08-24 05:29:40
|
Hi Louis — I think this gets back to your original question which I skipped hoping one of the experts would weigh in... I can't find the original mail (even in the online archives... that's odd), but I believe you asked whether there was a way to turn off execution-time checks from '!', e.g. in --fast / --no-checks mode? I think that question is still being debated here: https://github.com/chapel-lang/chapel/issues/13603 > (there is some additional logic that must be performed after obtaining > the recycled node and so we cannot return immediately). Even if you can't return immediately, you could do the following, since the initial assignment to `n` would be non-nil if its declaration was moved into the try block (noting that I've messed with your remove() API again... without that, you'd still need to use a '!' within the try block I think): proc recycle(): unmanaged object { try { const n = recycleList.remove(); ... do other stuff with n, just not setting it to nil ... return n; } catch exception { return new unmanaged object(); } } -Brad On Sat, 24 Aug 2019, Louis Jenkins wrote: > The `list.remove` function takes a reference and throws when empty (it's > the new standard module), but anyway, the example was more or less a > simplified version of actual code being used (there is some additional > logic that must be performed after obtaining the recycled node and so we > cannot return immediately). > > Thank you for informing me that the `!` operator does away with the > nil-able type, but is it possible to elide the nil-check here? This is > additional overhead that can still be avoided in the recycle list > example. If an additional branch is added to a critical path when it can > be avoided, it can end up being very undesirable. > > Get Outlook for Android<https://aka.ms/ghei36> > > ________________________________ > From: Brad Chamberlain <br...@cr...> > Sent: Saturday, August 24, 2019 1:07:46 AM > To: Louis Jenkins <Lou...@ho...> > Cc: Chapel Users Mailing List <cha...@li...>; Chapel Sourceforge Developers List <cha...@li...>; Chapel Educators Mailing List <cha...@li...> > Subject: Re: [Chapel-developers] Chapel 1.20: Offer to help with nilability changes > > > Hi Louis — > > I think there are a few options here (and probably others that the experts > could suggest... in this case, I'm mostly a messenger who's just trying > these features out for the first time today as well, so still picking up > some tricks as I go). > > First an assumption: I take it that `recycleList.remove(n)` is going to > store the thing that's removed in `n`? (i.e., `n = recycleList.remove();` > might be another way to express it showing the assignment?) > > The first way to write this pattern that occurs to me is as follows, where > I'm going to take the above liberty with reframing the recycleList API: > > proc recycle(): unmanaged object { > try { > return recycleList.remove(); > } catch exception { > return new unmanaged object(); > } > } > > Here, both branches return a non-nilable unmanaged object (assuming > remove() does when it doesn't throw), so things are fine. > > Another way would be to use the '!' operator (and here I'll revert to the > original API to avoid cheating): > > proc recycle(): unmanaged object { > var n: unmanaged object?; // this needs to use ? since n will > // initially be nil > try { > recycleList.remove(n); > } catch exception { > n = new unmanaged object(); > } > return n!; // will halt if 'n' is nil, but you've assured me it won't > } > > Note, however, that if the try block throws an exception that doesn't > match your exception pattern, 'n' will be 'nil' and the ! operator will > fail. > > I think a third approach would be to cast 'n' to 'unmanaged object', but > I'm not as expert at that approach yet (casting nilability away throws > when the value is 'nil' whereas '!' halts). > > > To your larger point, though, I think you're correct that nilability can't > be proven correct by a compiler in all cases (I suspect its equivalent to > the halting problem), so we do what compilers typically do: Be > conservative when we can't be sure and rely on the user to help out in > such cases. > > That said, note that checking nilability in Chapel is more about making > sure that you don't assign a `nil` or a `C?` to a `C` without first doing > something to convert it safely (like the '!' operator or a cast). And > less about trying to infer whether something is assigned non-nil values > along all paths or not. > > -Brad > > > On Sat, 24 Aug 2019, Louis Jenkins wrote: > >> **Memory/Object Pool Pattern** >> >> If I know for a fact that a function will always return an object that >> is not nil, such as the case of an object pool that will create a new >> object if an object cannot be recycled, how do you represent this with >> the new changes? It seems that the compiler cannot be determine if the >> nil-ability of an object can be proven statically, such as the case in >> the below code snippet; the compiler _could_ see that, in any and all >> paths, it is impossible for `n` to be `nil`, and is therefore not >> nil-able; a lot of static analyzers are capable of performing such >> analysis as well. Would this be considered something 'impossible' to do >> from a design standpoint, or would this be something that is planned to >> be supported? If not, how do you prevent nil-ability tainting >> everything? Is it planned to be able to say to the compiler: "I know >> that this is not nil-able after this point, please stop interfering with >> my code?" - I kid, I kid, but really, is there a way to tell it that >> something is non-nilable after a point? >> >> """ >> use List; >> >> var recycleList : list(unmanaged object); >> proc recycle() : unmanaged object { >> var n : unmanaged object; // ??? >> try { >> recycleList.remove(n); >> } catch exception { >> n = new unmanaged object(); >> } >> return n; >> } >> >> var x = recycle(); >> writeln(x); >> """ >> >> >> On 8/23/19, 8:20 PM, "Brad Chamberlain" <br...@cr...> wrote: >> >> >> I'll answer the parts of this I remember the answers to offhand and leave >> the rest for others: >> >> > What would an array of `var A : [D] C` contain by default? >> >> This would be an error (assuming a semicolon after the 'C' rather than an >> `= ...`, as would `var A: C;` However, I believe at present, the compiler >> only checks the latter, not the former (i.e., there's still a TODO here). >> >> >> > Why not make all types nil-able by default and make nil-ability an >> > opt-in feature? >> >> That is a potential consideration for the future (but is not considered a >> breaking change since no other types are currently nilable). >> >> -Brad >> >> >> >> > |