Lack of "protected' visibility leads to public nudity

975 views
Skip to first unread message

Eric Hawthorne

unread,
Jul 5, 2010, 5:35:37 PM7/5/10
to golang-nuts
I'm just experiencing something where I am trying
to get some implementation abstraction (i.e. factoring implementations
to respect DRY principle).

Therefore I have a package which exports an interface and also exports
a
"base" implementation struct type which provides a full implementation
of
some of the methods of the interface. The base implementation struct
type
also provides partial implementation (the general part) of one of the
other
methods of the interface.

It is desirable for "subclass" specific implementations to embed the
"base" struct type to get some basic "data members" and also some
basic
method implementations.

THE PROBLEM is that to use this pattern effectively, I am needing to
define too many of the methods of the interface as exported, and also
to define many of the data members of the base struct type as
exported,
so that I can use these general method implementations and general
data members in subclass struct types.

The reason that all these things (which are really semi-private
implementation
details) have to be exported is because I don't want to insist that
all subclass
struct types be added into the general package. Yet it would be handy
if they
could re-use code and data from the base struct type.

Would it make sense to add a protected visibility, which means that a
protected
method or data member of struct type A is visible an outer struct type
B
which embeds the struct type A, but the method and data are not
visible outside
of the method implementations of B.

I guess the naming convention would be tricky, given the
"capitalization is export status"
rule, but the lack of protection is leading to some overly exposed
parts of the code body.

Other "Go-ish" idioms to solve this problem are welcome. Anyone have
an alternate
solution?

nsf

unread,
Jul 5, 2010, 5:51:28 PM7/5/10
to golan...@googlegroups.com
On Mon, 5 Jul 2010 14:35:37 -0700 (PDT)
Eric Hawthorne <poetics...@gmail.com> wrote:

> I'm just experiencing something where I am trying
> to get some implementation abstraction (i.e. factoring implementations
> to respect DRY principle).
>

> ... bla-bla-bla, I read a lot of OO books ...


>
> Other "Go-ish" idioms to solve this problem are welcome. Anyone have
> an alternate
> solution?

You totally didn't give us a description of your problem. Having unable
to apply practices that you've read about in OO books in Go isn't a
problem, it's a wrong thing to do in the first place.

Just forget all this object-oriented bullshit you knew and start
thinking in terms of what you want from the code. What qualities are
desirable? (like extensibility, or maintainability, or flexibility,
etc.). How it is possible to achieve these qualities using a particular
language, etc, etc.

When I hear so much abstract nonsense, I ask people to come down back
to earth and start doing things that solve problems. Real problems.
Even if the resulting code isn't super flexible.

If it helps, great. If it's not, then I'm afraid you can only help
yourself and no one can solve your problems (I mean really, _your_
problems, not some real world problems, because that's what OOP does,
introduces a non-existing problems and you're full of them, at least
it looks like that to me).

Sorry for me being honest and direct.

Eric Hawthorne

unread,
Jul 5, 2010, 7:25:15 PM7/5/10
to golang-nuts
nsf dissed abstraction in general, calling it "object-oriented
bullshit".

By abstraction of common properties and behaviours, I mean first
analysing so as to find these in the problem and solution domain, and
then separating out the common properties and behaviours from overly
specific surrounding baggage, then ensuring that we only express, and
only implement, a common property or common behaviour once in the
codebase.

If you don't see the value in abstraction (as defined above), and the
DRY principle, then you simply haven't done much software engineering,
or quite possibly, you've done all of it badly.

Sorry, but I'm just being honest and direct.

(ps. Sorry for the horrible line-wraps in original post. I find the
editor window in google groups really annoying because it lets me type
about 150 characters on a single line before wrapping, which means I
can't read what I'm composing properly, so I tend to carriage-return
at a reasonable 60 or so characters. I have to learn to stop that, but
they should also fix the text entry window.)

Andrew Gerrand

unread,
Jul 5, 2010, 7:32:12 PM7/5/10
to Eric Hawthorne, golang-nuts
On 6 July 2010 07:35, Eric Hawthorne <poetics...@gmail.com> wrote:
> Other "Go-ish" idioms to solve this problem are welcome. Anyone have
> an alternate
> solution?

Hi Eric,

You haven't described the problem. You've described the issues you're
having with one particular solution to the problem.

The approach you're describing is one typically taken in class-based,
inheritance-oriented languages - but Go doesn't support inheritance.
If you're mostly accustomed to writing in the traditional "OOP" style,
then writing idiomatic Go code will require a fresh perspective.

