Reflecting on an outer struct from a method of an inner one.

224 views
Skip to first unread message

Eric Raymond

unread,
Sep 6, 2018, 7:34:54 PM9/6/18
to golang-nuts
This question is intended to assist implementation of a Go workalike for the Python Cmd library.  This workalike will be made publicly available,

The attached program lists

Inner
id

as expected - that is, the fieldnames of the Outer structure. But ListFields is a method of Inner.

In the attached program, I would like to be able to remove the &x argument from ListFields.  That is, I want the Inner code to be able to reflect on the Outer instance without having to be fed the address of the Outer instance explicitly.  I specifically do *not* want to move the method to Outer - reflection  from within Inner is the point of the exercise.

Is there a way for a struct instance to get its own address through the runtime? 

The application would be to walk through the user-defined methods of a Cmd-like interpreter instance.
innerclass.go

Nigel Tao

unread,
Sep 6, 2018, 9:45:37 PM9/6/18
to Eric Raymond, golang-nuts
On Fri, Sep 7, 2018 at 9:34 AM Eric Raymond <e...@thyrsus.com> wrote:
> The attached program lists

For future reference, it would probably be easier for the mailing list
to discuss example code via a link to the Go playground, especially
for runnable code. In this case: https://play.golang.org/p/IripNZNYIgo


> In the attached program, I would like to be able to remove the &x argument from ListFields. That is, I want the Inner code to be able to reflect on the Outer instance without having to be fed the address of the Outer instance explicitly. I specifically do *not* want to move the method to Outer - reflection from within Inner is the point of the exercise.

The short answer is no, not possible. Embedding Inner is not
inheritance. It is essentially sugar, as if you said

type Outer struct {
fieldName Inner
id int
}

and for every method foo on Inner, there was an implicit forwarding method:

func (o *Outer) foo(etc) { o.fieldName.foo(etc) }

it's just that you don't have to explicitly type "fieldName", or those
forwarding methods.

But that's it. Unlike Java's "(almost) everything is an Object", a
value's type is a compile time concept, not a runtime one (ignoring
Go's interface types, for now). If you have an Inner, all you have is
an Inner, and you can't distinguish "an Inner inside an Outer struct"
or "an Inner inside an array of 10 Inners" or "an Inner inside a Bar
struct inside a Qux struct". But also unlike Java, you can embed
multiple concrete types (the embeddee also doesn't have to be the
first field), just like a struct can have multiple fields: "type Outer
struct { Inner0; Inner1; id int; Inner3; }".

To repeat, embedding isn't inheritance. It's not that the Outer value
is-a Inner, it's that it contains-a Inner.


> Is there a way for a struct instance to get its own address through the runtime?

In "func (x *Inner) Etc(etc)", the address of the struct is the
variable "x": https://play.golang.org/p/PP7LKDxEHrg

