2

I'm writing a specification for a toy package I'm writing, and while most of the spec is guaranteeing that various structs in the package satisfy the primary public interface, I am also interested in specifying the methods that the interface must require; I know this is more than a little pedantic, but I figured it would be a neat experiment, and would apply pressure to keep the public interface stable.

This is my first try:

type Roller interface {
        Min()  int
}

type minS struct {}
func (m minS) Min() int {return 0}
func (m minS) Max() int {return 0}

func RollerSpec(c gospec.Context) {

        var r Roller = minS{}

        c.Specify("has a minimum value.", func() {
                _, ok := r.(interface{Min() int})
                c.Expect(ok, Equals, true)
        })

        c.Specify("has a maximum value.", func() {
                _, ok := r.(interface{Max() int})
                c.Expect(ok, Equals, true)
        })

        c.Specify("has an expected value.", func() {
                _, ok := r.(interface{Exp() int})
                c.Expect(ok, Equals, true)
        })

        c.Specify("can be rolled.", func() {
                _, ok := r.(interface{Roll() int})
                c.Expect(ok, Equals, true)
        })
}

As you can see, my Roller interface only requires Min() but minS implements both Min() and Max(). I pass the first two specs, even though Runner doesn't satisfy interface{Max() int} because the dummy type I use to test it does. Likewise, declaring r without a base type causes it to fail all specs.

It is obvious why Go would have type assertion between interfaces work on the actual stored type, but it's not what I'm looking for here. I've also checked the reflect package, but it seems like it only inspects structs as well. Is there a way to programmatically check whether an interface requires a given method without pulling in the parse package myself and crawling the parse tree for the method name?

matthias
  • 2,419
  • 1
  • 18
  • 27
  • You only mention `Runner` once without saying what it is or where it comes from. You also don't mention what `gospec` is (one reason why it's often a good idea to leave in all the `import` lines in examples). – Dave C May 20 '15 at 16:03
  • The easiest way to make sure a type satifies an interface is with `var _ Roller = minS{}`. If you have two interfaces (like the title of your question suggests) you can do `var _ SmallInterface = LargeInterface(nil)` which will only compile if all the methods of `SmallInterface` are contained within `LargeInterface` (either via embedding or explicitly). – Dave C May 20 '15 at 16:08

3 Answers3

2

Simply put you can't. There is no way to store just an interface since it's not a concrete type and reflection only works on concrete types. See the reflect package and it's documentation there.

What you want to do seems unnecessary anyway. The interface is your spec. What it looks like you want to do is write a spec to describe your spec and once you do that the big question is where do you stop. It's turtles all the way down :-)

Jeremy Wall
  • 23,907
  • 5
  • 55
  • 73
  • But interfaces have perfectly good introspectable types. It's completely possible to check for the condition OP wnats to check for, with a slight caveat (see my answer below). – BadZen May 19 '15 at 18:09
  • (And there are plenty of use cases in which you specify a subset of interfaces to have metalevel information, completely valid thing to "want to" do. Consider any sort of even rudimentary SOA framework...) – BadZen May 19 '15 at 18:10
  • (And interface values are also perfectly good runtime types - (ie. "concrete") - you can totally store an "enpty" (ie. with no corresponding struct in it) interface in memory, at runtime. Interface values are real things, which reside in memory, and hold (hidden/internal) references to the structs that implement them. See the question I reference below.) – BadZen May 19 '15 at 18:20
  • (Perhaps this was not true when this question was written however? Anyone who knows the state of things then care to comment?) – BadZen May 19 '15 at 18:30
1

Simple type assertions with individual interfaces should do it.

type minRoller interface {
    Min() int
}

type maxRoller interface {
    Max() int
}

And in your test:

if _, ok := r.(minRoller); ok {
    print("has a min value")
}
if _, ok := r.(maxRoller); ok {
    print("has a max value") 
}

Your Roller interface can embed the smaller ones:

type MinRoller interface {
    minRoller
}

type MinMaxRoller interface {
    minRoller
    maxRoller
}
themue
  • 7,626
  • 2
  • 25
  • 28
  • That seems like it still only verifies that `minS` or whatever underlying type I store in `r` fulfills Roller and each of the smaller ones--if I remove one of the embedded interfaces from the bigger one(s) but leave the methods in my structs, my specs will all still pass. I do plan to use smaller (private?) interfaces for testing other functionality later in the spec though. I'll be asking a separate, more subjective question related to that later. – matthias Aug 30 '12 at 13:57
0

Of course there is a way - the reflect.Type of an interface contains reflect.Method which correspond to the interface methods (don't try to call these, though! See my question Go reflection with interface embedded in struct - how to detect “real” functions?)

Using this, it's (almost) trivial to loop through the methods of an interface and check that those methods also exist in another interface:

func InterfaceExtendsInterface(parent reflect.Type, child reflect.Type) bool {
    if parent.Kind() != reflect.Interface || child.Kind() != reflect.Interface {
        return false
    }
    for i := 0; i < parent.NumMethod(); i++ {
        m := parent.Method(i)
        if n, has := child.MethodByName(m.Name); !has {
            return false
        } else if !reflect.DeepEqual(m,n) {
            return false
        }
    }
    return true
}

Because of the way interface "extension" (embedding of interface in interface) works, I don't really see a difference between

type Foo interface {...}
type Bar interface { Foo; ... }
type Baz struct { ... } // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Bar{}).Elem())

and the slightly weaker

type Foo interface {...}
type Bar interface { ... }
type Baz struct { ... } // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Bar{}).Elem()) && 
                        // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Foo{}).Elem())

to the point where I can't even think of any case where the two would ever imply different semantics for non-reflective calls on instances of Baz... (other than having to convert to Foo or not directly if Bar does not embed it, of course)

Community
  • 1
  • 1
BadZen
  • 4,083
  • 2
  • 25
  • 48
  • You may also be able to get away with just `child.ConvertibleTo(parent)` in most cases, in which case the above is reduced to a one-liner... – BadZen May 19 '15 at 18:18
  • 2
    If you know the types at compile time just do `var _ Foo = Bar(nil)`; that won't compile unless `Bar` implements `Foo` (either by embedding, or explicitly including the required methods). – Dave C May 20 '15 at 16:06