On Saturday, 1 April 2017 22:26:20 UTC+11, Axel Wagner wrote:
>
> Ian:
> Re your question: See my example given above (or the one below, which is 
> probably more authentic). For example, you might be allocating the returned 
> struct, and piece by piece filling in the fields. If there can be errors, 
> the natural expression might be, to just return the allocated struct, 
> whereas to then return nil, you need to explicitly branch. For example, say 
> I'd want to have a type which operates on some file:
>
> type Foo struct {
>     file *os.File
> }
>
> func NewFoo(fname string) (*Foo, error) {
>     f, err := os.Open(fname)
>     return &Foo{
>         file: f,
>     }, err
> }
>
> vs.
>
> func NewFoo(fname string) (*Foo, error) {
>     f, err := os.Open(fname)
>     if err != nil {
>         return nil, err
>     }
>     return &Foo{
>         file: f,
>     }, nil
> }
>
> I would usually write the latter version, even if the former is shorter 
> and the extra branch isn't necessary, because people shouldn't rely on the 
> first return if there's an error anyway.
> Because I do feel like people might not be so careful and then 
> dereferencing a nil *Foo will be a clearer symptom to debug, than debugging 
> whatever weird value Open might theoretically return being used 
> accidentally.
>
> On Sat, Apr 1, 2017 at 2:26 AM, Dave Cheney <da...@cheney.net 
> <javascript:>> wrote:
>
>> On 1 Apr 2017, at 11:02, Axel Wagner <axel.wa...@googlemail.com 
>> <javascript:>> wrote:
>>
>> On Sat, Apr 1, 2017 at 1:50 AM, Dave Cheney <da...@cheney.net 
>> <javascript:>> wrote:
>>
>>>
>>>
>>> On 1 Apr 2017, at 10:41, Axel Wagner <axel.wa...@googlemail.com 
>>> <javascript:>> wrote:
>>>
>>> So… Given that I'm *not* talking about modifying any contract - see a) 
>>> in my previous message - but just making an effort that I'm not contractual 
>>> bound by, I am not sure how I am supposed to read this. Is this an argument 
>>> for not being helpful? Because I don't quite see how your point invalidates 
>>> that. Or is it an argument for being hurtful? Which I also don't really 
>>> see, as I'm not talking about any change in contract.
>>>
>>> Like, I legit starting to doubt my sanity here; I don't see how I can 
>>> actually be any clearer about how I do not intend to change anything about 
>>> the "if a non-nil error is returned, assume the returns are invalid" rule. 
>>> The question is "how is it hurtful, if I then also add an extra layer of 
>>> defense against people violating that contract"?
>>>
>>>
>>> But why? Why encourage people to be reckless. IMO this is difference 
>>> between map ordering during iteration being undefined, which it is, rather 
>>> than guaranteed to be random, which is not.
>>>
>>
>> Great point. Why does gc implement it that way, then? And does it hurt, 
>> that gc implement it that way? The contract does not contain anything about 
>> the iteration order, so why did we add that code and CPU time to explicitly 
>> randomize it, instead of just letting buggy code be buggy and blow up at 
>> some point with hard to debug errors? This seems to be essentially the 
>> argument you are making, so why does it, seemingly, not apply to randomized 
>> map iteration in gc?
>>
>>
>> Map ordering is undefined by the spec, some implementations choose to 
>> randomise the order but this is an implementation detail.
>>
>
> But that is what I'm saying. The equivalency of the spec here is the API 
> contract and I am not changing that, but I'm changing an implementation 
> detail (and also reserve the right to change that later). It is incredibly 
> frustrated that you seem to continue to suppose I'm trying to somehow make 
> this a guarantee or part of the API contract, when I said so often that I'm 
> not.
>

But you are. I've been very clear that if there is an error the caller can 
assume nothing about the state of an other variable; they are, like the 
floor, lava.
 

