Sorry - correct link. I missed the subtle change.

> On Jun 7, 2021, at 8:18 PM, Robert Engels <reng...@ix.netcom.com> wrote:
> 
> 
> (I think you pasted the wrong link - that is my code). 
> 
> It is not about being unwilling to admit it. Your explanation/reasoning has 
> not convinced me. 
> 
> Imagine some library declares the EventLogger interface as shown. Acceptable. 
> Someone writes the RecordEvents() method taking an EventLogger. Acceptable. 
> 
> Now, I have a struct I want to use with as an EventLogger (badly named - 
> really EventSource). The code I wrote works fine. Test cases (of Log()) work 
> fine. It fails when used as a source to RecordEvents() (and similar held 
> reference patterns). 
> 
> How do you protect against this? What is the mechanism as a library author?
> 
> Clearly this is a trivial example but similar patterns are everywhere. 
> 
> Compare the Go interface handling with Java’s everything is a reference - 
> much simpler - and then adding value types that are explicit. Or a similar 
> implementation in Rust. In both cases knowing you wrote a correct 
> implementation is much easier. Java has since added annotations for aspects 
> like “thread safe” that cover the atomic aspects. 
> 
> I like Go. A lot. I’ve designed and built systems with millions of LOC. 
> Pointing out aspects that might benefit from changes should be encouraged - 
> if not it’s a religion not a programming language. 
> 
>>> On Jun 7, 2021, at 7:40 PM, Axel Wagner <axel.wagner...@googlemail.com> 
>>> wrote:
>>> 
>> 
>> 
>> 
>>> On Tue, Jun 8, 2021 at 2:05 AM Robert Engels <reng...@ix.netcom.com> wrote:
>>> 
>>> We agree. It needs a pointer receiver to work. The atomic is also needed in 
>>> this case for background logging. 
>>> 
>>> The problem in this case is that recordEvents() has to document that the  
>>> EventLogger passed to recordEvents() must have a pointer receiver for the 
>>> Log() method. There is nothing in the language that allows me to declare it 
>>> nor the compiler to enforce it.
>> 
>> It is possible to write a working implementation of that interface without a 
>> pointer receiver - it just needs to *contain* a pointer: 
>> https://play.golang.org/p/Xm6ASGcCyhR
>> You could also have a slice type, which also can do modifications without a 
>> pointer receiver. Or a map-type. Or a channel.
>> 
>> If you would restrict an interface to require pointer-receiver, you would 
>> wrongly restrict the implementer from all these possibilities.
>> 
>> As is the common wisdom, the user of an interface should not care what the 
>> concrete type implementing an interface is (except if it needs to do a 
>> type-assertions). It's the same wisdom that applies to people wanting to 
>> check if an interface contains a nil-pointer: That check relies on the 
>> assumption that the interface contains a pointer, which shouldn't be nil and 
>> that's not something that should concern the user of an interface.
>> 
>> Again, to be abundantly clear (you still seem unwilling to acknowledge 
>> this): The problem with your code is not the definition or usage of the 
>> interface. It's the definition of the method that is wrong. The 
>> interface-definition is fine and works fine.
>> 
>>> If you don’t see this as suboptimal and an area for improvement I am not 
>>> sure what else I can say.
>> 
>> I just want to state again, clearly, that all I objected to was you calling 
>> this "the most inconsistent and obtuse aspect of the Go language", which I 
>> perceived (and still do) to be an overstatement. "It is suboptimal" or "it 
>> is an area of improvement" are both significantly weaker statements, which I 
>> find less objectionable.
>>  
>> Personally, I still don't think it is a huge problem. And the fact that you 
>> where having a lot of trouble coming up with an example showing it to be one 
>> (the one you posted doesn't - again, it doesn't, in any way, change behavior 
>> when using or not using interfaces) is, in my view, a testament to that.
>> 
>>> And by the way, linters often flag correct code - that is why they have 
>>> disable options. They try to enforce the most common cases - and by the 
>>> recommendation in the faq to only use receivers of the same type - it seems 
>>> appropriate to me to have the linter flag this. 
>> 
>> I'm opposed to a linter flag, because it would flag correct code I regularly 
>> write. In general, linters should not be ignored - they either shouldn't be 
>> run, or they should be followed. Note that golint has no option to 
>> selectively disable a particular instance of a warning - the only way to 
>> silence a warning is to change the code. But I don't want to use a pointer 
>> receiver, if a value receiver is more appropriate.
>> 
>> If golint or go vet would start flagging this, I would likely follow the 
>> advice it's giving. Because that's how linters and static checks are 
>> supposed to be used - to enforce consistency. But I'd be sad doing it. Which 
>> is why I don't want them to flag it.
>> 
>> I'm less opposed to the FAQ entry. Simpy because an FAQ entry can be more 
>> easily ignored where it makes sense. If you will, it is one step in 
>> stringency below a linter. I'm fine defending my choice in a code review, 
>> but I don't want to defend it to a linter.
>>  
>>> As to this being in my opinion the most inconsistent and obtuse aspect of 
>>> Go - that is my opinion. Curious, what do you think would take the top spot?
>> 
>> I'm not sure. I don't like putting things in absolute order or claiming 
>> something is "the most X" for exactly that reason - it almost always turns 
>> out to be an overstatement.
>> 
>> Empirically, the issue of nil-pointers in interfaces not being nil seems to 
>> take one of the top spots, even though I don't fully understand why.
>> To me, concurrency in Go is extremely subtle and I would generally advice 
>> novices to stay away from it at first (or stay with extremely simple 
>> constructs), because they are likely to get it wrong.
>> Details of how Go handles constants and type-identity/assignabiity is what 
>> is probably most often tripping me, personally, up in questions/quizzes 
>> about Go. But it rarely comes up in practice.
>> The lack of co/contravariance is probably one of the things I miss the most 
>> from the language.
>> 
>> It really depends on what you're asking. And I'm very likely forgetting 
>> things while being put on the spot.
>> It's just a lot easier to make relative judgments, than absolute ones.
>> 
>>> 
>>> 
>>>>> On Jun 7, 2021, at 6:34 PM, Axel Wagner <axel.wagner...@googlemail.com> 
>>>>> wrote:
>>>>> 
>>>> 
>>>>> On Tue, Jun 8, 2021 at 1:26 AM Robert Engels <reng...@ix.netcom.com> 
>>>>> wrote:
>>>> 
>>>>> The pattern of a background stats collector is a common one. The atomic 
>>>>> is required not optional. 
>>>> 
>>>> It might be a common pattern, but it uses a pointer-receiver in that case. 
>>>> The atomic operation is not required, it operates on a local variable. 
>>>> Again, I don't understand how you can make statements that are so clearly 
>>>> wrong.
>>>> 
>>>> Feel free to try running it in the race detector without an atomic 
>>>> operation. Feel free trying to get the race detector to trigger without 
>>>> the atomic access, but keeping it silent when you add it. You'll find that 
>>>> this needs a pointer receiver. Because otherwise the function is operating 
>>>> on a local variable.
>>>>  
>>>>> 
>>>>>>> On Jun 7, 2021, at 6:16 PM, 'Axel Wagner' via golang-nuts 
>>>>>>> <golang-nuts@googlegroups.com> wrote:
>>>>>>> 
>>>>>> 
>>>>>> BTW, just to nail down the point of that code being wrong without 
>>>>>> interfaces: Your usage of `atomic` in `Log` is superfluous. You are 
>>>>>> operating on a local variable, so there is no possibility of concurrent 
>>>>>> modification. Your code is equivalent to this: 
>>>>>> https://play.golang.org/p/zYG0zTsk-2a
>>>>>> The only reason to use `atomic` here (and why you used it) is if that 
>>>>>> memory could be shared between goroutines. For that to happen, you need 
>>>>>> a pointer receiver though.
>>>>>> 
>>>>>> I refuse to believe that interfaces have anything to do with this 
>>>>>> obfuscation here. There is more than enough indication of it being wrong 
>>>>>> in any case.
>>>>>> 
>>>>>>> On Tue, Jun 8, 2021 at 1:05 AM Axel Wagner 
>>>>>>> <axel.wagner...@googlemail.com> wrote:
>>>>>>>> On Mon, Jun 7, 2021 at 11:42 PM Robert Engels <reng...@ix.netcom.com> 
>>>>>>>> wrote:
>>>>>>> 
>>>>>>>> I don’t think that represents the problem fairly. In the non interface 
>>>>>>>> case I know I can’t accept a copy so I would declare the method as 
>>>>>>>> taking a pointer to the struct.
>>>>>>> 
>>>>>>> How methods are declared should, in general, not be a matter of whether 
>>>>>>> or not they are assigned to an interface, but to whether or not they 
>>>>>>> need a pointer. Again: Your code is incorrect without interfaces. The 
>>>>>>> problem doesn't happen when you put that value into an interface - it 
>>>>>>> happens when you pass a copy of it and expect it to refer to the 
>>>>>>> original. Interfaces are just one way to create such a copy, but they 
>>>>>>> do not matter for the correctness of this code and for whether or not 
>>>>>>> that method needs a pointer receiver (it does).
>>>>>>> 
>>>>>>> But again, to be clear: I'm not saying problems like this *never* 
>>>>>>> happen and I'm not even saying that interfaces may obscure it in some 
>>>>>>> cases. Just that a) the root cause here is that your method really 
>>>>>>> needs to take a pointer-receiver, interfaces or not and b) that it 
>>>>>>> seems very much an overstatement to me to call this "the most 
>>>>>>> inconsistent and obtuse aspect of the Go language".
>>>>>>> 
>>>>>>>> With interfaces this is lost - as the interface is implicitly a pointer
>>>>>>> 
>>>>>>> Well, it seems a bad idea to say that interfaces are implicitly 
>>>>>>> pointers then. That seems to indicate that Rob's original phrasing is 
>>>>>>> indeed an important clarification - the language behaves as if the 
>>>>>>> value contained in them is copied when the interface value is copied.
>>>>>>> 
>>>>>>> It seems the confusion here is, that you assume it's not. And that 
>>>>>>> interfaces act as a pointers, when they don't.
>>>>>> 
>>>>>> -- 
>>>>>> 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.
>>>>>> To view this discussion on the web visit 
>>>>>> https://groups.google.com/d/msgid/golang-nuts/CAEkBMfGVyvYYpQhCp_JkxN9EvgZ4FXJ8_WpxseJOB1OR7qt6ww%40mail.gmail.com.
>>>> 
>>>> -- 
>>>> 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.
>>>> To view this discussion on the web visit 
>>>> https://groups.google.com/d/msgid/golang-nuts/CAEkBMfHibUpDHQU9m8%3DrLYtnDj%3DFY01nkuP4k0Giow-hCbhNgQ%40mail.gmail.com.

-- 
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.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/B1A4CA5D-09D2-4FC8-AA68-8D2E5B9F842A%40ix.netcom.com.

Reply via email to