Please give a concrete example of what you're trying to do, and then
we can offer some suggestions as to how you might structure your Go
program.

Cheers,
Andrew

Jakub Piotr Cłapa

unread,
Jul 5, 2010, 7:36:19 PM7/5/10
to golan...@googlegroups.com
On 06.07.10 01:25, Eric Hawthorne wrote:
> nsf dissed abstraction in general, calling it "object-oriented
> bullshit".
>
> By abstraction of common properties and behaviours, I mean first
> analysing so as to find these in the problem and solution domain, and
> then separating out the common properties and behaviours from overly
> specific surrounding baggage, then ensuring that we only express, and
> only implement, a common property or common behaviour once in the
> codebase.
>
> If you don't see the value in abstraction (as defined above), and the
> DRY principle, then you simply haven't done much software engineering,
> or quite possibly, you've done all of it badly.

AFAIK abstraction != language-ensured encapsulation.

--
regards,
Jakub Piotr Cłapa

Eric Hawthorne

unread,
Jul 5, 2010, 7:40:40 PM7/5/10
to golang-nuts
nsf wrote:

>You totally didn't give us a description of your problem.

Yeah. I did. I said that some other valid considerations
(abstraction, DRY, sensible package boundaries)
were forcing me to export private-like data and methods which should
not really be exported.

It should not need to be said, but having excess exported data
definitions and methods
is STRONG BAD, as it leads to inappropriate strong coupling, as well
as exposure of
data to corruption by package-using code.

protected status allows for strong coupling only where it is arguably
appropriate: in the
specialization-by-incremental-addition direction, while conserving
data safety and loose
coupling in other directions outside the package.

Andrew Gerrand

unread,
Jul 5, 2010, 7:45:32 PM7/5/10
to Eric Hawthorne, golang-nuts
On 6 July 2010 09:25, Eric Hawthorne <poetics...@gmail.com> wrote:
> By abstraction of common properties and behaviours, I mean first
> analysing so as to find these in the problem and solution domain, and
> then separating out the common properties and behaviours from overly
> specific surrounding baggage, then ensuring that we only express, and
> only implement, a common property or common behaviour once in the
> codebase.

By "abstraction of common properties and behaviours" are you referring
to those properties and behaviours of a type/class?

Typically, in OO world you build an abstract base class that does
something, and then write concrete classes that inherit from it that
behave in different ways.

In Go, you would achieve the same effect by using several types that
interact with each other via interface types, and whose
implementations are therefore interchangeable. A safe approach to
writing Go code (or, really, any code) is "make each part as simple as
possible, and communicate through simple interfaces."

I could be more specific if you would give an example of the problem
you're trying to solve.

Cheers,
Andrew

Eric Hawthorne

unread,
Jul 5, 2010, 7:49:00 PM7/5/10
to golang-nuts
Jakub Piotr Cłapa wrote:

> AFAIK abstraction != language-ensured encapsulation.

language-ensured encapsulation leads directly to abstraction (and
single implementation of each concept) in s/w implementation, since a
module which only exports a small number of behaviours is inherently a
less specific, thing, and most likely a more re-usable (safely re-
usable that is) thing than a module which exports lots of
implementation details.

jimmy frasche

unread,
Jul 5, 2010, 7:48:59 PM7/5/10
to Eric Hawthorne, golang-nuts
On Mon, Jul 5, 2010 at 4:40 PM, Eric Hawthorne <poetics...@gmail.com> wrote:
> nsf wrote:
>
>>You totally didn't give us a description of your problem.
>
> Yeah. I did. I said that some other valid considerations
> (abstraction, DRY, sensible package boundaries)
> were forcing me to export private-like data and methods which should
> not really be exported.

That's a meta-analysis of the description of your problem at best. If
you give a short code example along with a concrete description of
what you are trying to do, we have something to discuss.

> It should not need to be said, but having excess exported data
> definitions and methods
> is STRONG BAD, as it leads to inappropriate strong coupling, as well
> as exposure of
> data to corruption by package-using code.

That sounds well and good but I can't tell WHY you're running up against this.

> protected status allows for strong coupling only where it is arguably
> appropriate: in the
> specialization-by-incremental-addition direction, while conserving
> data safety and loose
> coupling in other directions outside the package.

Go uses abstraction by interfaces and extension by composition, but
since you haven't provided any conrete examples I cannot tell whether
this would help or hinder you.

Eric Hawthorne

