composite types and type assertion

320 views
Skip to first unread message

Daniel Skinner

unread,
May 18, 2012, 1:01:41 AM5/18/12
to golan...@googlegroups.com
Reading through effective go and the go blog to understand composite types and type assertion, I'm still a little confused by wanting to do something like this

type A int
type B A
type X B
type Y B

func check(a A) {
    // check if 'a' is an X or Y and handle accordingly
}

but I can't pass an X or Y in as an A, so then i could do this

func check(a interface{}) {
    switch t := a.(type) {
        // ...
    }
}

But then I'd want a compile time error if 'a interface{}' is not of A and if I try to do something like

func check(a interface{}) {
    _, ok := a.(A)
    if !ok {
        fmt.Println("not ok")
    }
    // ...
}

But then it's always "not ok" for an X or Y. And of course if A is an interface{} with no methods, then it's always ok.

How can I approach this problem in Go to generate compile time errors and not issues during runtime?

Jesse McNelis

unread,
May 18, 2012, 1:09:59 AM5/18/12
to Daniel Skinner, golan...@googlegroups.com
On Fri, May 18, 2012 at 3:01 PM, Daniel Skinner <dan...@dasa.cc> wrote:
> Reading through effective go and the go blog to understand composite types
> and type assertion, I'm still a little confused by wanting to do something
> like this
>
> type A int
> type B A
> type X B
> type Y B
>
> func check(a A) {
>     // check if 'a' is an X or Y and handle accordingly
> }

This doesn't make sense. An 'A' is just an 'A', it can't be either an
'X' or a 'Y'.
They are all independent types.

Type assertions work on interface types so you can get the concrete
type they contain.
http://golang.org/ref/spec#Type_assertions

> but I can't pass an X or Y in as an A, so then i could do this
>
> func check(a interface{}) {
>     switch t := a.(type) {
>         // ...
>     }
> }
>
> But then I'd want a compile time error if 'a interface{}' is not of A and if
> I try to do something like
>
> func check(a interface{}) {
>     _, ok := a.(A)
>     if !ok {
>         fmt.Println("not ok")
>     }
>     // ...
> }
>
> But then it's always "not ok" for an X or Y. And of course if A is an
> interface{} with no methods, then it's always ok.
>
> How can I approach this problem in Go to generate compile time errors and
> not issues during runtime?



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

Daniel Skinner

unread,
May 18, 2012, 1:36:39 AM5/18/12
to Jesse McNelis, golan...@googlegroups.com

I guess I'm trying to relate this to inheritance and identifying instances but that's just not correct thinking for Go.

John Asmuth

unread,
May 18, 2012, 8:40:15 AM5/18/12
to golan...@googlegroups.com, Jesse McNelis
Certainly "type A B" implies no kind of inheritance. A doesn't even get any of B's methods - just its operators (if it's a chan, numeric etc) and its fields (if it's a struct).

The way to composite in go is like so:

type A struct {
    B
}

Now A has all of B's methods. But you still can't pass A where it wants a B, because that is just not what composition means. For that you need polymorphism, which is where interfaces come in.

But basically, inheritance is something that seems convenient at first but really hurts designs in the long run. Try to work out something that doesn't use it.

John Asmuth

unread,
May 18, 2012, 8:41:44 AM5/18/12
to golan...@googlegroups.com, Jesse McNelis


On Friday, May 18, 2012 8:40:15 AM UTC-4, John Asmuth wrote:
type A struct {
    B
}

Now A has all of B's methods. But you still can't pass A where it wants a B, because that is just not what composition means. For that you need polymorphism, which is where interfaces come in.

Just to be clear, you can pass the B part of A where a B is required, with "anAInstance.B". Just be aware that none of the B methods will be "overridden" with what you may have defined them to be in A, as it would be with inheritance. 

Daniel Skinner

unread,
May 18, 2012, 10:46:26 AM5/18/12
to John Asmuth, golan...@googlegroups.com

Polymorphism, something I use everyday but can't identify in the wild. Obviously I should go read about composite types.

Thanks for your insight.

Daniel Skinner

unread,
May 18, 2012, 12:47:50 PM5/18/12
to John Asmuth, golan...@googlegroups.com
After reading about composite types and seeing the err of my question and thought in general, and to answer my own question, it seems that if I want compile time errors and the general style that might come about with polymorphism, I should make use of duck-typing, creating an interface for each type appropriately with the necessary required methods to accommodate.

While this was my original thought, I couldn't help but thing how elegant it would have been (since the result is a string) to simply have

type ActionType string
type ActionStart ActionType

type ActionFn func(string) ActionType

and then go about defining custom actions such as

var myActionStart ActionStart = "start"

func myActionFn(s string) ActionType {
    switch s {
    case whatever:
        return myActionStart
    }
}

of which there's more then just ActionStart, and such a type determines placement of string. Though this is an abuse of thought to the definition of a type, so sing duck typing I could resolve, but would need to write a bit more code. Perhaps I'll explore some other possibilities with Go as well.

Kyle Lemons

unread,
May 18, 2012, 1:50:03 PM5/18/12
to Daniel Skinner, John Asmuth, golan...@googlegroups.com
type Action string
const (
  Start Action = "start"
)

Then you can compare any Action variable to see if it's == Start.  See Effective Go for more prose on the subject.

Daniel Skinner

unread,
May 18, 2012, 3:05:04 PM5/18/12
to Kyle Lemons, John Asmuth, golan...@googlegroups.com
Except I need two bits of information, is this a start/end/ignore/etc and what is its value. In this case, I can only really know its values, and any string satisfies Action.

I wrote an example with comments to illustrate

But to summarize, I was exploring options for what something like this might accomplish

const (
    ActionStart iota
    ActionEnd
    ActionIgnore
)

type Action struct {
    action int
    value  string
}

and then i could switch/case on Action.action, and I could provide some additional types for code clarity, etc but i think this all gets to the point.
Reply all
Reply to author
Forward
0 new messages