>  
>
>> Both are contracts with the same result to the casual user -- map 
>>> iteration is unpredictable, but by not guaranteeing that the order will be 
>>> random, it prevents people relying on the side effect.
>>>
>>> This is the argument I'm making now, yes, you could go to effort to make 
>>> sure that some of the values you return are nil so that they explode as 
>>> soon as someone forgets to check an error, but you probably shouldn't 
>>> because
>>>
>>> A, this is providing a stronger contract than necessary.
>>>
>>
>> No, it is not. The contract is the same. I'm sorry to be a stickler here, 
>> but I really don't see why this point is so elusive. I am not suggesting 
>> adding a "if an error is returned, the other return values will have their 
>> zero value" to my godoc.
>>
>>
>> No, it's not. One is "in the presence of an error, the other values 
>> returned are undefined", your contract is "in the presence of an error, the 
>> others values returned will be their respective zero value"
>>
>
> No, this is categorically not what I'm suggesting and I have been very 
> explicit about that.
> I'm saying "I'll try to do X, because I think it's sometimes helpful", not 
> "I promise you that I will always do X, so trust me on this". Just like gc 
> decided to randomize the map-iteration order to expose bugs where code 
> relies on iteration order but did not change the spec 
>

You cannot write a useful API contract that says "in the event of an error, 
we'll try to return a useful zero value", because a confirming 
implementation can simply _not_ return a useful zero value, and still be 
compliant with the contract of the API. I hold as an example, 
runtime.SetFinalizer
 

>
>>  
>>
>>> B, it encourages people to be clever and try to avoid the error checking 
>>> idiom.
>>>
>>
>> I legit don't see how, given that this is not a rule. I also don't do it 
>> with any kind of strictness that would allow people to rely on it.
>>
>>
>> In your scheme, if there is an error, the function 
>>
>> func NewT() (*T, error)
>>
>> I could check if the value of T is nil, or the error is nil, to confirm 
>> the error condition.
>>
>
> But then your code is buggy. Because that is not part of the API. 
>

But this is what your API contract allows; you've committed to the caller 
that in the event of and error, T _WILL_ be nil, and people will rely on 
that. I argue it's better to save people from themselves and offer no 
guarentee on the state of any other return values in the case of an error.
 

>  
>
>> C, doesn't work for all return values, only the pointer shaped ones.
>>>
>>
>> I disagree. An empty string or a 0 or whatever is *still* a much more 
>> telling symptom to debug than *some* string/integer/whatever, especially if 
>> it's an invalid value (and if it isn't; why is would we even talk about it).
>>
>>
>> But now you've introduced the problem that sometimes func f() (int, 
>> error) might return 0, but you don't know if that's a valid value, or the 
>> zero value--without checking the error value, so your back to square one.
>>
>
> *exasperated sigh*
> So? Yes, of course errors need to be checked. Yes, of course, not always 
> will people look at the return and say "well, that can't be right". Of 
> course, not every API lends itself to this.
> But that's okay because this is not about changing any API contracts or to 
> reduce any requirement on checking error values or anything… but just to 
> *sometimes* add a branch to *sometimes* make things blow up in a clearer 
> way if *sometimes* people forget to do it.
>

Sure, it's nice the be helpful, but where does it stop? I *guarantee* you 
that people will built upon the contract of this method; knowing that all 
the return values are valid; even in the presence of errors.

How many times do we see, on a daily basis, people post code without 
appropriate error checking to this mailing list, to the bug tracker, to 
other support venues, because it mostly works; and because it mostly works 
it'll keep working right up to the point there is an error in their 
production code. 

It is simpler, and more importantly more consistent, which helps all users 
of all APIs, not just the ones who are lucky enough to use yours, to stick 
to the error contract; if an error occurs you cannot say anything about the 
state of the other return values, they are simply undefined.
 

>  
>
>>
>>
>> But anyway, yes, I mostly do this with pointers, but *so what*? Why throw 
>> the baby out with the bathwater? Again, this is not part of any API. This 
>> is not an all or nothing thing. This is a safety net for people coding bugs 
>> and it's totally fine if it is there sometimes and not there at other times 
>> (and you even argue yourself that it shouldn't, to "keep people on their 
>> toes").
>>
>>
>> People will build castles on your foundation of unexpected side effects. 
>> You want to avoid that. 
>>
>
> Then why does gc randomizes map-iteration order? People might rely on 
> that. And the spec clearly says it's unspecified, so we shouldn't implement 
> a particular behavior beyond what is required by spec…
>
> Anyway. I *do* give up. I don't think this is an actual controversy, but 
> apparently it is either impossible for me to express unambiguously what I'm 
> talking about or impossible for others to read what I'm writing. Because I 
> did try, in every E-Mail, to be unambiguously clear but it still just got 
> ignored again… I give up.
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to