unread,
Jul 5, 2010, 8:15:47 PM7/5/10
to golang-nuts
> Go uses abstraction by interfaces and extension by composition, but
> since you haven't provided any conrete examples I cannot tell whether
> this would help or hinder you.

Ok. Here's an example (derived from a real case.)

package loadcntrl

/*
Start() launches a goroutine which repeatedly, periodically calls
the
AssessAndAct() method on the control-program it has been given.
There are different types of control-program, which vary according
to
what their AssessAndAct() method does.
*/
type ControlProgramRunner interface {

Initialize(cp ControlProgram)
Start()
Stop()
}

type ControlProgram interface {
AddInstrumentReading(instrumentReading InstrumentReading)
AddControllerSetting(controllerSetting ControllerSetting)
AssessAndAct()
}

type ControlProgramBase struct {
InstrumentReadings map[string] *InstrumentReading
ControlSettings map[string] *ControllerSetting
}

func (cp *ControlProgramBase) AssessAndAct() {
// some stuff all control programs need to do at beginning of
their action.
}

/////////////////////////////////
package hot_water_heater

import . "loadcntrl"

type HotWaterHeaterControlProgram struct {
ControlProgramBase

}

func (cp *HotWaterHeaterControlProgram AssessAndAct() {
cp.ControlProgramBase.AssessAndAct()
// some specific hot water heater control stuff
}

///////////////////////////////////////

Ok so the main problem here (with the way I've done it anyway) is that
AssessAndAct(),InstrumentReadings, and ControlSettings should not be
exported from the loadcntrl package, since they are implementation
details.

(However they are handy for the sub-types of controllers to have
access to, to avoid repeating that aspect of implementation.)

In particular, it is DANGEROUS to have AssessAndAct() exported,
because the ONLY valid and appropriate way it can be
called (by anyone other than a subclass extension-implementation of
the method) is inside some kind of control loop managed by the
ControlProgramRunner.

So I end up with the admonition in a comment "Don't call this even
though it is exported!! unless you are the body of an override of this
method in a sub-type of controller." Lame-o.

Serra

unread,
Jul 5, 2010, 8:26:23 PM7/5/10
to golang-nuts
On 6 Jul., 04:40, Eric Hawthorne <poeticsoftw...@gmail.com> wrote:
> nsf wrote:
> >You totally didn't give us a description of your problem.
>
> Yeah. I did.
I DRO here.

> It should not need to be said, but having excess exported data
> definitions and methods
> is STRONG BAD, as it leads to inappropriate strong coupling, as well
> as exposure of
> data to corruption by package-using code.

Well... In the short two years I was coding excessively in ruby I
never ever had problems with "public nudity". And ruby is the most OO
language I've seen so far (I have to admit I haven't seen many).
You sound like an old Java advocate, specifically one that now tries
to write Java in golang.
That's like using the German grammar in the English language.
Also, listen to what Andrew said.
"In Go, you would achieve the same effect by using several types that
interact with each other via interface types, and whose
implementations are therefore interchangeable. A safe approach to
writing Go code (or, really, any code) is "make each part as simple as
possible, and communicate through simple interfaces." "

Here's my 2 cents:
"When I program in golang, I adhere to these principles:
Whenever there are two ways, go for the easier one. Don't think how,
think what. Don't race, talk."

David Roundy

unread,
Jul 5, 2010, 8:28:49 PM7/5/10
to Eric Hawthorne, golang-nuts
Why not just avoid exporting any of those "helper" functions, and
don't use the embedding feature? You just need to add a function like

func WrapControlProgram(ControlProgram) ControlProgramHelper

(or alternatively this could be embedded in the ControlProgramRunner,
if you don't need to have multiple different ControlProgramRunners
defined in different packages)

David

type ControlProgram interface {
AddInstrumentReading(instrumentReading InstrumentReading,
instrumentReadings map[string] *InstrumentReading,
controllerSettings map[string] *ControllerSetting)
AddControllerSetting(controllerSetting ControllerSetting,
instrumentReadings map[string] *InstrumentReading,
controllerSettings map[string] *ControllerSetting)
AssessAndAct()
}

type ControlProgramHelper struct {
instrumentReadings map[string] *InstrumentReading
controlSettings map[string] *ControllerSetting
theProgram ControlProgram
}

func (cp *ControlProgramHelper) AssessAndAct() {


// some stuff all control programs need to do at beginning of their action.

// ...
// and now do the real stuff!
theProgram.AssessAndAct()
}

Jessta

unread,
Jul 5, 2010, 8:51:08 PM7/5/10
to Eric Hawthorne, golang-nuts
On Tue, Jul 6, 2010 at 10:15 AM, Eric Hawthorne
<poetics...@gmail.com> wrote:
> In particular, it is DANGEROUS to have AssessAndAct() exported,
> because the ONLY valid and appropriate way it can be
> called (by anyone other than a subclass extension-implementation of
> the method) is inside some kind of control loop managed by the
> ControlProgramRunner.
>
> So I end up with the admonition in a comment "Don't call this even
> though it is exported!! unless you are the body of an override of this
> method in a sub-type of controller." Lame-o.

If the only purpose of the 'loadcntrl' package is to be a base for
other packages, then you may as well put all of it in the same
package. Which fixes all your problems.

Separate the different 'ControlProgram's in to different files within
the same package if you need to.


- jessta

--
=====================
http://jessta.id.au

chris dollin

unread,
Jul 6, 2010, 12:51:37 AM7/6/10
to nsf, golan...@googlegroups.com
On 5 July 2010 22:51, nsf <no.smi...@gmail.com> wrote:

> Sorry for me being honest and direct.

It came over as rude & dismissive, I'm afraid, whatever
its technical merits.

Chris

--
Chris "allusive" Dollin

Serge Hulne

unread,
Jul 6, 2010, 3:49:14 AM7/6/10
to golang-nuts
If you want (or need) encapsulation, virtuality, polymorphism,
inheritance you probably ought to use C++ (or D from Digital Mars).

These OO features can be handy, but it is known from Python that they
are not that useful (Python classes have no private or protected
members and nobody ever complained).

The entire idea of Go (as far as I understand) is to dispense with all
the OO features which are not absolutely indispensable, in order to
have a simple, cleaner, smaller, non-ambiguous language, with only
limited genericity (interfaces) and a simpler and more transparent OO
model than C++.

If the features you mention were available in Go, then it would kind
of defeat the purpose, since you would just have reinvented C++ (and
all the associated unnecessary and counterproductive complexity).

Serge.

unread,
Jul 6, 2010, 4:08:09 AM7/6/10
to golang-nuts
On Jul 6, 2:26 am, Serra <DaveyJon...@web.de> wrote:
> "When I program in golang, I adhere to these principles:
> Whenever there are two ways, go for the easier one. Don't think how,
> think what. Don't race, talk."

Religious crap. You previously worked in marketing or something?

chris dollin

unread,
Jul 6, 2010, 4:29:23 AM7/6/10
to ⚛, golang-nuts

They're terse summaries of reasonable principles, at least one of which
is part of the Go phrasebook. Critique them by all means, but frothing
rudeness isn't critique.

unread,
Jul 6, 2010, 5:04:44 AM7/6/10
to golang-nuts
On Jul 6, 1:32 am, Andrew Gerrand <a...@golang.org> wrote:
> On 6 July 2010 07:35, Eric Hawthorne <poeticsoftw...@gmail.com> wrote:
>
> > Other "Go-ish" idioms to solve this problem are welcome. Anyone have
> > an alternate
> > solution?
>
> Hi Eric,
>
> You haven't described the problem. You've described the issues you're
> having with one particular solution to the problem.

Actually, he gave a pretty good description of the problem.

The short answer is: Go supports just two layers of symbol visibility.
The first one is "public", the second one is "package private". If X
needs to access Y, then:

- Y needs to be public, or
- Y has to be in the same package as X

Any information-hiding pattern or access-control pattern which needs
to go beyond these rules cannot be expressed in Go directly. Period.

For example, the following cannot be expressed in Go:

(X uses Y) and
(Y uses Z) and
(Y is only visible to X) and
(Z is only visible to members of Ys) and
(Y is a member of Ys) and
(X is not a member of Ys)

> The approach you're describing is one typically taken in class-based,
> inheritance-oriented languages - but Go doesn't support inheritance.
> If you're mostly accustomed to writing in the traditional "OOP" style,
> then writing idiomatic Go code will require a fresh perspective.

I think access-control issues are *not* unique to "OOP style", but it
is a generic problem of any programming language in which you denote
things by their *names* (function names, procedure names, type names,
field names, package names, parameter names, variable names, ...). In
short, these issues arise in pretty much all existing programming
languages - *irrespective* of whether they are imperative, OO,
functional, whatever.

Note that in the above example I did *not* say what X, Y, Ys, Z are.
They could denote classes, functions, types, fields, interfaces,
methods, variables ...

Andrew Gerrand

unread,
Jul 6, 2010, 5:37:37 AM7/6/10
to ⚛, golang-nuts
On 6 July 2010 19:04, ⚛ <0xe2.0x...@gmail.com> wrote:
> On Jul 6, 1:32 am, Andrew Gerrand <a...@golang.org> wrote:
>> On 6 July 2010 07:35, Eric Hawthorne <poeticsoftw...@gmail.com> wrote:
>>
>> > Other "Go-ish" idioms to solve this problem are welcome. Anyone have
>> > an alternate
>> > solution?

Eric here specifically asks for "Go-ish idioms" - that's what I was
addressing in my responses.

>> You haven't described the problem. You've described the issues you're
>> having with one particular solution to the problem.
>
> Actually, he gave a pretty good description of the problem.

A problem he's experiencing because of the approach he's taking to
structure his code. An approach that doesn't work well with Go's
feature set (as you point out, below).

> The short answer is: Go supports just two layers of symbol visibility.
> The first one is "public", the second one is "package private". If X
> needs to access Y, then:
>
> - Y needs to be public, or
> - Y has to be in the same package as X
>
> Any information-hiding pattern or access-control pattern which needs
> to go beyond these rules cannot be expressed in Go directly. Period.

Sure, but is that an issue? Not in my experience.

>> The approach you're describing is one typically taken in class-based,
>> inheritance-oriented languages - but Go doesn't support inheritance.
>> If you're mostly accustomed to writing in the traditional "OOP" style,
>> then writing idiomatic Go code will require a fresh perspective.
>
> I think access-control issues are *not* unique to "OOP style", but it

I didn't say that "access-control issues are unique to OOP style". I
was talking about the approach in general.

> is a generic problem of any programming language in which you denote
> things by their *names* (function names, procedure names, type names,
> field names, package names, parameter names, variable names, ...). In
> short, these issues arise in pretty much all existing programming
> languages - *irrespective* of whether they are imperative, OO,
> functional, whatever.
>
> Note that in the above example I did *not* say what X, Y, Ys, Z are.
> They could denote classes, functions, types, fields, interfaces,
> methods, variables ...

But you did say "members of," which does not apply to all of the above.

Andrew

roger peppe

unread,
Jul 6, 2010, 5:56:26 AM7/6/10
to Eric Hawthorne, golang-nuts
On 5 July 2010 22:35, Eric Hawthorne <poetics...@gmail.com> wrote:
> Would it make sense to add a protected visibility, which means that a
> protected
> method or data member of struct type A is visible an outer struct type
> B
> which embeds the struct type A, but the method and data are not
> visible outside
> of the method implementations of B.

i don't think this would make sense.
currently, an embedded value is just like an instance
variable except that you don't have to write the
methods that re-call the same methods on the
instance variable.

you're suggesting that "embedding" should somehow
be different from "using". in go there is no distinction,
which is actually useful - i can change from embedding
a value to using that value without any loss of generality,
and i have done so in the past.

> In particular, it is DANGEROUS to have AssessAndAct() exported,
> because the ONLY valid and appropriate way it can be
> called (by anyone other than a subclass extension-implementation of
> the method) is inside some kind of control loop managed by the
> ControlProgramRunner.

"by anyone other than a subclass extension-implementation of the method"

implies that subclasses (sic) have some special privilege.
but assuming subclasses are implemented by external code,
why should they be trusted any more than any other user
of the package?

what's to stop a subclass calling AssessAndAct from within
AddControllerSetting? indeed, what's to stop it spawning
a goroutine that calls AssessAndAct some arbitrary time later?

every langage has limits on the behaviour that the compiler
can enforce. at some point you have to move from compiler-enforced to
convention-enforced behaviour.

PS go doesn't have subclasses or subtypes.

unread,
Jul 6, 2010, 7:03:39 AM7/6/10
to golang-nuts
On Jul 6, 11:37 am, Andrew Gerrand <a...@golang.org> wrote:
> On 6 July 2010 19:04, ⚛ <0xe2.0x9a.0...@gmail.com> wrote:

> > Note that in the above example I did *not* say what X, Y, Ys, Z are.
> > They could denote classes, functions, types, fields, interfaces,
> > methods, variables ...
>
> But you did say "members of," which does not apply to all of the above.

Well, I did start by implying that X and Y are Go package members. So
it may sound confusing when I later said X and Y could denote
anything.

It was more along the lines of "set membership". For example, in an
abstract sense, a variable is a member of the function/block in which
it was declared.

roger peppe

unread,
Jul 6, 2010, 7:04:24 AM7/6/10
to Eric Hawthorne, golang-nuts
On 6 July 2010 01:15, Eric Hawthorne <poetics...@gmail.com> wrote:
> /*
>   Start() launches a goroutine which repeatedly, periodically calls
> the
>  AssessAndAct() method on the control-program it has been given.
>  There are different types of control-program, which vary according
> to
>   what their AssessAndAct() method does.
> */
> type ControlProgramRunner interface {
>
>   Initialize(cp ControlProgram)
>   Start()
>   Stop()
> }
>
> type ControlProgram interface {
>   AddInstrumentReading(instrumentReading InstrumentReading)
>   AddControllerSetting(controllerSetting ControllerSetting)
>   AssessAndAct()
> }

one way to go about this might be to pass AssessAndAct
a context parameter of a type that's private to the
loadcntrl package.

type Context struct {
// private fields
}

type ControlProgram interface {
  AddInstrumentReading(instrumentReading InstrumentReading)
  AddControllerSetting(controllerSetting ControllerSetting)

  AssessAndAct(ctxt *Context)
}

then only the ControlProgramRunner is able to create
instances of Context, so it becomes harder to call
ControlProgramBase.AssessAndAct outside of the
ControlProgramRunner control loop. of course, it's still possible,
by using goroutines or storing ctxt in a global variable, but that's
equally possible with a protected subclass.

at least this way the user of the package has
to make an effort to use the AssessAndAct function
in the wrong way.

> import . "loadcntrl"

this is a bad sign BTW - it's almost never a good idea to import into "."

Gustavo Niemeyer

unread,
Jul 6, 2010, 8:35:17 AM7/6/10
to Serge Hulne, golang-nuts
> These OO features can be handy, but it is known from Python that they
> are not that useful (Python classes have no private or protected
> members and nobody ever complained).

Not true. I'm one case, and I'm sure there are others:

http://blog.labix.org/2009/05/15/class-member-access-control-enforcement-vs-convention

That said, unlike the present thread, that post is mostly about public
vs. private. Protected is a special case of public, because it
doesn't really *hide* away anything from users. It just forces users
to access the method in a given way, and so API stability and the such
continue to apply equally to protected members.

(...)


> If the features you mention were available in Go, then it would kind
> of defeat the purpose, since you would just have reinvented C++ (and
> all the associated unnecessary and counterproductive complexity).

i don't really care about protected either, but dismissing features
because they are in a given language is as bad as desiring them for
the same reason.

--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/blog
http://niemeyer.net/twitter

Eric Hawthorne

unread,
Jul 6, 2010, 12:29:18 PM7/6/10
to golang-nuts
To the person who astutely observed that Go doesn't have subtypes or
subclasses:

It is problem and solution domains, when analysed/conceptualized so as
to minimize their complexity of description, which have subtypes /
subclasses.

i.e. effective programming of just about anything except a simple
arithmetic problem has subtypes and subclasses.

How much the programming language helps you realize these inherent
subtypes/ subclasses and work with them safely and easily is another
matter.

I'm still trying to figure out whether Go has enough encapsulation and
abstraction support to be usable for large, complex s/w projects. It
gives me the impression of having been created by C programmers/
inventors who have grudgingly admitted that there might be something
to all that "ivy league OO" crap but they think it mostly gets in the
way. I'm exploring their worldview from a background that includes
everything from Basic, C, C++ through CLOS,Java,Python,(even Oz) etc.
so I'm pretty open minded but am calling the issues with their
decisions as I see them.



Corey Thomasson

unread,
Jul 6, 2010, 12:34:18 PM7/6/10
to Eric Hawthorne, golang-nuts
On 6 July 2010 12:29, Eric Hawthorne <poetics...@gmail.com> wrote:
> To the person who astutely observed that Go doesn't have subtypes or
> subclasses:
>
> It is problem and solution domains, when analysed/conceptualized so as
> to minimize their complexity of description, which have subtypes /
> subclasses.
>
> i.e. effective programming of just about anything except a simple
> arithmetic problem has subtypes and subclasses.

No, types and classes are just abstractions. It may be that all
problems can be abstracted into types and classes, but they aren't
inherent in the problem nor the solution

roger peppe

unread,
Jul 6, 2010, 12:53:48 PM7/6/10
to Eric Hawthorne, golang-nuts
On 6 July 2010 17:29, Eric Hawthorne <poetics...@gmail.com> wrote:
> To the person who astutely observed that Go doesn't have subtypes or
> subclasses:
>
> It is problem and solution domains, when analysed/conceptualized so as
> to minimize their complexity of description, which have subtypes /
> subclasses.

as the person who made that not-so-astute observation,
i beg to differ.

it is possible to analyse problem and solution domains in
such terms, but there are many other ways of doing so.

personally, i prefer to analyse problems
in terms of objects which can use or be used,
and independent processes which communicate
with one another. where you'd subclass, use factorization instead.
i'd suggest that this usually results in a much more satisfactory
program structure .

i'm biased, i'll freely admit - i have drunk deeply of the go Kool-Aid.

Florian Weimer

unread,
Jul 6, 2010, 2:27:20 PM7/6/10
to Eric Hawthorne, golang-nuts
* Eric Hawthorne:

> Would it make sense to add a protected visibility,

The problem is that any OO book written since 1995 or so tells you
that from a design perspective, "protected" methods are still part of
the public interface of a class.

> Therefore I have a package which exports an interface and also
> exports a "base" implementation struct type which provides a full
> implementation of some of the methods of the interface. The base
> implementation struct type also provides partial implementation (the
> general part) of one of the other methods of the interface.

I suppose you could create a factory function which implements the
interface in terms of the more basic interface.

Steven

unread,
Jul 6, 2010, 11:53:30 PM7/6/10
to Florian Weimer, Eric Hawthorne, golang-nuts
I feel like I'm committing sacrilege, but there is a fairly simple way to achieve something like what I think (if I understood correctly) the author is looking for.

package backend

type partImpl struct{}

func (pi *partImpl) PublicMethod() {}

type PartImpl struct { partImpl }

func (pi *PartImpl) ProtectedMethod() {}

/**** ****/
package frontend

import "./backend"

/* This strips ProtectedMethod from the type, but not PublicMethod */
type partImpl backend.PartImpl

type FullImpl struct { partImpl }

func (fi *FullImpl) Method() {
(*backend.PartImpl)(&fi.partImpl).ProtectedMethod()
}

Since the user cannot refer to &frontend.FullImpl.partImpl, there is no way for them to convert it to a *backend.PartImpl, so they cannot use (*PartImpl).ProtectedMethod on your type. You could use a function, but it wouldn't change much.

Andrew Gerrand

unread,
Jul 7, 2010, 12:15:34 AM7/7/10
to Steven, Florian Weimer, Eric Hawthorne, golang-nuts
On 7 July 2010 13:53, Steven <stev...@gmail.com> wrote:
> I feel like I'm committing sacrilege, but there is a fairly simple way to
> achieve something like what I think (if I understood correctly) the author
> is looking for.

There are other ways of doing this, too, using closures. (Closures are
amazingly powerful things.)

-- a.go

package a

type Base struct{}

func (b *Base) PublicMethod() {
println("Public Method")
}

func (b *Base) protectedMethod() {
println("Protected Method")
}

func NewBase() (*Base, func()) {
b := new(Base)
return b, func() { b.protectedMethod() }
}

-- x.go

package x

import "./a"

type Thing struct {
*a.Base
protectedBaseMethod func()
}

func NewThing() *Thing {
t := new(Thing)
t.Base, t.protectedBaseMethod = a.NewBase()
return t
}

func (t *Thing) UseProtectedMethod() {
t.protectedBaseMethod()
}

-- main.go

package main

import "./x"

func main() {
t := x.NewThing()
t.Base.PublicMethod()
t.UseProtectedMethod()
}

--

Of course, the second return value from a.NewBase() could be another
public type (instead of a func()), which has several methods
implemented on it that are - in essence - 'protected'.

There are much simpler ways of structuring software, though. If you
simply must do it this way, the capacity is there.

Andrew

Steven

unread,
Jul 7, 2010, 1:12:30 AM7/7/10
to Andrew Gerrand, Florian Weimer, Eric Hawthorne, golang-nuts
I was intentionally avoiding dynamic dispatch within the data structure, since having several layers of it could seriously cripple performance. Best to leave it for the top level abstraction if you can. Plus, its nice not to impose a factory method when its not necessary, since it inevitably propagates throughout the code. But yes, there are all kinds of useful things you can to with interfaces and closures :)

