Someone mentioned that data is data and there is no need to hide 
implementation details. If we are to use that reasoning, there is no need 
for Go to allow un-exported fields/types/functions. Why do we need them at 
all? The reason for hiding implementation details is to narrow down the 
access points to the code. By keeping the access points narrow, it reduces 
user's cognitive burden and leaves room for future 
refactoring/changes/extension to the code. It is called abstraction. 

Someone also mentioned that you cannot add methods to an interface without 
breaking compatibility. The only case that this is true is when you have no 
control over the implementation. If you have control over the 
implementation, I don't see why you can't add methods to an interface and 
change its implementation in the same way you would to a struct if you are 
to return a struct. If you have no access to the struct, you can't add 
methods to the structs either. It isn't fair to consider this argument as 
the "return struct" superiority over "return interface".

Readability is subjective. Haskell code is often incredibly terse and 
elegant. Some people even swear by its readability and comprehension. While 
I agree that Haskell is elegant, I don't find it to be easy to read. There 
have been many metrics to measure code readability/comprehension/complexity 
over the years, and none is quite reliable. I even wrote a linter that was 
used in my company back then, and the tool became the company's standard 
that the code must pass before it can be merged to the main branch. 
Thinking back, what was readable back then is no longer the most readable 
today. Readability varies from person to person, and it changes over time.

Anyhow, I am wary when someone tries to promote a certain principle as the 
one true way. There is no one true way. I have seen best practices come and 
go. C++ is the best documentation of how best practices change over the 
years. C++ *is* a catalogue of best practices. The bad things we see in C++ 
were once considered best practices at some point in time. Programming 
languages that promote specific concepts as the one-true-way fade away when 
the concepts are no longer in fads.   

Treat a programming language and its features as tools to solve your 
problem. I am not here to promote "returning interface". I was just sharing 
how "returning interface" was useful in my case. 

On Wednesday, August 10, 2022 at 3:53:44 AM UTC+7 t...@timpeoples.com wrote:

