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 <d...@cheney.net> wrote:

> On 1 Apr 2017, at 11:02, Axel Wagner <axel.wagner...@googlemail.com>
> wrote:
>
> On Sat, Apr 1, 2017 at 1:50 AM, Dave Cheney <d...@cheney.net> wrote:
>
>>
>>
>> On 1 Apr 2017, at 10:41, Axel Wagner <axel.wagner...@googlemail.com>
>> 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 golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to