Package reflect uses embedding to good effect to reuse code, while still hiding the particulars of the implementation. sync.Mutex is commonly embedded in types in external packages to make them lockable. I see no problem with having a package provide useful building blocks while still granting the user some control over the interface of the resultant types. Now, if you were to try for some sort of complex hierarchy, then I would consider that a frivolous OO make-work project the vast majority of the time.

Olreich

unread,
Jul 7, 2010, 11:03:04 AM7/7/10
to golang-nuts
In the water heater example, the easiest and least code-changing
method of restricting access to the ControlProgramBase methods and
data would be to have an object in HotWaterHeaterControlProgram that
is private, initializing it when you build the object. So your package
hot_water_heater would look like:

package hot_water_heater
import . "loadcntrl"

type HotWaterHeaterControlProgram struct {
controlProgramBase ControlProgramBase
}

func (cp *HotWaterHeaterControlProgram) AssessAndAct() {
cp.controlProgramBase.AssessAndAct()
// some specific hot water heater control stuff
}

I think this would achieve the goals stated in the first post. In Go,
it seems one level of visibility is created by making it Public on
layer 0 and private on layer 1. This won't help you on exporting
interface details, since all interface details have to be Public I
thought, but for data member control it should be sufficient to allow
you to expand your programs with a form of embedded automation. It
would probably be recommended that you be wary, as embedding can
quickly get out of hand and you could have a type hierarchy that you
have to worry about again.

