My point is that your proposal appeared to rely on the language handling "if p != nil" in a certain way. Unless I misunderstand, that is how you implicitly convert a ?T into a *T. That needs to be spelled out in a way that always works.
Because the caller should not allocate a zero length ring. All pointers to rings are zero length rings by default.But ring.New(0) fails.
1. It is an example where notnullness does not make things easier because you know the pointers in rings are never null so by definition notnullness can never help in this case. It just makes things uglier (but not too much).It's exactly because the pointers are never null, but that allocation nevertheless returns null values, that ring.go is interesting.
2. What makes ring.go really ugly is that the original one has a broken API. The ring allocating function has to work for 0 and negative values as well which is just plain wrong!!! It is wrong conceptually that New(-1) makes the same result as New(0). New(-1) should simply be an error in my opinion.New(0) should work. New(-1) is not very interesting. I think it would be fine to simply have it fail.
What a bigger problem is that either the caller has to handle the empty ring differently or the ring should check in every function whether the ring is empty. The original ring.go simply crashed when called with empty rings (except Len() and Iter()) and it even had comments about not to call with empty rings but we both know that nobody reads comments...Calling Next or Prev on an empty ring is in effect an out-of-bounds array access. A Go program will crashe on an out-of-bounds array access. It seems perfectly reasonable to me that it should crash on a Next or Prev call on an empty ring. What else should happen?
It would add different * and ? notation and the shadow type which will not be used directly by 99% of programmers. It would also add type conversion rules which are totally natural because this is the minimum required support. I can understand if it is too much for eliminating null pointer crashes but calling it *significant* complication is a little bit unrealistic to me.For example, you haven't considered the effect on type reflection and type assertions. It's going to get increasingly complicated.
Thanks for the updated Ring. It seems to me that any Ring which can be empty will have to be written as ?Ring.
Then, if I understand correctly, code like if ring.Len() != 0 { next = ring.Next(); } won't compile. You will have to write if ring != nil { next = ring.Next(); } which I guess is OK though it may be confusing.
Now this decision unfortunately will make creating a sandboxed Go environment impossible. Please reconsider. (This little bit is what makes Go's memory model different from the .NET v1 memory model if I am not mistaken.)Can you explain this further? Note that any other decision has some very serious performance costs when using global variables. .NET is quite different since (as I understand it) .NET code is run by a JIT.
2. Interfaces have two processor word sized entries and so will be observed by another thread independently (can observe a vtable for type1 while a pointer to a struct of type2).We know how to fix this: you have to make an interface value a pointer to those two words, never overwrite them, and let the garbage collector clean up the mess.
3. Can similar problems happen with channels and slices?Slices yes, channels no. The fix for slices is similar but can still avoid reallocation on each slice operation.The second can be fixed by writing strongly (simple write on x86) a zero to the object pointer before updating an interface which is not on the stack.No it can't. You have to change the memory layout.
Let me try again. You have to spell out in precise detail when the language permits an assignment from ?T to *T. For example, is this OK? if p.next != nil { var x *T = p.next; }From the proposal: “p = q” only allowed if it can be inferred from an “if q != nil” or from a “q = qq” where qq *T that q is not null. So yes, it is OK.Aside from the thread safety issue, here we are talking about the language, not any particular implementation. We have to be able to write a specification such that all implementations of the language do the same thing. "It can be inferred" is not a specification. How is this inference done? When does it work and when does it fail?
I understand that you are not proposing anything about out-of-bounds array access, but I am curious as to your position on how those should be handled. Am I wrong in seeing the cases as related?They are absolutely similar. And as I see you have already invented slices!!! You just do not like notnull pointers...Slices are essentially a safe way to do pointer arithmetic. I don't really see them as related to out-of-bounds array access. You can have out-of-bounds access to a slice, too.
The main difference is that statical array bound checking requires first order logic while pointers do not. In the general sense neither problem can be solved generally (== halting problem) but my instinct tells me (and we know from history) that at least notnull pointers can be done practically. (This is equivalent with the attackable sentence in the prior message.) I do not think that I could propose a similar proposal for array indices so I just do not do it. Slices should make array indices much more safe and that is all we can do about that (that is my belief).We know that there are other languages which supports non-null pointers. What do those languages do about out-of-bounds array accesses?
>> Aside from the thread safety issue, here we are talking about the
>> language, not any particular implementation. We have to be able to
>> write a specification such that all implementations of the language do
>> the same thing. "It can be inferred" is not a specification. How is
>> this inference done? When does it work and when does it fail?
>>
>>
>
> Now as I have read the Eiffel notnullness specification, I can see
> that my proposal is 90% of the Eiffel specification if we fix the two
> problems Nigel Tao has shown me. Is there a remote chance that you
> will ever include notnullness to the language or you just let people
> wasting their time writing endless proposals because you do not ever
> consider them? I ask this because for me it takes ~10x as much to
> write something in English than it takes for you.
I think it is very unlikely that we will add notnullness to the
language. But if somebody produced a clear proposal which addressed
all issues and did not make the language more complex then we would
consider it seriously. That is, we are not opposed to the idea in
theory; we are opposed to the practical consequences of the idea.
I apologize if I seem to be leading you on. I was responding to your
proposal by asking questions to make it clear that (I think) there are
critical areas which your proposal did not address.
> (BTW there was no
> thread safety issue in my proposal.)
Yes, there is a thread safety issue. You wrote that in code like
this:
if p.next != nil {
var x *T = p.next
}
that x could be presumed to be non-nil. But that is only true if
p.next did not change between the test and the assignment. Since the
goal of your proposal, as I understand it, is to ensure that nil
pointers can not occur when a value is presumed to be non-nil, you
have to consider that one way or another. You can explicitly say that
you don't care about this kind of race condition--that is, you can say
that it is OK if a program with race conditions crashes due to a nil
pointer dereference--but I don't think you can simply ignore it.
Thanks for the information about Spec#. I think the distinction
between *T and ?T could be seen as another way of writing a
precondition.
Ian
This "90% of Eiffel" has meant that there is no way (I could think of)
that notnullness could be implemented with less new language features.
Eiffel authors did not do that and I do not even consider Spec# which is
more complex than Eiffel (in my opinion). However it does not mean too
much new language elements:
1. *p and ?p (and one of them already exists)
2. shadow type (almost the same as checking uninitialized variables)
3. conversion rules
4. "exceptions" as primitive post conditions
Now if "not more complex" means only accepting the first one then I
agree, notnullness will not happen in Go anytime.
> I apologize if I seem to be leading you on. I was responding to your
> proposal by asking questions to make it clear that (I think) there are
> critical areas which your proposal did not address.
>
>
Ehem, I have no idea what this "I seem to be leading you on" expression
means in English but apology accepted blindly... :)
Seriously: I really have no idea what part from my message does it
answer. What is sure that writing something complex in English takes 10x
as much time for me than for you so I would rather save my time if you
could just tell me not to bother to do something. The problem is when
you (here I mean you == Go authors) completely avoid answering some idea
on this list so at least a one liner such as: "over my dead body" (or:
"it is really a bad idea") would allow me not wasting my time (and it
seems to me that it is not only my opinion).
> Yes, there is a thread safety issue. You wrote that in code like
> this:
>
> if p.next != nil {
> var x *T = p.next
> }
>
No, it should use the second time the cached p.next which != nil if the
compiler would follow my proposal. It is not a race condition just
confusing code. But not a race condition.... :) So I do not say that
this should not be fixed in the proposal just not a race condition.
>> I think it is very unlikely that we will add notnullness to the
>> language. But if somebody produced a clear proposal which addressed
>> all issues and did not make the language more complex then we would
>> consider it seriously. That is, we are not opposed to the idea in
>> theory; we are opposed to the practical consequences of the idea.
>>
>>
>
> This "90% of Eiffel" has meant that there is no way (I could think of)
> that notnullness could be implemented with less new language
> features. Eiffel authors did not do that and I do not even consider
> Spec# which is more complex than Eiffel (in my opinion). However it
> does not mean too much new language elements:
> 1. *p and ?p (and one of them already exists)
> 2. shadow type (almost the same as checking uninitialized variables)
> 3. conversion rules
> 4. "exceptions" as primitive post conditions
> Now if "not more complex" means only accepting the first one then I
> agree, notnullness will not happen in Go anytime.
That approach definitely makes the language more complex. I do not
think that we would implement any proposal along those lines.
>> Yes, there is a thread safety issue. You wrote that in code like
>> this:
>>
>> if p.next != nil {
>> var x *T = p.next
>> }
>>
> No, it should use the second time the cached p.next which != nil if
> the compiler would follow my proposal. It is not a race condition just
> confusing code. But not a race condition.... :) So I do not say that
> this should not be fixed in the proposal just not a race condition.
This is a good example of additional complexity in the language. It
means, for example, that the language can no longer be implemented by
a naive interpreter, but that the interpreter must be aware that
p.next must be cached here. It can be done--nobody disputes that--but
it increases complexity.
Ian
On Dec 7 2009, 11:02 pm, Ken Thompson <k...@google.com> wrote:
> i agree with ian, there is an isomorphism between
> dynamic (vsstatic) catching of an array/slice index
> out of bounds and a reference thru a nil pointer.
> this discussion concentrates on pointers and leaves
> arrays alone. they are exactly the same.
>
> maybe this can be handled outside of the compiler.
> how about someone converting ESC (extendedstaticcheck)
> as a (non-compiler) tool for go. this sounds like value-added
> for both camps. my fear then is that code will become mucked up
> with lots of stupid annotations. my case in point is
> (void)f();
> as a totally stupid side-effect of running lint.
Would the Go creators be interested in an ESC/Go?
Best,
Joe Kiniry (one of "the ESC guys" who has been keeping an eye on Go)
http://kindsoftware.com/
> Hi Ken et al,
>
> On Dec 7 2009, 11:02 pm, Ken Thompson <k...@google.com> wrote:
>> i agree with ian, there is an isomorphism between
>> dynamic (vsstatic) catching of an array/slice index
>> out of bounds and a reference thru a nil pointer.
>> this discussion concentrates on pointers and leaves
>> arrays alone. they are exactly the same.
>>
>> maybe this can be handled outside of the compiler.
>> how about someone converting ESC (extendedstaticcheck)
>> as a (non-compiler) tool for go. this sounds like value-added
>> for both camps. my fear then is that code will become mucked up
>> with lots of stupid annotations. my case in point is
>> (void)f();
>> as a totally stupid side-effect of running lint.
>
> Would the Go creators be interested in an ESC/Go?
I certainly expect that there would be some interesting cases where
static checking could be applied to Go outside of the compiler.
Ian
>
> Yes, there is a thread safety issue. You wrote that in code like
> this:
>
> if p.next != nil {
> var x *T = p.next
> }
>
> that x could be presumed to be non-nil. But that is only true if
> p.next did not change between the test and the assignment. Since the
> goal of your proposal, as I understand it, is to ensure that nil
> pointers can not occur when a value is presumed to be non-nil, you
> have to consider that one way or another. You can explicitly say that
> you don't care about this kind of race condition--that is, you can say
> that it is OK if a program with race conditions crashes due to a nil
> pointer dereference--but I don't think you can simply ignore it.
>
Is the type switch statement in go thread safe? Eg, if we have:
switch i := p.next.(type) {
case nil:
...
case *int:
...
}
Then if the value of p.next is changed by another thread, is the type of
i in the branches of the switch guaranteed to be correct?
It seems to me that the simplest way of introducing non-null pointers
into go would be if variant types were first introduced. Then non-null
pointers could work in exactly the way that they do in ML-style languages:
Suppose that we can declare variant types as follows:
type Foo union { X, Y, Z }
such that Foo is a type like interface{} except that it only accepts
values of the type X, Y or Z. (This is not very much like a C union, so
it might be desirable to pick another name).
Then, ?T would be an alias for union {nil, *T}. It would only be
possible to examine the value of an ?T by using a type switch (or a
function which itself used a type switch). Therefore, access to ?T
would be thread safe if general type switches are thread safe.
Uninitialized *T would be banned, unless it was part of a union also
containing nil.
Potential objections to this that I can see are:
- it might be considered a bit cumbersome to only be able to access ?T
via a switch. ML-like languages tend to add syntactic sugar for dealing
with variant types.
- It may be unobvious when a type contains an *T and therefore can't be
declared uninitialized. This can be mitigated by good compiler error
messages.
Ealdwulf
> Is the type switch statement in go thread safe? Eg, if we have:
> switch i := p.next.(type) {
> case nil:
> ...
> case *int:
> ...
> }
>
> Then if the value of p.next is changed by another thread, is the type
> of i in the branches of the switch guaranteed to be correct?
Yes. The expression is evaluated first and (in effect) stored in a
temporary variable. Then the type switch is done. In each case with
a type, in effect a new local variable is created with the same name,
and the temporary variable is assigned to the new local variable.
Ian
It is solved in Eiffel (and my never will be finished proposal) by
introducing a new name for the shared field/variable's (locals are
thread safe always) current notnull value. The following idiom could do
this without syntactic sugar.
if p := p.next; p != nil {
// here p != nil, strange isn't it... :)
}
However the trick is not how to define nullable and notnull pointer
types but how to make the system useful. Unfortunately Eiffel's (and so
mine) proposal is the minimal plumbing needed what seems too much for Go...
package ...
type notNil struct {
*int
}
func NotNil(ptr *int) (notNil) {
if ptr == nil { panic("pointer is nil, not nil pointer needed") }
return notNil{prt}
}
This way, as long as you always recieve notNil's and never assign to
notNil.int yourself, you'll know that neither you nor anyone using
your package is passing you a nil pointer. Though it seems a bit
overkill to me just to avoid some if statements. But thats just an
idle side thought...
Also a side note, I find the Some x | None approach to "pointers" in
ocaml neat in that nil (None) isn't a value, its an alternative to
having a value, so if you don't offer the alternative, you don't have
nil. I know they aren't really pointers, and they wouldn't fit into
Go, but they're neat nonetheless.
On Dec 3 2009, 1:24 am, NoiseEHC <Noise...@freemail.hu> wrote:
> Hi Russ (and members of this mailing list of course)!
>
> Here is the missing ring.go file I have promised to you. :)
>
> In fact this is a rewrite of a formal proposal which I was starting to
> write in 2009.11.16... It was originally named as “proposal for
> exception handling and notnull pointers” but in the meantime it seemed
> better finishing the notnullness of pointers so here it is.
>
I would really love to see discussion on Exceptions going on, as I
personally consider it the most laking feature, preventing the
adoption of the go language in the large scale (IMHO!). Lua seems to
do well without them and I hacked Lua some time yet never liked it
and there are hacks using pcall / error to circumvent the restriction.
Having said that, has the core team already settled on a descission
wheather exceptions (or function-wise sort of, probly named
differently?) are planed for sometime to become part of the language
besides "exceptions remain an open issue. "?
Actually the last exception proposal was made by me as "[go-nuts] Yet
Another Proposal For Exceptions (and it is almost finished now...)".
Unfortunately it did not get any feedback from Go authors so I cannot
comment on whatever they are settled on. BTW now that we know that there
will be no notnull pointers in Go, my (already simple) proposal could be
simplified even more by permitting only one error condition on a way
like Esko Luontola proposed. (My feeling that it will not be accepted
even though it would just solve the exception problem perfectly.)
> Having said that, has the core team already settled on a descission
> wheather exceptions (or function-wise sort of, probly named
> differently?) are planed for sometime to become part of the language
> besides "exceptions remain an open issue. "?
No, we haven't. Exceptions, um, remain an open issue.
Ian