> I'm Sorry -- when I said "readability reviewer" I was referring to a very 
> google-specific term about how they ensure the author of a change 
> understands the language they're writing. Granted, it's been a while since 
> I worked there but, at that time, each change request (aka MR, PR, etc...) 
> required approval from (1) a code owner and (2) someone with *readability* 
> for 
> the language in question -- and, if the author covered both of those, an 
> "LGTM" from someone else was still required (i.e. *Minimum Two Brains*). 
> Regardless, no code change could be submitted without someone with 
> *readability* being involved (either the author or a reviewer).
>
> Each language had their own procedures about how *readability* would be 
> granted. For Go, each and every CR from a non-readability SWE would get 
> assigned to a random *readability reviewer*  to ensure the code meets 
> certain standards and is *idiomatically Go.*  It took me ~15 months and 
> just under 60 CRs to get Go *readability* (compared to 4 months and 3 CRs 
> for Python).
>
> On Tuesday, August 9, 2022 at 1:08:42 PM UTC-7 bse...@computer.org wrote:
>
>> On Tue, Aug 9, 2022 at 1:52 PM Tim Peoples <t...@timpeoples.com> wrote:
>>
>>> Yeah, I'm with Burak on this one. The interface usage you're describing 
>>> Henry is exactly the kind of thing I'm talking about.  While on the surface 
>>> it may seem advantageous -- in fact, I also tried writing Go that way when 
>>> I first started -- my *readability* reviewers at Google did well to 
>>> enlighten me about the many problems this can cause with Go -- some of 
>>> which rog was kind enough to enumerate.
>>
>>
>> I think "readability" is not the right metric to use here. "Code 
>> comprehension" (comprehensibility?) should be the right metric. Readability 
>> does not always imply it can be easily comprehended. Java is readable, but 
>> not necessarily comprehensible. I argue that Go code is more comprehensible 
>> than code written in most other languages, because you can understand all 
>> the implications of the code using mostly "local knowledge", that is, 
>> knowledge you can gain by reading pieces of code "close" to the point of 
>> interest. Wherever you have interfaces, you need non-local knowledge to 
>> understand what's going on.
>>
>>  
>>
>>>
>>> Also, since originally posting this yesterday, I've come to learn that 
>>> my new shop is not only utilizing preemptive interface definitions but also 
>>> a complete dependency injection framework and rather strict adherence to 
>>> *Clean 
>>> Architecture*™ 
>>> <https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html>
>>>  
>>> -- (likely from the scripts in this repo 
>>> <https://github.com/thnkrn/go-gin-clean-arch>) which goes a long way 
>>> towards explaining why so much of the code looks like Java written in Go 
>>> syntax.
>>>
>>> On Tuesday, August 9, 2022 at 8:33:07 AM UTC-7 bse...@computer.org 
>>> wrote:
>>>
>>>> On Mon, Aug 8, 2022 at 11:27 PM Henry <henry.ad...@gmail.com> wrote:
>>>>
>>>>> I am sure that many of us have been on that journey. After using Go 
>>>>> for some time, we discover some practices that are not necessarily in 
>>>>> agreement with the existing "adages" but effectively solve our problems.  
>>>>>
>>>>> For me, if the data type is mutable, I prefer returning interfaces. It 
>>>>> would be something like this:
>>>>> ```
>>>>> type Student interface {
>>>>>    //...
>>>>> }
>>>>>
>>>>> type studentImpl struct {
>>>>>    //...
>>>>> }
>>>>>
>>>>> func NewStudent(id string) Student {
>>>>>    return &studentImpl{
>>>>>       //...
>>>>>    }
>>>>> }
>>>>> ```
>>>>> There is a bit of history why I use this approach. For a struct with a 
>>>>> mutex, I wanted to ensure that the user did not accidentally copy the 
>>>>> struct. Nowadays we have *go vet* to give us a warning, but this was 
>>>>> before *go vet* had this functionality. So, I return a pointer to the 
>>>>> struct and hide it behind an interface. That way, it hides the 
>>>>> implementation details from the user and the user can pass the object 
>>>>> around without knowing whether it has a mutex or not.
>>>>>
>>>>> And then I ended up with some *constructors* returning structs and 
>>>>> some returning interfaces. To ensure consistency, my colleagues and I 
>>>>> decided to return interfaces for all mutable objects. For immutable 
>>>>> objects, we return structs.
>>>>>
>>>>> The nice thing about this approach is that it makes the syntax a lot 
>>>>> cleaner as you have to deal with fewer pointers. 
>>>>> ```
>>>>> //instead of this
>>>>> func Update(student *Student) {
>>>>>   //...
>>>>> }
>>>>> func UpdateMany(students []*Student){
>>>>>   //...
>>>>> }
>>>>>
>>>>> //now you have this
>>>>> func Update(student Student) {
>>>>>   //...
>>>>> }
>>>>> func UpdateMany(students []Student){
>>>>>   //...
>>>>> }
>>>>> ```
>>>>> Some members in the team came from higher level languages and they 
>>>>> found working with pointers a bit awkward, so we made some accommodation 
>>>>> for them. 
>>>>>
>>>>
>>>>> There are times when I need to *upgrade* some of these mutable 
>>>>> objects, and this approach has proven to be quite flexible. It also plays 
>>>>> nicely with code generators.
>>>>>
>>>>> Some people may disagree with this approach, but I have been using it 
>>>>> ever since: return interface for mutable objects, return structs for 
>>>>> immutable objects.
>>>>>
>>>>
>>>> I am one of those who disagrees. I have not seen any benefit from 
>>>> having interfaces for data objects other than making other developers 
>>>> happy. In my opinion, this amounts to emulating another language in Go. 
>>>> There are cases where this might make sense, but as a general principle, I 
>>>> think it should be avoided. Data is data. There are no implementation 
>>>> details to hide.
>>>>
>>>>  
>>>>
>>>>> On Tuesday, August 9, 2022 at 3:09:10 AM UTC+7 t...@timpeoples.com 
>>>>> wrote:
>>>>>
>>>>>> I can't speak to the *auto-generated swagger client* case but I 
>>>>>> believe gRPC is still doing things the right way -- in that the 
>>>>>> framework 
>>>>>> defines an interface I (the framework API consumer) then implements.  
>>>>>> IOW: 
>>>>>> I don't see that as a "java style interface" (where the interface 
>>>>>> defines 
>>>>>> the API contract).
>>>>>>
>>>>>> I suspect you and I are saything the same thing.
>>>>>>
>>>>>> t.
>>>>>>
>>>>>> On Monday, August 8, 2022 at 12:51:29 PM UTC-7 bse...@computer.org 
>>>>>> wrote:
>>>>>>
>>>>>>> On Mon, Aug 8, 2022 at 12:51 PM Tim Peoples <t...@timpeoples.com> 
>>>>>>> wrote:
>>>>>>>
>>>>>>>> I don't necessarily consider the "multiple implementations" case as 
>>>>>>>> being truly preemptive -- if there really are multiple implementations 
>>>>>>>> (e.g. the "hash" package from the standard library).
>>>>>>>>
>>>>>>>> I'm much more concerned about interfaces that are defined by an API 
>>>>>>>> producer -- for one and only one impl -- and then adding a bunch of 
>>>>>>>> extra 
>>>>>>>> (often autogenerated) code to deal with that.
>>>>>>>>
>>>>>>>
>>>>>>> Like a gRPC client/server, or auto-generated swagger client/server?
>>>>>>>
>>>>>>> I've had many instances where such an auto-generated client had to 
>>>>>>> be passed down components that have no knowledge of those services. 
>>>>>>> Writing 
>>>>>>> such components using interfaces declaring only parts of those service 
>>>>>>> implementations have benefits. An example that I can think of is an 
>>>>>>> audit-trail service that deals with recording transaction metadata, 
>>>>>>> looking 
>>>>>>> them up, etc. It makes sense to write components that use only the 
>>>>>>> writer 
>>>>>>> part of that service, instead of requiring the whole thing. It makes 
>>>>>>> writing tests easier. It lets you decouple services better, add 
>>>>>>> adapters/interceptors etc.
>>>>>>>  
>>>>>>>
>>>>>>>>
>>>>>>>> t.
>>>>>>>>
>>>>>>>> On Monday, August 8, 2022 at 11:02:31 AM UTC-7 bse...@computer.org 
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> On Mon, Aug 8, 2022 at 11:17 AM Tim Peoples <t...@timpeoples.com> 
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> For years I've read the old adage, "Accept interfaces, return 
>>>>>>>>>> structs" and have spent years working to instill this understanding 
>>>>>>>>>> among 
>>>>>>>>>> my colleagues. I gathered a great many skills while learning Go (and 
>>>>>>>>>> acquiring readability)  back in the day -- and one of the strongest 
>>>>>>>>>> of 
>>>>>>>>>> those is the idea that interfaces should be defined by their 
>>>>>>>>>> consumer 
>>>>>>>>>> instead of an API producer -- but I've now been away from Google 
>>>>>>>>>> longer 
>>>>>>>>>> than I was there and I'm beginning to suspect that the general 
>>>>>>>>>> consensus 
>>>>>>>>>> among the *Go Literati* may have shifted around some things -- 
>>>>>>>>>> like preemptive interfaces.
>>>>>>>>>>
>>>>>>>>>> My arguments against preemptive interfaces have recently run into 
>>>>>>>>>> more and more pushback  -- especially among the influx of developers 
>>>>>>>>>> coming 
>>>>>>>>>> from the Java and/or C# world who seem to continually reject any 
>>>>>>>>>> notion 
>>>>>>>>>> that Go should be any different from the way they've always done 
>>>>>>>>>> things.
>>>>>>>>>>
>>>>>>>>>> This has recently come to a head with a brand new job (I'm 3 
>>>>>>>>>> weeks in) where virtually all of their services are built atop a 
>>>>>>>>>> dependency 
>>>>>>>>>> injection framework having a data model with dozens (if not 
>>>>>>>>>> hundreds) of 
>>>>>>>>>> preemptive interfaces and my initial, cursory review tells me the 
>>>>>>>>>> codebase 
>>>>>>>>>> is at least an order of magnitude more complex that it needs to be.  
>>>>>>>>>> (Note, 
>>>>>>>>>> I was told that none SWEs at this company (other than myself) knew 
>>>>>>>>>> any Go 
>>>>>>>>>> before they started).
>>>>>>>>>>
>>>>>>>>>> So, my questions to the group are thus, "Should I even care about 
>>>>>>>>>> this at all?  Are preemptive interfaces now considered the norm with 
>>>>>>>>>> Go? 
>>>>>>>>>> Or, should I just shut up and crawl back into my hole?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I believe both approaches have their uses. What you call 
>>>>>>>>> preemptive interfaces can be effectively used to hide implementation 
>>>>>>>>> details where multiple implementations can exist. This approach can 
>>>>>>>>> coexist 
>>>>>>>>> very well with interfaces defined by the consumer. For example we 
>>>>>>>>> have 
>>>>>>>>> services that are written to implement an interface, so it becomes a 
>>>>>>>>> logical deployment unit. Then we have consumers of that service that 
>>>>>>>>> define 
>>>>>>>>> parts of the interface service implements, so the consumer is not 
>>>>>>>>> dependent 
>>>>>>>>> on the complete service, and we can add any interceptors/filters.
>>>>>>>>>
>>>>>>>>> However, I agree with your assessment that especially newcomers 
>>>>>>>>> tend to choose the traditional "interface is a contract" approach. In 
>>>>>>>>> addition to the effect of other languages, people seem to like "clean 
>>>>>>>>> architecture". Nevertheless, even with people dedicated to clean 
>>>>>>>>> architecture, the idea of "interface parts" seems to resonate, 
>>>>>>>>> especially 
>>>>>>>>> when you show that you can define an interface on the consumer side 
>>>>>>>>> that 
>>>>>>>>> combines parts of multiple "contracts".
>>>>>>>>>  
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> TIA,
>>>>>>>>>> Tim.
>>>>>>>>>>
>>>>>>>>>> -- 
>>>>>>>>>> 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...@googlegroups.com.
>>>>>>>>>> To view this discussion on the web visit 
>>>>>>>>>> https://groups.google.com/d/msgid/golang-nuts/f4777928-875d-4c0a-a4a7-9fb57bf9d51fn%40googlegroups.com
>>>>>>>>>>  
>>>>>>>>>> <https://groups.google.com/d/msgid/golang-nuts/f4777928-875d-4c0a-a4a7-9fb57bf9d51fn%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>>>> .
>>>>>>>>>>
>>>>>>>>> -- 
>>>>>>>> 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...@googlegroups.com.
>>>>>>>>
>>>>>>> To view this discussion on the web visit 
>>>>>>>> https://groups.google.com/d/msgid/golang-nuts/34469b98-c37e-4a1d-af13-729b639a71ben%40googlegroups.com
>>>>>>>>  
>>>>>>>> <https://groups.google.com/d/msgid/golang-nuts/34469b98-c37e-4a1d-af13-729b639a71ben%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>> .
>>>>>>>>
>>>>>>> -- 
>>>>> 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...@googlegroups.com.
>>>>>
>>>> To view this discussion on the web visit 
>>>>> https://groups.google.com/d/msgid/golang-nuts/3774ae88-45ea-4181-909d-f18c788c7395n%40googlegroups.com
>>>>>  
>>>>> <https://groups.google.com/d/msgid/golang-nuts/3774ae88-45ea-4181-909d-f18c788c7395n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>> .
>>>>>
>>>> -- 
>>> 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...@googlegroups.com.
>>>
>> To view this discussion on the web visit 
>>> https://groups.google.com/d/msgid/golang-nuts/e8bc57a6-bcee-4904-82f6-f52cdf371b6bn%40googlegroups.com
>>>  
>>> <https://groups.google.com/d/msgid/golang-nuts/e8bc57a6-bcee-4904-82f6-f52cdf371b6bn%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>>

-- 
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/093d0901-2ca3-414a-a5ac-84932b1c92f1n%40googlegroups.com.

Reply via email to