One of the paradigms of Go is keeping everything Orthogonal, so that
types fade away and you worry more about logic. It also allows
flexibility to change things, as editing a type won't be propagated to
a huge dependency tree that relies on it, which turn small changes
into big changes.

Jakub Piotr Cłapa

unread,
Jul 7, 2010, 12:49:42 PM7/7/10
to golan...@googlegroups.com

The third incarnation of a series of modules written by a good
programmer is likely to be reusable and it has nothing to do with
compiler assisted proof-checking. (who do you want to cheat when you
access private members? who do you want to cheat when you extend a class
for which you have neither good documentation nor source code?)

IMHO you are exagerating minor problems. I think it would be best to ask
Smalltalk, Python, Ruby, C or Objective-C programmers whether they feel
their implementation details are too "exposed".

--
regards,
Jakub Piotr Cłapa

Gustavo Niemeyer

unread,
Jul 7, 2010, 1:17:15 PM7/7/10
to Jakub Piotr Cłapa, golan...@googlegroups.com
> IMHO you are exagerating minor problems. I think it would be best to ask
> Smalltalk, Python, Ruby, C or Objective-C programmers whether they feel
> their implementation details are too "exposed".

This argument is getting old. You'll find a lot of people that won't
care, and will find a lot of people that care. Caring or not may be
easily mapped to use cases the developer has had experience with.