But I don't think that's going to help you. You can't go from a
pointer-to-Inner to a pointer-to-Outer without knowing (i.e. passing)
the Outer type (assuming that there's more than one Outer type), and
if you're passing the Outer type, you might as well pass the Outer
pointer.


> The application would be to walk through the user-defined methods of a Cmd-like interpreter instance.

Asking about embedding and reflection sounds like an XY problem
(http://xyproblem.info/). Can you elaborate on what "walk through the
user-defined methods of a Cmd-like interpreter instance" is?

Eric Raymond

unread,
Sep 6, 2018, 11:53:29 PM9/6/18
to golang-nuts


On Thursday, September 6, 2018 at 9:45:37 PM UTC-4, Nigel Tao wrote:
Asking about embedding and reflection sounds like an XY problem
(http://xyproblem.info/). Can you elaborate on what "walk through the
user-defined methods of a Cmd-like interpreter instance" is?

Sure.  My reposurgeon program uses the Python Cmd class:


The Go translation needs an emulation of Cmd, which my talented and hardworking apprentice Ian Bruene has just shipped an 0.1 version of:


The main remaining issue with his implementation is that he has to explicitly register command handlers, linking them to names. This is an obviously error-prone pain in the ass, and I want to make it work more like the Python original.  In that original, you declare a class that inherits from Cmd.  Cmd then walks through your interpreter class picking out method names beginning with "doXXXXX" and binding them to be fired by the command string XXXXX.

Obviously we can't use inheritance in the normal sense here, but I was hoping that if an embedded instance of Cmd could discover the struct instance it's part of, a similar thing could be pulled off.  Alas, your answer that this can't be done is unsurprising and I was half expecting it.  Calling one function to pass in the outer type beats having to declare all the command handlers individually....

...provided, that is, that it's possible to get from the member name to a function pointer that can be called like a closure.  Does the reflection system support such a call-indirect method?

Todd Neal

unread,
Sep 7, 2018, 12:34:44 AM9/7/18
to Eric Raymond, golang-nuts

Eric Raymond <e...@thyrsus.com> writes:

> Obviously we can't use inheritance in the normal sense here, but I was
> hoping that if an embedded instance of Cmd could discover the struct
> instance it's part of, a similar thing could be pulled off. Alas, your
> answer that this can't be done is unsurprising and I was half expecting
> it. Calling one function to pass in the outer type beats having to declare
> all the command handlers individually....

There's no typename to reflected type or enumeration of all types that
implement a particular interface at runtime, unless you build it
yourself.

You might be interested in the init() documentation at
https://golang.org/doc/effective_go.html#init . You can declare
multiple init functions (say one in each file where you define a
command), and if you write the registration when you write the command,
you would get the automatic registration you're looking for. A 'gotcha'
to watch out for is that the init function won't run if the package
isn't imported, leading to imports like this:

import (
_ "github.com/someuser/somepackage"
)

which are common for the database drivers. The package is imported
solely to cause the init function to run. See
https://golang.org/doc/effective_go.html#blank_import for more info.

> ...provided, that is, that it's possible to get from the member name to a
> function pointer that can be called like a closure. Does the reflection
> system support such a call-indirect method?

Sure, see an example I put together at
https://play.golang.org/p/divEOT31i-4 which shows two ways that the
receiver can be captured, one without reflection and one with.


- Todd

Nigel Tao

unread,
Sep 7, 2018, 12:58:05 AM9/7/18
to Eric Raymond, golang-nuts
On Fri, Sep 7, 2018 at 1:53 PM Eric Raymond <e...@thyrsus.com> wrote:
> ...provided, that is, that it's possible to get from the member name to a function pointer that can be called like a closure. Does the reflection system support such a call-indirect method?

Yep. No embedding or other inheritance-like feature needed:
https://play.golang.org/p/SPcJnV0VCr5

Nigel Tao

unread,
Sep 7, 2018, 4:01:23 AM9/7/18
to Eric Raymond, golang-nuts
Building on Todd's example, you can add a little more type safety
(having the map value type be a "func etc" instead of "reflect.Value)
by Curry'ing the receiver: https://play.golang.org/p/n3sDpxfd2td

I've also split up the single run function into two: one binds strings
to functions, the second takes some strings (e.g. as args, but you
could imagine from a REPL) and calls the corresponding functions.
"Favor composition over inheritance" is old programming advice, not
specific to Go, but is obviously applicable to Go, which does not have
inheritance. Instead of having a FooCmd class that inherits from a Cmd
base class that adds smarts (in this case, being 'scriptable' or being
invokable from string input), have a dumb FooCmd type (and a dumb
BarCmd type, etc.) and compose those dumb types with a runner type
that does the binding / scriptability.

roger peppe

unread,
Sep 7, 2018, 8:03:32 AM9/7/18
to Nigel Tao, Eric Raymond, golang-nuts
Ha, I wrote this version before I saw that response:

https://play.golang.org/p/vO2TI7OeJk_E
> --
> 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.
> For more options, visit https://groups.google.com/d/optout.

ianb...@gmail.com

unread,
Sep 7, 2018, 11:34:52 AM9/7/18
to golang-nuts


On Friday, September 7, 2018 at 3:01:23 AM UTC-5, Nigel Tao wrote:
Building on Todd's example, you can add a little more type safety
(having the map value type be a "func etc" instead of "reflect.Value)
by Curry'ing the receiver: https://play.golang.org/p/n3sDpxfd2td

I've also split up the single run function into two: one binds strings
to functions, the second takes some strings (e.g. as args, but you
could imagine from a REPL) and calls the corresponding functions.
[...]

Ah, good. What I have in my local repo is more or less on the right track, though less sophisticated than what is in your link. Thanks!

ianb...@gmail.com

unread,
Sep 7, 2018, 1:41:57 PM9/7/18
to golang-nuts


On Thursday, September 6, 2018 at 6:34:54 PM UTC-5, Eric Raymond wrote:
In the attached program, I would like to be able to remove the &x argument from ListFields.  That is, I want the Inner code to be able to reflect on the Outer instance without having to be fed the address of the Outer instance explicitly.  I specifically do *not* want to move the method to Outer - reflection  from within Inner is the point of the exercise.

I actually have a solution for the &x problem. The handler-struct can be an interface with some form of SetFakeSuperClass() method which the Kommandant constructor calls. The only problem is that it is be brittle if the Kmdt struct is copied. But realistically I'm having a hard time coming up with a scenario where that is relevant and Kommandant is the breakage rather than the code using it.

Reply all
Reply to author
Forward
0 new messages