>From a previous life (with Icon), I'm almost tempted to return as erroneous as possible a value along with a non-nil error. On a 36-bit Honeywell, there was a value that was an illegal int, float and pointer. I want to return that (;-))
--dave (wearing his Dr Evil hat) c-b [Are you the Dave Cheney from the University of Waterloo? Thoth, Port and the electric trains course?] On Saturday, April 1, 2017 at 7:41:45 AM UTC-4, Dave Cheney wrote: > > > > 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> wrote: >> >>> On 1 Apr 2017, at 11:02, Axel Wagner <axel.wa...@googlemail.com> wrote: >>> >>> On Sat, Apr 1, 2017 at 1:50 AM, Dave Cheney <da...@cheney.net> wrote: >>> >>>> >>>> >>>> On 1 Apr 2017, at 10:41, Axel Wagner <axel.wa...@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. >> > > 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.