Some input specific about Python, which was mentioned earlier in this thread:

http://blog.labix.org/2009/05/15/class-member-access-control-enforcement-vs-convention

The comments received are very interesting to get a feeling of
different perspectives.

The current public + package private logic available in Go suits me well, FWIW.

Ian Lance Taylor

unread,
Jul 7, 2010, 4:24:20 PM7/7/10
to Eric Hawthorne, golang-nuts
Eric Hawthorne <poetics...@gmail.com> writes:

> THE PROBLEM is that to use this pattern effectively, I am needing to
> define too many of the methods of the interface as exported, and also
> to define many of the data members of the base struct type as
> exported,
> so that I can use these general method implementations and general
> data members in subclass struct types.

As others have said, posing the problem in abstract terms pushes you in
a particular direction. It's quite true that Go does not support
protected visibility. There are various ways around this. The most
obvious is for your child package to use a private type and a public
interface value, which gives you complete control over the set of
exposed methods. The interface exported by the child package can, if
you choose, inherit from the interface exported by the base package.

The main reason I'm replying is to mention another approach, which
requires an extra pointer per object, like this:

==================================================

package p1

type PublicInterface interface {
F1()
}

type ProtectedInterface interface {
F2()
}

type Base struct {
i int
}
func (p *Base) F1() {
}
func (p *Base) F2() {
}

==================================================

package p2

import "./p1"

type Child struct {
p1.PublicInterface
base *p1.Base
}

func (p *Child) F3() {
p.F1()
p.base.F2()
}

func NewChild() *Child {
p := new(p1.Base)
return &Child{p, p}
}

==================================================

package main

import "./p2"

func main() {
p := p2.NewChild()
p.F1()
p.F2() // INVALID: p.F2 undefined.
}

==================================================

Reply all
Reply to author
Forward
0 new messages