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
> }
>
You mustn't do this version because you don't know the state of f without
checking err first.
>
> 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 <[email protected]
> <javascript:>> wrote:
>
>> On 1 Apr 2017, at 11:02, Axel Wagner <[email protected]
>> <javascript:>> wrote:
>>
>> On Sat, Apr 1, 2017 at 1:50 AM, Dave Cheney <[email protected]
>> <javascript:>> wrote:
>>
>>>
>>>
>>> On 1 Apr 2017, at 10:41, Axel Wagner <[email protected]
>>> <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.
>
>
>> 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.
>
>
>>
>>
>>
>>> 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.
>
>
>> 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.
>
>
>>
>>
>> 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 [email protected].
For more options, visit https://groups.google.com/d/optout.